From c36a6bcb477a8276dbe48ef3bd4eebf7996a6583 Mon Sep 17 00:00:00 2001 From: Mario Joa-Ng Date: Thu, 17 Jul 2025 23:04:14 +0200 Subject: [PATCH 001/275] Fix the bug in calculating fh_config->neAxc where num_beams_period is multiplified twice. --- radio/fhi_72/oran-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/fhi_72/oran-config.c b/radio/fhi_72/oran-config.c index 89c1838fa3..684d44e7cf 100644 --- a/radio/fhi_72/oran-config.c +++ b/radio/fhi_72/oran-config.c @@ -918,7 +918,7 @@ static bool set_fh_config(void *mplane_api, int ru_idx, int num_rus, enum xran_c fh_config->dpdk_port = ru_idx; // DPDK port number used for FH fh_config->sector_id = 0; // Band sector ID for FH; not used in xran fh_config->nCC = 1; // number of Component carriers supported on FH; M-plane info - fh_config->neAxc = RTE_MAX(oai0->num_distributed_ru * oai0->tx_num_channels / num_rus, oai0->num_distributed_ru * oai0->rx_num_channels / num_rus); // number of eAxc supported on one CC = max(PDSCH, PUSCH) + fh_config->neAxc = RTE_MAX(oai0->tx_num_channels / num_rus, oai0->rx_num_channels / num_rus); // number of eAxc supported on one CC = max(PDSCH, PUSCH) fh_config->neAxcUl = 0; // number of eAxc supported on one CC for UL direction = PUSCH; used only if XRAN_CATEGORY_B fh_config->nAntElmTRx = 0; // number of antenna elements for TX and RX = SRS; used only if XRAN_CATEGORY_B fh_config->nDLFftSize = 0; // DL FFT size; not used in xran From 4eb9dd3d3829b9baf40d89fee15b0ec74c500da3 Mon Sep 17 00:00:00 2001 From: Mario Joa-Ng Date: Wed, 13 Aug 2025 15:50:38 +0200 Subject: [PATCH 002/275] set beam_id to -1 instead of 0. --- openair1/PHY/INIT/nr_init.c | 6 ++++-- openair1/PHY/INIT/nr_init_ru.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 60bc63dac4..778903be91 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -175,8 +175,10 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) if (cfg->analog_beamforming_ve.analog_bf_vendor_ext.value) { common_vars->beam_id = (int **)malloc16(common_vars->num_beams_period * sizeof(int*)); - for (int i = 0; i < common_vars->num_beams_period; i++) - common_vars->beam_id[i] = (int*)malloc16_clear(fp->symbols_per_slot * fp->slots_per_frame * sizeof(int)); + for (int i = 0; i < common_vars->num_beams_period; i++) { + common_vars->beam_id[i] = (int*)malloc16(fp->symbols_per_slot * fp->slots_per_frame * sizeof(int)); + memset(common_vars->beam_id[i], -1, fp->symbols_per_slot * fp->slots_per_frame * sizeof(int)); + } } common_vars->txdataF = (c16_t ***)malloc16(common_vars->num_beams_period * sizeof(c16_t**)); for (int i = 0; i < common_vars->num_beams_period; i++) { diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index a0766c348f..ca753938a6 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -53,8 +53,10 @@ void nr_phy_init_RU(RU_t *ru) int nb_rx_streams = ru->nb_rx * ru->num_beams_period; LOG_I(NR_PHY, "nb_tx_streams %d, nb_rx_streams %d, num_Beams_period %d\n", nb_tx_streams, nb_rx_streams, ru->num_beams_period); ru->common.beam_id = malloc16_clear(ru->num_beams_period * sizeof(int*)); - for(int i = 0; i < ru->num_beams_period; i++) - ru->common.beam_id[i] = malloc16_clear(fp->symbols_per_slot * fp->slots_per_frame * sizeof(int)); + for(int i = 0; i < ru->num_beams_period; i++) { + ru->common.beam_id[i] = malloc16(fp->symbols_per_slot * fp->slots_per_frame * sizeof(int)); + memset(ru->common.beam_id[i], -1, fp->symbols_per_slot * fp->slots_per_frame * sizeof(int)); + } if (ru->if_south <= REMOTE_IF5) { // this means REMOTE_IF5 or LOCAL_RF, so allocate memory for time-domain signals // Time-domain signals From e4f7443552fa6da3d04acb7df4ef7b215f832997 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 16 Oct 2025 16:11:55 +0200 Subject: [PATCH 003/275] =?UTF-8?q?Initialize=20NCC=3D0=20on=20NGAP=20Init?= =?UTF-8?q?ial=20Context=20Setup=20per=203GPP=20TS=2033.501=20=C2=A76.9.2.?= =?UTF-8?q?1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused kgnb_ncc: nh_ncc is already there Closes #1008 --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 2 +- openair2/RRC/NR/nr_rrc_defs.h | 1 - openair2/RRC/NR/rrc_gNB.c | 3 +-- openair2/RRC/NR/rrc_gNB_NGAP.c | 4 +++- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 6b65ecd263..2d92cb9630 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1025,7 +1025,7 @@ int do_RRCReestablishment(int8_t nh_ncc, uint8_t *const buffer, size_t buffer_si rrcReestablishment->criticalExtensions.choice.rrcReestablishment = CALLOC(1, sizeof(NR_RRCReestablishment_IEs_t)); // 3GPP TS 33.501 Section 6.11 Security handling for RRC connection re-establishment procedure - rrcReestablishment->criticalExtensions.choice.rrcReestablishment->nextHopChainingCount = nh_ncc >= 0 ? nh_ncc : 0; + rrcReestablishment->criticalExtensions.choice.rrcReestablishment->nextHopChainingCount = nh_ncc; rrcReestablishment->criticalExtensions.choice.rrcReestablishment->lateNonCriticalExtension = NULL; rrcReestablishment->criticalExtensions.choice.rrcReestablishment->nonCriticalExtension = NULL; diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index e76802338c..a52d5537c0 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -188,7 +188,6 @@ typedef struct gNB_RRC_UE_s { /* KgNB as derived from KASME received from EPC */ uint8_t kgnb[32]; - int8_t kgnb_ncc; uint8_t nh[32]; int8_t nh_ncc; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index c2734c755a..97d0feda67 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -998,8 +998,7 @@ static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; uint32_t ssb_arfcn = get_ssb_arfcn(du); LOG_I(NR_RRC, "Reestablishment update key pci=%d, earfcn_dl=%u\n", cell_info->nr_pci, ssb_arfcn); - nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, ue_p->nh_ncc >= 0 ? ue_p->nh : ue_p->kgnb, ue_p->kgnb); - ue_p->kgnb_ncc = 0; + nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, ue_p->nh_ncc > 0 ? ue_p->nh : ue_p->kgnb, ue_p->kgnb); int size = do_RRCReestablishment(ue_context_pP->ue_context.nh_ncc, buffer, NR_RRC_BUF_SIZE, xid); LOG_A(NR_RRC, "Send RRCReestablishment [%d bytes] to RNTI %04x\n", size, ue_p->rnti); diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 7d75384a9b..59cc242e28 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -122,7 +122,9 @@ static void set_UE_security_key(gNB_RRC_UE_t *UE, uint8_t *security_key_pP) /* Saves the security key */ memcpy(UE->kgnb, security_key_pP, SECURITY_KEY_LENGTH); memset(UE->nh, 0, SECURITY_KEY_LENGTH); - UE->nh_ncc = -1; + /* 3GPP TS 33.501 ยง6.9.2.1.1: On Initial Context Setup, AMF does not send NH; + * gNB shall initialize NCC to 0. */ + UE->nh_ncc = 0; char ascii_buffer[65]; for (i = 0; i < 32; i++) { From b0ab5e8795a53c39cba72c1ed68a59fc953b9756 Mon Sep 17 00:00:00 2001 From: Shubhika Garg Date: Fri, 17 Oct 2025 09:53:29 +0200 Subject: [PATCH 004/275] ci: Ensure DockerHub push failure doesn't halt pipeline - catchError will prevent from abruptly aborting the entire pipeline upon failure. - Removed redundant `currentBuild.result` in post-failure block --- ci-scripts/Jenkinsfile-GitLab-Container | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ci-scripts/Jenkinsfile-GitLab-Container b/ci-scripts/Jenkinsfile-GitLab-Container index 3301c133ec..a2bb8112fc 100644 --- a/ci-scripts/Jenkinsfile-GitLab-Container +++ b/ci-scripts/Jenkinsfile-GitLab-Container @@ -271,14 +271,15 @@ pipeline { when { expression {doBuild && "PUSH".equals(env.gitlabActionType)} } steps { script { - triggerSlaveJob ('RAN-DockerHub-Push', 'DockerHub-Push') + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + triggerSlaveJob ('RAN-DockerHub-Push', 'DockerHub-Push') + } } } post { failure { script { echo "Push to Docker-Hub KO" - currentBuild.result = 'FAILURE' failingStages += '\n * RAN-DockerHub-Push' } } From d0089369d3c0fc1d7b1976a277f27fa0f8af4e1a Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Fri, 17 Oct 2025 10:59:49 +0200 Subject: [PATCH 005/275] CI: adjust TDD pattern in SA-B200 40 MHz TDD test --- .../conf_files/gnb-du.sa.band78.106prb.usrpb200.conf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf index e4d9127284..23475e3838 100644 --- a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf +++ b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf @@ -135,11 +135,11 @@ gNBs = # pattern1 # dl_UL_TransmissionPeriodicity # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 - dl_UL_TransmissionPeriodicity = 6; - nrofDownlinkSlots = 7; - nrofDownlinkSymbols = 6; - nrofUplinkSlots = 2; - nrofUplinkSymbols = 4; + dl_UL_TransmissionPeriodicity = 5; + nrofDownlinkSlots = 3; + nrofDownlinkSymbols = 10; + nrofUplinkSlots = 1; + nrofUplinkSymbols = 2; ssPBCH_BlockPower = -25; } From 8424c8ecb6a44c9692ca8d0622bfb37381a8172f Mon Sep 17 00:00:00 2001 From: alexjiao Date: Fri, 17 Oct 2025 16:21:09 +0800 Subject: [PATCH 006/275] Fix crashes for T-Tracer to work with NR-UE 1. Fix UE_PHY_PDSCH_IQ event 2. Fix UE_PHY_PDCCH_ENERGY event 3. Fix UE_PHY_PDSCH_ENERGY event 4. Fix UE_PHY_MEAS event All fixes align NR UE events with their definitions in T_messages.txt and match the expected format in the tracer GUI parser. --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 9 ++++++++- .../PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c | 16 +++++++++++----- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 4 ++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 43b27e3932..10cfa121a7 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -372,7 +372,14 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, LOG_D(NR_PHY_DCI, "slot %d: pdcch log2_maxh = %d (%d,%d)\n", proc->nr_slot_rx, log2_maxh, avgP[0], avgs); #endif #if T_TRACER - T(T_UE_PHY_PDCCH_ENERGY, T_INT(0), T_INT(0), T_INT(proc->frame_rx % 1024), T_INT(proc->nr_slot_rx), T_INT(avgs)); + T(T_UE_PHY_PDCCH_ENERGY, + T_INT(0), + T_INT(proc->frame_rx % 1024), + T_INT(proc->nr_slot_rx), + T_INT(avg[0]), + T_INT(antRx > 1 ? avg[1] : 0), + T_INT(antRx > 2 ? avg[2] : 0), + T_INT(antRx > 3 ? avg[3] : 0)); #endif LOG_D(NR_PHY_DCI, "we enter nr_pdcch_channel_compensation(log2_maxh=%d)\n", log2_maxh); LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_compensation(rxdataF_ext x dl_ch_estimates_ext -> rxdataF_comp)\n"); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index 5e677b542b..ab73bedb4f 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -540,6 +540,16 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, } *log2_maxh = (log2_approx(avgs) / 2) + 1; LOG_D(PHY, "[DLSCH] AbsSubframe %d.%d log2_maxh = %d (%d)\n", frame % 1024, nr_slot_rx, *log2_maxh, avgs); +#if T_TRACER + T(T_UE_PHY_PDSCH_ENERGY, + T_INT(gNB_id), + T_INT(frame % 1024), + T_INT(nr_slot_rx), + T_INT(avg[0]), // layer 0, antenna 0 + T_INT(n_rx > 1 ? avg[1] : 0), // layer 0, antenna 1 + T_INT(nl > 1 ? avg[n_rx] : 0), // layer 1, antenna 0 + T_INT(nl > 1 && n_rx > 1 ? avg[n_rx + 1] : 0)); // layer 1, antenna 1 +#endif } stop_meas_nr_ue_phy(ue, DLSCH_CHANNEL_LEVEL_STATS); if (meas_enabled) { @@ -552,9 +562,6 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, first_symbol_flag, ue->phy_cpu_stats.cpu_time_stats[DLSCH_CHANNEL_LEVEL_STATS].p_time / (cpuf * 1000.0)); } -#if T_TRACER - T(T_UE_PHY_PDSCH_ENERGY, T_INT(gNB_id), T_INT(0), T_INT(frame % 1024), T_INT(nr_slot_rx)); -#endif //---------------------------------------------------------- //--------------------- channel compensation --------------- @@ -746,13 +753,12 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, #if T_TRACER T(T_UE_PHY_PDSCH_IQ, T_INT(gNB_id), - T_INT(ue->Mod_id), T_INT(frame % 1024), T_INT(nr_slot_rx), T_INT(nb_rb_pdsch), T_INT(fp->N_RB_UL), T_INT(fp->symbols_per_slot), - T_BUFFER(&rxdataF_comp[gNB_id][0], 2 * /* ulsch[UE_id]->harq_processes[harq_pid]->nb_rb */ fp->N_RB_UL * 12 * 2)); + T_BUFFER(&rxdataF_comp[gNB_id][0], 2 * fp->N_RB_UL * 12 * fp->symbols_per_slot * 2)); #endif if (ue->phy_sim_pdsch_rxdataF_comp) diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index f22d13c5fc..69dd29c460 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -440,7 +440,6 @@ static void nr_ue_measurement_procedures(uint16_t l, if(nr_slot_rx == 0) T(T_UE_PHY_MEAS, T_INT(gNB_id), - T_INT(ue->Mod_id), T_INT(proc->frame_rx % 1024), T_INT(nr_slot_rx), T_INT((int)(10 * log10(ue->measurements.rsrp[0]) - ue->rx_total_gain_dB)), @@ -448,7 +447,8 @@ static void nr_ue_measurement_procedures(uint16_t l, T_INT((int)(ue->measurements.rx_power_avg_dB[0] - ue->measurements.n0_power_avg_dB)), T_INT((int)ue->measurements.rx_power_avg_dB[0]), T_INT((int)ue->measurements.n0_power_avg_dB), - T_INT((int)ue->measurements.wideband_cqi_avg[0])); + T_INT((int)ue->measurements.wideband_cqi_avg[0]), + T_INT((int)ue->common_vars.freq_offset)); #endif } From 5df7d000f9a04beb41ab98c77333e830666a9d15 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Fri, 17 Oct 2025 16:34:24 +0800 Subject: [PATCH 007/275] Fix for DL SNR to work in T-Tracer with NR-UE Add missing T_UE_PHY_DL_TICK and T_UE_PHY_UL_TICK events Calculate missing n0_power_avg_dB for noise measurements --- openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c | 3 ++- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c b/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c index 36b8d8a673..7d6ab8e6f2 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c @@ -125,8 +125,9 @@ void nr_ue_measurements(PHY_VARS_NR_UE *ue, for (gNB_id = 0; gNB_id < ue->n_connected_gNB; gNB_id++) { ue->measurements.rx_power_avg_dB[gNB_id] = dB_fixed( ue->measurements.rx_power_avg[gNB_id]); + ue->measurements.n0_power_avg_dB = dB_fixed(ue->measurements.n0_power_avg); ue->measurements.wideband_cqi_tot[gNB_id] = ue->measurements.rx_power_tot_dB[gNB_id] - ue->measurements.n0_power_tot_dB; - ue->measurements.wideband_cqi_avg[gNB_id] = ue->measurements.rx_power_avg_dB[gNB_id] - dB_fixed(ue->measurements.n0_power_avg); + ue->measurements.wideband_cqi_avg[gNB_id] = ue->measurements.rx_power_avg_dB[gNB_id] - ue->measurements.n0_power_avg_dB; ue->measurements.rx_rssi_dBm[gNB_id] = ue->measurements.rx_power_avg_dB[gNB_id] + 30 - SQ15_SQUARED_NORM_FACTOR_DB - ((int)ue->openair0_cfg[0].rx_gain[0] - (int)ue->openair0_cfg[0].rx_gain_offset[0]) - dB_fixed(ue->frame_parms.ofdm_symbol_size); diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 69dd29c460..a91ae75d32 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -372,6 +372,9 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, n AssertFatal(ue->CC_id == 0, "Transmission on secondary CCs is not supported yet\n"); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX,VCD_FUNCTION_IN); +#if T_TRACER + T(T_UE_PHY_UL_TICK, T_INT(ue->Mod_id), T_INT(frame_tx % 1024), T_INT(slot_tx)); +#endif const int samplesF_per_slot = NR_SYMBOLS_PER_SLOT * ue->frame_parms.ofdm_symbol_size; c16_t txdataF_buf[ue->frame_parms.nb_antennas_tx * samplesF_per_slot] __attribute__((aligned(32))); @@ -1039,6 +1042,9 @@ int pbch_pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_ nr_ue_dlsch_init(phy_data->dlsch, NR_MAX_NB_LAYERS>4 ? 2:1, ue->max_ldpc_iterations); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_RX, VCD_FUNCTION_IN); +#if T_TRACER + T(T_UE_PHY_DL_TICK, T_INT(ue->Mod_id), T_INT(frame_rx % 1024), T_INT(nr_slot_rx)); +#endif LOG_D(PHY," ****** start RX-Chain for Frame.Slot %d.%d ****** \n", frame_rx%1024, nr_slot_rx); From 867d476397eddee945b38f1f60ba0fa3f86b92c9 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Fri, 17 Oct 2025 11:36:39 +0200 Subject: [PATCH 008/275] CI: L2-Sim-Test-4G replace node name by localhost By configuring the Jenkins pythonExecutor to match the test machine, we can use localhost as the node for running individual test cases. This change makes it easy to move tests across machines, as the only adjustment needed is updating the pythonExecutor in Jenkins. --- .../xml_files/container_4g_l2sim_tdd.xml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ci-scripts/xml_files/container_4g_l2sim_tdd.xml b/ci-scripts/xml_files/container_4g_l2sim_tdd.xml index eabece50cd..d9ac14038b 100644 --- a/ci-scripts/xml_files/container_4g_l2sim_tdd.xml +++ b/ci-scripts/xml_files/container_4g_l2sim_tdd.xml @@ -44,7 +44,7 @@ Pull_Local_Registry Pull Images from Local Registry - obelix + localhost oai-enb oai-lte-ue @@ -88,16 +88,16 @@ Attach_UE Attach OAI UE l2sim4g_ue - obelix + localhost Ping Ping trf-gen from LTE-UE 1 l2sim4g_ue - obelix + localhost l2sim4g_ext_dn - obelix + localhost -c 20 0 @@ -106,9 +106,9 @@ Ping Ping LTE-UE 1 from trf-gen l2sim4g_ext_dn - obelix + localhost l2sim4g_ue - obelix + localhost -c 20 0 @@ -117,9 +117,9 @@ Iperf Iperf UDP Downlink l2sim4g_ue - obelix + localhost l2sim4g_ext_dn - obelix + localhost -u -t 30 -b 1M -R 25 80 @@ -129,9 +129,9 @@ Iperf Iperf UDP Uplink l2sim4g_ue - obelix + localhost l2sim4g_ext_dn - obelix + localhost -u -t 30 -b 3M 25 80 @@ -149,7 +149,7 @@ Clean_Test_Server_Images true Clean Test Images on Test Server - obelix + localhost oai-enb oai-lte-ue From 149c4dbf6ea8b6192849d4cbe3177d1227321c26 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Fri, 17 Oct 2025 12:17:15 +0200 Subject: [PATCH 009/275] CI: RFSim-5G update XML files Use node for Create/Deploy/Undeploy() (as done in !3579) --- .../xml_files/container_5g_rfsim_multiue.xml | 19 +++++++------------ .../xml_files/container_5g_rfsim_n2_ho.xml | 19 +++++++------------ 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/ci-scripts/xml_files/container_5g_rfsim_multiue.xml b/ci-scripts/xml_files/container_5g_rfsim_multiue.xml index 79d85e154c..4e8c582099 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_multiue.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_multiue.xml @@ -48,40 +48,36 @@ Pull_Local_Registry Pull Images from Local Registry - 0 + localhost oai-gnb-asan oai-nr-ue Create_Workspace Create new Workspace - 0 - 0 + localhost Deploy_Object Deploy OAI 5G CoreNetwork + localhost ci-scripts/yaml_files/5g_rfsimulator_multiue mysql oai-amf oai-smf oai-upf oai-ext-dn - 0 - 0 Deploy_Object Deploy OAI 5G gNB RF sim SA + localhost ci-scripts/yaml_files/5g_rfsimulator_multiue oai-gnb - 0 - 0 Deploy_Object Deploy OAI 5G NR-UE RF sim SA + localhost ci-scripts/yaml_files/5g_rfsimulator_multiue oai-nr-ue - 0 - 0 @@ -195,15 +191,14 @@ ci-scripts/yaml_files/5g_rfsimulator_multiue 1,0,0,0 1,0,0,0 - 0 - 0 + localhost Clean_Test_Server_Images true Clean Test Images on Test Server - 0 + localhost oai-gnb-asan oai-nr-ue diff --git a/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml b/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml index feffaedf05..404e46434c 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml @@ -55,33 +55,30 @@ Pull_Local_Registry Pull Images from Local Registry - 0 + localhost oai-gnb-asan oai-nr-cuup-asan oai-nr-ue-asan Create_Workspace Create new Workspace - 0 - 0 + localhost Deploy_Object Deploy OAI 5G CoreNetwork + localhost ci-scripts/yaml_files/5g_rfsimulator_n2_ho mysql oai-amf oai-smf oai-upf oai-ext-dn - 0 - 0 Deploy_Object Deploy OAI 5G CU-CP/CU-UP/DU 0 + UE RFsim SA + localhost ci-scripts/yaml_files/5g_rfsimulator_n2_ho oai-cucp-0 oai-cuup-0 oai-du-0 oai-nr-ue - 0 - 0 @@ -94,10 +91,9 @@ Deploy_Object Deploy OAI 5G CU-CP/CU-UP/DU 1 RFsim SA + localhost ci-scripts/yaml_files/5g_rfsimulator_n2_ho oai-cucp-1 oai-cuup-1 oai-du-1 - 0 - 0 @@ -186,9 +182,8 @@ Undeploy_Object true Undeploy all OAI 5G stack + localhost ci-scripts/yaml_files/5g_rfsimulator_n2_ho - 0 - 0 10,100,100,100 10,100,100,100 @@ -197,7 +192,7 @@ Clean_Test_Server_Images true Clean Test Images on Test Server - 0 + localhost oai-gnb-asan oai-nr-cuup-asan oai-nr-ue-asan From 2bacf7ba54da1e41c0efab8a6019aa2e852d8b73 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Fri, 17 Oct 2025 16:20:01 +0200 Subject: [PATCH 010/275] CI: RF-Sim-Test-4G replace node name by localhost By configuring the Jenkins pythonExecutor to match the test machine, we can use localhost as the node for running individual test cases. This change makes it easy to move tests across machines, as the only adjustment needed is updating the pythonExecutor in Jenkins. --- .../xml_files/container_4g_rfsim_fdd_05MHz.xml | 18 +++++++++--------- .../container_4g_rfsim_fdd_05MHz_noS1.xml | 16 ++++++++-------- .../xml_files/container_4g_rfsim_fdd_10MHz.xml | 18 +++++++++--------- .../xml_files/container_4g_rfsim_fdd_20MHz.xml | 18 +++++++++--------- .../xml_files/container_4g_rfsim_fembms.xml | 4 ++-- .../xml_files/container_4g_rfsim_mbms.xml | 4 ++-- .../xml_files/container_4g_rfsim_tdd_05MHz.xml | 16 ++++++++-------- 7 files changed, 47 insertions(+), 47 deletions(-) diff --git a/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz.xml b/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz.xml index 4b93c10fcd..562856b00c 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz.xml @@ -103,16 +103,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim4g_ue - acamas + localhost Ping Ping Traffic-Gen from LTE-UE rfsim4g_ue - acamas + localhost rfsim4g_ext_dn - acamas + localhost -c 20 5 @@ -121,9 +121,9 @@ Ping Ping LTE-UE from Traffic-Gen rfsim4g_ext_dn - acamas + localhost rfsim4g_ue - acamas + localhost -c 20 5 @@ -136,8 +136,8 @@ -u -t 30 -b 2M -R 25 80 - acamas - acamas + localhost + localhost @@ -148,8 +148,8 @@ -u -t 30 -b 1M 25 80 - acamas - acamas + localhost + localhost diff --git a/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz_noS1.xml b/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz_noS1.xml index 968e30071a..1773d5d439 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz_noS1.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz_noS1.xml @@ -83,9 +83,9 @@ Ping Ping Traffic-Gen from LTE-UE rfsim4g_ue - acamas + localhost rfsim4g_enb_nos1 - acamas + localhost -c 20 5 @@ -94,9 +94,9 @@ Ping Ping LTE-UE from eNB rfsim4g_enb_nos1 - acamas + localhost rfsim4g_ue - acamas + localhost -c 20 5 @@ -105,9 +105,9 @@ Iperf Iperf UDP DL rfsim4g_ue - acamas + localhost rfsim4g_enb_nos1 - acamas + localhost -u -t 30 -b 2M -R 25 80 @@ -117,9 +117,9 @@ Iperf Iperf UDP UL rfsim4g_ue - acamas + localhost rfsim4g_enb_nos1 - acamas + localhost -u -t 30 -b 1M 25 80 diff --git a/ci-scripts/xml_files/container_4g_rfsim_fdd_10MHz.xml b/ci-scripts/xml_files/container_4g_rfsim_fdd_10MHz.xml index 076ee6995b..7861d8f332 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fdd_10MHz.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fdd_10MHz.xml @@ -103,16 +103,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim4g_ue - acamas + localhost Ping Ping Traffic-Gen from LTE-UE rfsim4g_ue - acamas + localhost rfsim4g_ext_dn - acamas + localhost -c 20 5 @@ -121,9 +121,9 @@ Ping Ping LTE-UE from Traffic-Gen rfsim4g_ext_dn - acamas + localhost rfsim4g_ue - acamas + localhost -c 20 5 @@ -132,9 +132,9 @@ Iperf Iperf UDP DL rfsim4g_ue - acamas + localhost rfsim4g_ext_dn - acamas + localhost -u -t 30 -b 2M -R 25 80 @@ -144,9 +144,9 @@ Iperf Iperf UDP UL rfsim4g_ue - acamas + localhost rfsim4g_ext_dn - acamas + localhost -u -t 30 -b 1M 25 80 diff --git a/ci-scripts/xml_files/container_4g_rfsim_fdd_20MHz.xml b/ci-scripts/xml_files/container_4g_rfsim_fdd_20MHz.xml index 2e049e4efe..9199f276e7 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fdd_20MHz.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fdd_20MHz.xml @@ -103,16 +103,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim4g_ue - acamas + localhost Ping Ping Traffic-Gen from LTE-UE rfsim4g_ue - acamas + localhost rfsim4g_ext_dn - acamas + localhost -c 20 5 @@ -121,9 +121,9 @@ Ping Ping LTE-UE from Traffic-Gen rfsim4g_ext_dn - acamas + localhost rfsim4g_ue - acamas + localhost -c 20 5 @@ -132,9 +132,9 @@ Iperf Iperf UDP DL rfsim4g_ue - acamas + localhost rfsim4g_ext_dn - acamas + localhost -u -t 30 -b 2M -R 25 80 @@ -144,9 +144,9 @@ Iperf Iperf UDP UL rfsim4g_ue - acamas + localhost rfsim4g_ext_dn - acamas + localhost -u -t 30 -b 1M 25 80 diff --git a/ci-scripts/xml_files/container_4g_rfsim_fembms.xml b/ci-scripts/xml_files/container_4g_rfsim_fembms.xml index 7ab434c804..73c01f2b32 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fembms.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fembms.xml @@ -80,9 +80,9 @@ Iperf2_Unidir Iperf2 UDP DL rfsim4g_ue_fembms - acamas + localhost rfsim4g_enb_fembms - acamas + localhost -u -t 30 -b 2M 25 80 diff --git a/ci-scripts/xml_files/container_4g_rfsim_mbms.xml b/ci-scripts/xml_files/container_4g_rfsim_mbms.xml index e6a4cde19a..ea00caa09e 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_mbms.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_mbms.xml @@ -80,9 +80,9 @@ Iperf Iperf UDP DL rfsim4g_ue - acamas + localhost rfsim4g_enb_nos1 - acamas + localhost -u -t 30 -b 2M -R 25 80 diff --git a/ci-scripts/xml_files/container_4g_rfsim_tdd_05MHz.xml b/ci-scripts/xml_files/container_4g_rfsim_tdd_05MHz.xml index 2e22577879..d3129e0c68 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_tdd_05MHz.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_tdd_05MHz.xml @@ -103,9 +103,9 @@ Ping Ping Traffic-Gen from LTE-UE rfsim4g_ue - acamas + localhost rfsim4g_ext_dn - acamas + localhost -c 20 5 @@ -114,9 +114,9 @@ Ping Ping LTE-UE from Traffic-Gen rfsim4g_ext_dn - acamas + localhost rfsim4g_ue - acamas + localhost -c 20 5 @@ -125,9 +125,9 @@ Iperf Iperf UDP DL rfsim4g_ue - acamas + localhost rfsim4g_ext_dn - acamas + localhost -u -t 30 -b 2M -R 25 80 @@ -137,9 +137,9 @@ Iperf Iperf UDP UL rfsim4g_ue - acamas + localhost rfsim4g_ext_dn - acamas + localhost -u -t 30 -b 1M 25 80 From c8b09f570f744bd02aad963e7a1291acbff15467 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Fri, 17 Oct 2025 16:21:12 +0200 Subject: [PATCH 011/275] CI: RF-Sim-Test-5G replace node name by localhost By configuring the Jenkins pythonExecutor to match the test machine, we can use localhost as the node for running individual test cases. This change makes it easy to move tests across machines, as the only adjustment needed is updating the pythonExecutor in Jenkins. --- .../xml_files/container_5g_e1_rfsim.xml | 6 +-- .../xml_files/container_5g_f1_rfsim.xml | 38 +++++++++--------- .../xml_files/container_5g_fdd_rfsim.xml | 18 ++++----- ci-scripts/xml_files/container_5g_rfsim.xml | 40 +++++++++---------- .../xml_files/container_5g_rfsim_24prb.xml | 22 +++++----- .../xml_files/container_5g_rfsim_2x2.xml | 18 ++++----- .../container_5g_rfsim_fdd_phytest.xml | 18 ++++----- .../container_5g_rfsim_fr2_32prb.xml | 10 ++--- .../xml_files/container_5g_rfsim_multiue.xml | 36 ++++++++--------- .../xml_files/container_5g_rfsim_n2_ho.xml | 26 ++++++------ .../xml_files/container_5g_rfsim_ntn_geo.xml | 18 ++++----- .../xml_files/container_5g_rfsim_ntn_leo.xml | 10 ++--- .../xml_files/container_5g_rfsim_sidelink.xml | 2 +- .../xml_files/container_5g_rfsim_tdd_dora.xml | 10 ++--- .../xml_files/container_5g_rfsim_u0_25prb.xml | 24 +++++------ 15 files changed, 148 insertions(+), 148 deletions(-) diff --git a/ci-scripts/xml_files/container_5g_e1_rfsim.xml b/ci-scripts/xml_files/container_5g_e1_rfsim.xml index d27091ac63..54b34e0cff 100644 --- a/ci-scripts/xml_files/container_5g_e1_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_e1_rfsim.xml @@ -76,16 +76,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue rfsim5g_ue2 rfsim5g_ue3 - acamas acamas acamas + localhost localhost localhost Ping Ping ext-dn from all NR-UEs rfsim5g_ue rfsim5g_ue2 rfsim5g_ue3 - acamas acamas acamas + localhost localhost localhost rfsim5g_5gc_fixed_ip - acamas + localhost -c 20 -i0.25 5 diff --git a/ci-scripts/xml_files/container_5g_f1_rfsim.xml b/ci-scripts/xml_files/container_5g_f1_rfsim.xml index 50e794e4cb..d8dc580a04 100644 --- a/ci-scripts/xml_files/container_5g_f1_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_f1_rfsim.xml @@ -107,7 +107,7 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - acamas + localhost @@ -122,9 +122,9 @@ Ping Ping ext-dn from all NR-UEs rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i0.25 5 @@ -132,7 +132,7 @@ Custom_Command Simulate a DL radio channel with noise (3 dB) - acamas + localhost echo channelmod modify 0 noise_power_dB 3 | ncat --send-only 192.168.71.181 8091 @@ -141,9 +141,9 @@ Iperf (DL/3Mbps/UDP)(30 sec) -u -b 3M -t 20 -R rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -153,9 +153,9 @@ Iperf (UL/3Mbps/UDP)(30 sec) -u -b 3M -t 20 rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -163,7 +163,7 @@ Custom_Command Trigger Reestablishment - acamas + localhost echo ci force_reestab | ncat 192.168.71.171 9090 | grep -E 'Reset RLC counters of UE RNTI [0-9a-f]{4} to trigger reestablishment' @@ -171,9 +171,9 @@ Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i0.5 -w25 80 @@ -181,56 +181,56 @@ Custom_Command Verify Reestablishment - acamas + localhost echo ci get_reestab_count | ncat 192.168.71.150 9090 | grep -E 'UE RNTI [0-9a-f]{4} reestab 1' Custom_Command Simulate a disruption of DL radio channel (ploss 55) - acamas + localhost echo channelmod modify 0 ploss 55 | ncat 192.168.71.181 8091 Custom_Command Get UE sync state (UE ID 0) - acamas + localhost echo ciUE sync_state 0 | ncat 192.168.71.181 8091 | grep -E UE_NOT_SYNC Custom_Command Restoration of the original DL channel conditions (ploss 20) - acamas + localhost echo channelmod modify 0 ploss 20 | ncat 192.168.71.181 8091 Custom_Command Get UE sync state (UE ID 0) - acamas + localhost echo ciUE sync_state 0 | ncat 192.168.71.181 8091 | grep -E UE_CONNECTED Custom_Command Trigger Handover - acamas + localhost echo ci trigger_f1_ho | ncat 192.168.71.150 9090 Custom_Command UE (1) connected to DU-PCI 0? - acamas + localhost echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.150 9090 | grep "3584" Custom_Command UE (1) connected to DU-PCI 1? - acamas + localhost echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.150 9090 | grep "3585" diff --git a/ci-scripts/xml_files/container_5g_fdd_rfsim.xml b/ci-scripts/xml_files/container_5g_fdd_rfsim.xml index 6ce1e37b90..3c4e30c97b 100644 --- a/ci-scripts/xml_files/container_5g_fdd_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_fdd_rfsim.xml @@ -70,16 +70,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - acamas + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -88,9 +88,9 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_ue - acamas + localhost -c 20 -i 0.25 5 @@ -100,9 +100,9 @@ Iperf (DL/3Mbps/UDP)(30 sec) -u -b 3M -t 30 -R rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -112,9 +112,9 @@ Iperf (UL/3Mbps/UDP)(30 sec) -u -b 3M -t 30 rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 diff --git a/ci-scripts/xml_files/container_5g_rfsim.xml b/ci-scripts/xml_files/container_5g_rfsim.xml index d7fb38ef8f..e2566bfd25 100644 --- a/ci-scripts/xml_files/container_5g_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_rfsim.xml @@ -125,16 +125,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue rfsim5g_ue2 rfsim5g_ue3 rfsim5g_ue4 rfsim5g_ue5 rfsim5g_ue6 rfsim5g_ue7 rfsim5g_ue8 rfsim5g_ue9 rfsim5g_ue10 - acamas acamas acamas acamas acamas acamas acamas acamas acamas acamas + localhost localhost localhost localhost localhost localhost localhost localhost localhost localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -143,9 +143,9 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_ue - acamas + localhost -c 20 -i 0.25 5 @@ -154,9 +154,9 @@ Ping Ping ext-dn from Second NR-UE rfsim5g_ue2 - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -165,9 +165,9 @@ Ping Ping Second NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_ue2 - acamas + localhost -c 20 -i 0.25 5 @@ -176,9 +176,9 @@ Ping Ping ext-dn from both UEs rfsim5g_ue rfsim5g_ue2 - acamas acamas + localhost localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -187,9 +187,9 @@ Ping Ping ext-dn from all UEs rfsim5g_ue rfsim5g_ue2 rfsim5g_ue3 rfsim5g_ue4 rfsim5g_ue5 rfsim5g_ue6 rfsim5g_ue7 rfsim5g_ue8 rfsim5g_ue9 rfsim5g_ue10 - acamas acamas acamas acamas acamas acamas acamas acamas acamas acamas + localhost localhost localhost localhost localhost localhost localhost localhost localhost localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 -s 1016 5 @@ -199,9 +199,9 @@ Iperf (DL/3Mbps/UDP)(30 sec) -u -b 3M -t 20 -R rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -211,9 +211,9 @@ Iperf (UL/1Mbps/UDP)(30 sec) -u -b 1M -t 20 rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -222,21 +222,21 @@ Attach_UE Attach OAI UE 1 and 2 rfsim5g_ue rfsim5g_ue2 - acamas acamas + localhost localhost Detach_UE Detach OAI UE 1 and 2 rfsim5g_ue rfsim5g_ue2 - acamas acamas + localhost localhost Detach_UE Detach OAI UE 3 to 10 rfsim5g_ue3 rfsim5g_ue4 rfsim5g_ue5 rfsim5g_ue6 rfsim5g_ue7 rfsim5g_ue8 rfsim5g_ue9 rfsim5g_ue10 - acamas acamas acamas acamas acamas acamas acamas acamas + localhost localhost localhost localhost localhost localhost localhost localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_24prb.xml b/ci-scripts/xml_files/container_5g_rfsim_24prb.xml index 7db37d8e63..849c91a7b0 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_24prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_24prb.xml @@ -88,7 +88,7 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - acamas + localhost @@ -101,9 +101,9 @@ Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i0.5 80 @@ -112,9 +112,9 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_ue - acamas + localhost -c 20 -i0.2 5 @@ -124,9 +124,9 @@ Iperf (DL/3Mbps/UDP)(30 sec) -u -b 3M -t 20 -R rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -136,9 +136,9 @@ Iperf (UL/1Mbps/UDP)(30 sec) -u -b 1M -t 20 rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -146,14 +146,14 @@ Custom_Command Force Msg3 C-RNTI RA - acamas + localhost echo ciUE force_crnti_ra | ncat 192.168.71.150 8091 Custom_Command Force RRC_IDLE (UE ID 0) - acamas + localhost echo ciUE force_RRC_IDLE | ncat 192.168.71.150 8091 diff --git a/ci-scripts/xml_files/container_5g_rfsim_2x2.xml b/ci-scripts/xml_files/container_5g_rfsim_2x2.xml index 75924e1ca2..ed0e11807c 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_2x2.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_2x2.xml @@ -70,16 +70,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - acamas + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -88,9 +88,9 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_ue - acamas + localhost -c 20 -i 0.25 5 @@ -100,9 +100,9 @@ Iperf (DL/10Mbps/UDP)(30 sec) -u -b 10M -t 20 -R rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -112,9 +112,9 @@ Iperf (UL/3Mbps/UDP)(30 sec) -u -b 3M -t 20 rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 diff --git a/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml b/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml index b376df99f4..4ff5dbedc8 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml @@ -49,7 +49,7 @@ Custom_Command Clean-Up any residual volume - acamas + localhost docker volume rm 5g_rfsimulator_fdd_phytest_rrc.config -f @@ -77,9 +77,9 @@ Ping Ping gNB from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_gnb_nos1 - acamas + localhost -c20 -i0.2 0 @@ -88,9 +88,9 @@ Ping Ping NR-UE from gNB rfsim5g_gnb_nos1 - acamas + localhost rfsim5g_ue - acamas + localhost -c20 -i0.2 0 @@ -100,9 +100,9 @@ Iperf (DL/30kbps/UDP)(30 sec) -u -b 0.03M -t 20 -R -c 10.0.1.1 rfsim5g_ue - acamas + localhost rfsim5g_gnb_nos1 - acamas + localhost 5 90 @@ -112,9 +112,9 @@ Iperf (UL/30kbps/UDP)(30 sec) -u -b 0.03M -t 20 -c 10.0.1.1 rfsim5g_ue - acamas + localhost rfsim5g_gnb_nos1 - acamas + localhost 5 90 diff --git a/ci-scripts/xml_files/container_5g_rfsim_fr2_32prb.xml b/ci-scripts/xml_files/container_5g_rfsim_fr2_32prb.xml index 6a47da0ff9..37b90f2385 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_fr2_32prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_fr2_32prb.xml @@ -70,16 +70,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - acamas + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i0.2 0 @@ -88,9 +88,9 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_ue - acamas + localhost -c 20 -i0.2 0 diff --git a/ci-scripts/xml_files/container_5g_rfsim_multiue.xml b/ci-scripts/xml_files/container_5g_rfsim_multiue.xml index 4e8c582099..215f58c2a7 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_multiue.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_multiue.xml @@ -84,16 +84,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_multiue1 rfsim5g_multiue2 rfsim5g_multiue3 rfsim5g_multiue4 rfsim5g_multiue5 rfsim5g_multiue6 rfsim5g_multiue7 rfsim5g_multiue8 rfsim5g_multiue9 rfsim5g_multiue10 - acamas acamas acamas acamas acamas acamas acamas acamas acamas acamas + localhost localhost localhost localhost localhost localhost localhost localhost localhost localhost Ping Ping ext-dn from NR-UE rfsim5g_multiue1 - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -102,9 +102,9 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_multiue1 - acamas + localhost -c 20 -i 0.25 5 @@ -113,9 +113,9 @@ Ping Ping ext-dn from Second NR-UE rfsim5g_multiue2 - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -124,9 +124,9 @@ Ping Ping Second NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_multiue2 - acamas + localhost -c 20 -i 0.25 5 @@ -135,9 +135,9 @@ Ping Ping ext-dn from both UEs rfsim5g_multiue1 rfsim5g_multiue2 - acamas acamas + localhost localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -146,9 +146,9 @@ Ping Ping ext-dn from all UEs rfsim5g_multiue1 rfsim5g_multiue2 rfsim5g_multiue3 rfsim5g_multiue4 rfsim5g_multiue5 rfsim5g_multiue6 rfsim5g_multiue7 rfsim5g_multiue8 rfsim5g_multiue9 rfsim5g_multiue10 - acamas acamas acamas acamas acamas acamas acamas acamas acamas acamas + localhost localhost localhost localhost localhost localhost localhost localhost localhost localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 -s 1016 5 @@ -158,9 +158,9 @@ Iperf (DL/3Mbps/UDP)(30 sec) -u -b 3M -t 20 -R rfsim5g_multiue1 - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -170,9 +170,9 @@ Iperf (UL/1Mbps/UDP)(30 sec) -u -b 1M -t 20 rfsim5g_multiue1 - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -181,7 +181,7 @@ Detach_UE Detach OAI UEs rfsim5g_multiue1 - acamas + localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml b/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml index 404e46434c..5da417c284 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml @@ -85,7 +85,7 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - acamas + localhost @@ -100,9 +100,9 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_ue - acamas + localhost -c 50 -i 0.25 5 @@ -111,9 +111,9 @@ Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 50 -i 0.25 10 @@ -123,9 +123,9 @@ Iperf (DL/10Mbps/UDP)(30 sec) -u -b 3M -t 20 -R rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -135,9 +135,9 @@ Iperf (UL/4Mbps/UDP)(30 sec) -u -b 1M -t 20 rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -151,7 +151,7 @@ Custom_Command Trigger N2 Handover - acamas + localhost echo ci trigger_n2_ho 1,1 | ncat 192.168.71.150 9090 yes @@ -159,14 +159,14 @@ Custom_Command UE (1) connected to DU-PCI 1? - acamas + localhost echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.180 9090 | grep "1234" Custom_Command Trigger N2 Handover - acamas + localhost echo ci trigger_n2_ho 0,1 | ncat 192.168.71.180 9090 yes @@ -174,7 +174,7 @@ Custom_Command UE (1) connected to DU-PCI 0? - acamas + localhost echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.150 9090 | grep "3584" diff --git a/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml b/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml index 7107b7971b..32bbbc683d 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml @@ -74,16 +74,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - acamas + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -92,9 +92,9 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_ue - acamas + localhost -c 20 -i 0.25 5 @@ -102,7 +102,7 @@ Custom_Command Trigger Reestablishment - acamas + localhost echo ci force_reestab | ncat 192.168.71.140 9090 | grep -E 'Reset RLC counters of UE RNTI [0-9a-f]{4} to trigger reestablishment' yes @@ -111,9 +111,9 @@ Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i0.5 -w25 80 @@ -121,7 +121,7 @@ Custom_Command Verify Reestablishment - acamas + localhost echo ci get_reestab_count | ncat 192.168.71.140 9090 | grep -E 'UE RNTI [0-9a-f]{4} reestab 1' yes diff --git a/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml b/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml index 63897cfb8b..dd9fb56067 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml @@ -69,16 +69,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - acamas + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -87,9 +87,9 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_ue - acamas + localhost -c 20 -i 0.25 5 diff --git a/ci-scripts/xml_files/container_5g_rfsim_sidelink.xml b/ci-scripts/xml_files/container_5g_rfsim_sidelink.xml index 58bff964d4..6ddd6d13b7 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_sidelink.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_sidelink.xml @@ -74,7 +74,7 @@ Custom_Command Check that UE synched - acamas + localhost docker logs rfsim5g-oai-nr-ue-2 | grep -m 1 "PSBCH RX:OK" diff --git a/ci-scripts/xml_files/container_5g_rfsim_tdd_dora.xml b/ci-scripts/xml_files/container_5g_rfsim_tdd_dora.xml index 97730bd233..a9f7f2c3aa 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_tdd_dora.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_tdd_dora.xml @@ -49,7 +49,7 @@ Custom_Command Clean-Up any residual volume - acamas + localhost docker volume rm 5g_rfsimulator_tdd_dora_rrc.config -f @@ -83,9 +83,9 @@ Ping Ping gNB from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_gnb_nos1 - acamas + localhost -c20 -i0.2 0 @@ -94,9 +94,9 @@ Ping Ping NR-UE from gNB rfsim5g_gnb_nos1 - acamas + localhost rfsim5g_ue - acamas + localhost -c20 -i0.2 0 diff --git a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml index c5c76124d3..f70c81732e 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml @@ -72,16 +72,16 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - acamas + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -90,9 +90,9 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - acamas + localhost rfsim5g_ue - acamas + localhost -c 20 -i 0.25 5 @@ -101,16 +101,16 @@ Attach_UE Verify 2nd PDU session is up rfsim5g_ue_pdu_2 - acamas + localhost Ping Ping ext-dn from NR-UE on PDU session ID 2 rfsim5g_ue_pdu_2 - acamas + localhost rfsim5g_ext_dn - acamas + localhost -c 20 -i 0.25 5 @@ -120,9 +120,9 @@ Iperf (DL/3Mbps/UDP)(30 sec) -u -b 3M -t 20 -R rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 @@ -132,9 +132,9 @@ Iperf (UL/1Mbps/UDP)(30 sec) -u -b 1M -t 20 rfsim5g_ue - acamas + localhost rfsim5g_ext_dn - acamas + localhost 5 90 From 4d372c5f58b9fbf9b3dfb37661b2ddb53b36f626 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Fri, 17 Oct 2025 18:07:50 +0800 Subject: [PATCH 012/275] Improve T-Tracer GUI for UE Make PHY measurements diagrams larger for a better view, reduce PHY/MAC/RLC/PDCP/RRC textlog height --- common/utils/T/tracer/ue.c | 50 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/common/utils/T/tracer/ue.c b/common/utils/T/tracer/ue.c index f4ea38f980..6d7d9ce4c5 100644 --- a/common/utils/T/tracer/ue.c +++ b/common/utils/T/tracer/ue.c @@ -196,7 +196,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, view *v; logger *l; - main_window = new_toplevel_window(g, 1200, 900, "eNB tracer"); + main_window = new_toplevel_window(g, 1200, 900, "UE tracer"); top_container = new_container(g, VERTICAL); widget_add_child(g, main_window, top_container, -1); @@ -207,7 +207,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, /* logo */ widget_add_child(g, line, logo, -1); - input_signal_plot = new_xy_plot(g, 256, 55, "input signal", 20); + input_signal_plot = new_xy_plot(g, 512, 110, "input signal", 20); widget_add_child(g, line, input_signal_plot, -1); xy_plot_set_range(g, input_signal_plot, 0, 7680*10, 20, 70); input_signal_log = new_framelog(h, database, @@ -221,7 +221,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, logger_add_view(input_signal_log, input_signal_view); /* UE x PDSCH IQ data */ - w = new_xy_plot(g, 55, 55, "", 50); + w = new_xy_plot(g, 110, 110, "", 50); e->pdsch_iq_ue_xy_plot = w; widget_add_child(g, line, w, -1); xy_plot_set_range(g, w, -500, 500, -500, 500); @@ -232,7 +232,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, e->pdsch_iq_ue_logger = l; /* UE x estimated DL channel */ - w = new_xy_plot(g, 280, 55, "", 50); + w = new_xy_plot(g, 560, 110, "", 50); e->dl_estimate_ue_xy_plot = w; widget_add_child(g, line, w, -1); xy_plot_set_range(g, w, 0, 512*10, -10, 80); @@ -245,7 +245,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, e->dl_estimate_ue_logger = l; /* PHY Meas */ - w = new_xy_plot(g, 128, 55, "", 50); + w = new_xy_plot(g, 256, 110, "", 50); e->phy_meas_xy_plot = w; widget_add_child(g, line, w, -1); xy_plot_set_range(g, w, 0, 1024*10, -130, 35); @@ -271,7 +271,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, /* UE x DL mcs */ line = new_container(g, HORIZONTAL); widget_add_child(g, top_container, line, -1); - w = new_xy_plot(g, 128, 55, "", 20); + w = new_xy_plot(g, 192, 110, "", 20); xy_plot_set_range(g, w, 0, 1024*10, -2, 30); e->dl_mcs_xy_plot = w; widget_add_child(g, line, w, -1); @@ -282,7 +282,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, e->dl_mcs_logger = l; /* UE x UL mcs */ - w = new_xy_plot(g, 128, 55, "", 20); + w = new_xy_plot(g, 192, 110, "", 20); xy_plot_set_range(g, w, 0, 1024*10, -2, 30); e->ul_mcs_xy_plot = w; widget_add_child(g, line, w, -1); @@ -295,7 +295,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, /* UE x PUSCH TX Power */ // line = new_container(g, HORIZONTAL); // widget_add_child(g, top_container, line, -1); - w = new_xy_plot(g, 128, 55, "", 20); + w = new_xy_plot(g, 192, 110, "", 20); e->pusch_power_xy_plot = w; widget_add_child(g, line, w, -1); xy_plot_set_range(g, w, 0, 1024*10, -30, 50); @@ -313,7 +313,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, /* UE x PUCCH TX Power */ // line = new_container(g, HORIZONTAL); // widget_add_child(g, top_container, line, -1); - w = new_xy_plot(g, 128, 55, "", 20); + w = new_xy_plot(g, 192, 110, "", 20); e->pucch_power_xy_plot = w; widget_add_child(g, line, w, -1); xy_plot_set_range(g, w, 0, 1024*10, -30, 50); @@ -329,7 +329,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, e->pucch_ampl_logger = l; /* UE x PDSCH energy */ - w = new_xy_plot(g, 128, 55, "", 50); + w = new_xy_plot(g, 192, 110, "", 50); e->pdsch_energy_ue_xy_plot = w; widget_add_child(g, line, w, -1); xy_plot_set_range(g, w, 0, 1024*10, -10, 80); @@ -355,7 +355,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, e->pdsch_energy_logger = l; /* UE x PDCCH energy */ - w = new_xy_plot(g, 128, 55, "", 50); + w = new_xy_plot(g, 192, 110, "", 50); e->pdcch_energy_ue_xy_plot = w; widget_add_child(g, line, w, -1); xy_plot_set_range(g, w, 0, 1024*10, -10, 80); @@ -381,7 +381,7 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, e->pdcch_energy_logger = l; /* UE x PDCCH IQ data */ - w = new_xy_plot(g, 55, 55, "", 50); + w = new_xy_plot(g, 110, 110, "", 50); e->pdcch_iq_ue_xy_plot = w; widget_add_child(g, line, w, -1); xy_plot_set_range(g, w, -100, 100, -100, 100); @@ -544,16 +544,16 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, /* phy/mac/rlc/pdcp/rrc textlog */ line = new_container(g, HORIZONTAL); widget_add_child(g, top_container, line, -1); - container_set_child_growable(g, top_container, line, 1); + container_set_child_growable(g, top_container, line, 0); /* phy */ col = new_container(g, VERTICAL); widget_add_child(g, line, col, -1); container_set_child_growable(g, line, col, 1); widget_add_child(g, col, new_label(g, "PHY"), -1); - text = new_textlist(g, 100, 10, new_color(g, "#afa")); + text = new_textlist(g, 100, 5, new_color(g, "#afa")); widget_add_child(g, col, text, -1); - container_set_child_growable(g, col, text, 1); + container_set_child_growable(g, col, text, 0); textview = new_view_textlist(10000, 10, g, text); e->phyview = textview; @@ -562,24 +562,24 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, widget_add_child(g, line, col, -1); container_set_child_growable(g, line, col, 1); widget_add_child(g, col, new_label(g, "MAC"), -1); - text = new_textlist(g, 100, 10, new_color(g, "#adf")); + text = new_textlist(g, 100, 5, new_color(g, "#adf")); widget_add_child(g, col, text, -1); - container_set_child_growable(g, col, text, 1); + container_set_child_growable(g, col, text, 0); textview = new_view_textlist(10000, 10, g, text); e->macview = textview; line = new_container(g, HORIZONTAL); widget_add_child(g, top_container, line, -1); - container_set_child_growable(g, top_container, line, 1); + container_set_child_growable(g, top_container, line, 0); /* rlc */ col = new_container(g, VERTICAL); widget_add_child(g, line, col, -1); container_set_child_growable(g, line, col, 1); widget_add_child(g, col, new_label(g, "RLC"), -1); - text = new_textlist(g, 100, 10, new_color(g, "#aff")); + text = new_textlist(g, 100, 5, new_color(g, "#aff")); widget_add_child(g, col, text, -1); - container_set_child_growable(g, col, text, 1); + container_set_child_growable(g, col, text, 0); textview = new_view_textlist(10000, 10, g, text); e->rlcview = textview; @@ -588,24 +588,24 @@ static void ue_main_gui(ue_gui *e, gui *g, event_handler *h, void *database, widget_add_child(g, line, col, -1); container_set_child_growable(g, line, col, 1); widget_add_child(g, col, new_label(g, "PDCP"), -1); - text = new_textlist(g, 100, 10, new_color(g, "#ed9")); + text = new_textlist(g, 100, 5, new_color(g, "#ed9")); widget_add_child(g, col, text, -1); - container_set_child_growable(g, col, text, 1); + container_set_child_growable(g, col, text, 0); textview = new_view_textlist(10000, 10, g, text); e->pdcpview = textview; line = new_container(g, HORIZONTAL); widget_add_child(g, top_container, line, -1); - container_set_child_growable(g, top_container, line, 1); + container_set_child_growable(g, top_container, line, 0); /* rrc */ col = new_container(g, VERTICAL); widget_add_child(g, line, col, -1); container_set_child_growable(g, line, col, 1); widget_add_child(g, col, new_label(g, "RRC"), -1); - text = new_textlist(g, 100, 10, new_color(g, "#fdb")); + text = new_textlist(g, 100, 5, new_color(g, "#fdb")); widget_add_child(g, col, text, -1); - container_set_child_growable(g, col, text, 1); + container_set_child_growable(g, col, text, 0); textview = new_view_textlist(10000, 10, g, text); e->rrcview = textview; From f231655b31d4e36c98e602120094c2085b60ee96 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Mon, 20 Oct 2025 15:04:26 +0100 Subject: [PATCH 013/275] Restore UE Capability IE after RRCReestablishment --- openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index debc0d5530..064857a8e3 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -967,6 +967,8 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc) ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup); UE->CellGroup = oldUE->CellGroup; oldUE->CellGroup = NULL; + UE->capability = oldUE->capability; + oldUE->capability = NULL; UE->mac_stats = oldUE->mac_stats; UE->measgap_config = oldUE->measgap_config; /* 38.331 5.3.7.2 says that the UE releases the spCellConfig, so we drop it From c645e8b0962eaf78b93b867341ff32bfb454acb3 Mon Sep 17 00:00:00 2001 From: Mario Joa-Ng Date: Wed, 13 Aug 2025 18:36:08 +0200 Subject: [PATCH 014/275] Fix xran: Correctly read PRACH beam index Upgrade xran patch, bump version number. --- .../F/oaioran_F.patch | 154 ++++++++++-------- radio/fhi_72/CMakeLists.txt | 2 +- 2 files changed, 89 insertions(+), 67 deletions(-) diff --git a/cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch b/cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch index bec423b95e..3f953b36d9 100644 --- a/cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch +++ b/cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch @@ -1,5 +1,5 @@ diff --git a/fhi_lib/app/src/common.h b/fhi_lib/app/src/common.h -index ac5f471..5efcce3 100644 +index ac5f471..b0f7ed1 100644 --- a/fhi_lib/app/src/common.h +++ b/fhi_lib/app/src/common.h @@ -28,7 +28,7 @@ @@ -7,7 +7,7 @@ index ac5f471..5efcce3 100644 #include -#define VERSIONX "oran_f_release_v1.0" -+#define VERSIONX "oran_f_release_v1.4" ++#define VERSIONX "oran_f_release_v1.5" #define APP_O_DU 0 #define APP_O_RU 1 @@ -919,7 +919,7 @@ index 4acade1..c939edc 100644 for(i = 0; i < xran_ports_num; i++){ g_xran_dev_ctx[i] = pCtx; diff --git a/fhi_lib/lib/src/xran_main.c b/fhi_lib/lib/src/xran_main.c -index 7c472d7..216c7f5 100644 +index 7c472d7..9da3621 100644 --- a/fhi_lib/lib/src/xran_main.c +++ b/fhi_lib/lib/src/xran_main.c @@ -35,7 +35,11 @@ @@ -969,7 +969,29 @@ index 7c472d7..216c7f5 100644 print_dbg("PRACH eAxC_offset %d\n", pPrachCPConfig->eAxC_offset); /* Save some configs for app */ -@@ -1570,6 +1593,7 @@ int32_t handle_ecpri_ethertype(struct rte_mbuf* pkt_q[], uint16_t xport_id, stru +@@ -1169,6 +1192,10 @@ xran_prepare_cp_ul_slot(uint16_t xran_port_id, uint32_t nSlotIdx, uint32_t nCcS + uint8_t seqid = xran_get_cp_seqid(pHandle, XRAN_DIR_UL, cc_id, port_id); + + beam_id = xran_get_beamid(pHandle, XRAN_DIR_UL, cc_id, port_id, slot_id); ++ pBufList = &(p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[buf_id][cc_id][ant_id].sBufferList); ++ struct xran_prb_map *prbMap = pBufList->pBuffers->pData; ++ struct xran_prb_elm *pPrbElm = &prbMap->prbMap[0]; //mjoang ++ beam_id = pPrbElm->nBeamIndex; + ret = generate_cpmsg_prach(pHandle, ¶ms, sect_geninfo, mbuf, p_xran_dev_ctx, + frame_id, subframe_id, slot_id, tti, + beam_id, cc_id, port_id, occasionid, seqid); +@@ -1338,6 +1365,10 @@ tx_cp_ul_cb(struct rte_timer *tim, void *arg) + uint8_t seqid = xran_get_cp_seqid(pHandle, XRAN_DIR_UL, cc_id, port_id); + + beam_id = xran_get_beamid(pHandle, XRAN_DIR_UL, cc_id, port_id, slot_id); ++ pBufList = &(p_xran_dev_ctx->sFrontHaulRxPrbMapBbuIoBufCtrl[buf_id][cc_id][ant_id].sBufferList); ++ struct xran_prb_map *prbMap = pBufList->pBuffers->pData; ++ struct xran_prb_elm *pPrbElm = &prbMap->prbMap[0]; //mjoang ++ beam_id = pPrbElm->nBeamIndex; + ret = generate_cpmsg_prach(pHandle, ¶ms, sect_geninfo, mbuf, p_xran_dev_ctx, + frame_id, subframe_id, slot_id, tti, + beam_id, cc_id, port_id, occasionid, seqid); +@@ -1570,6 +1601,7 @@ int32_t handle_ecpri_ethertype(struct rte_mbuf* pkt_q[], uint16_t xport_id, stru { case ECPRI_IQ_DATA: pkt_data[num_data++] = pkt; @@ -977,7 +999,7 @@ index 7c472d7..216c7f5 100644 break; // For RU emulation case ECPRI_RT_CONTROL_DATA: -@@ -1587,7 +1611,7 @@ int32_t handle_ecpri_ethertype(struct rte_mbuf* pkt_q[], uint16_t xport_id, stru +@@ -1587,7 +1619,7 @@ int32_t handle_ecpri_ethertype(struct rte_mbuf* pkt_q[], uint16_t xport_id, stru break; default: if (p_dev_ctx->fh_init.io_cfg.id == O_DU) { @@ -986,7 +1008,7 @@ index 7c472d7..216c7f5 100644 } break; } -@@ -1876,7 +1900,7 @@ xran_sector_get_instances (uint32_t xran_port, void * pDevHandle, uint16_t nNumI +@@ -1876,7 +1908,7 @@ xran_sector_get_instances (uint32_t xran_port, void * pDevHandle, uint16_t nNumI for (i = 0; i < nNumInstances; i++) { /* Allocate Memory for CC handles */ @@ -995,7 +1017,7 @@ index 7c472d7..216c7f5 100644 if(pCcHandle == NULL) return XRAN_STATUS_RESOURCE; -@@ -2347,8 +2371,7 @@ ring_processing_func_per_port(void* args) +@@ -2347,8 +2379,7 @@ ring_processing_func_per_port(void* args) for (i = 0; i < ctx->io_cfg.num_vfs && i < XRAN_VF_MAX; i = i+1) { if (ctx->vf2xran_port[i] == port_id) { for(qi = 0; qi < ctx->rxq_per_port[port_id]; qi++){ @@ -1005,7 +1027,7 @@ index 7c472d7..216c7f5 100644 } } } -@@ -2414,9 +2437,6 @@ xran_spawn_workers(void) +@@ -2414,9 +2445,6 @@ xran_spawn_workers(void) nWorkerCore = nWorkerCore << 1; } @@ -1015,7 +1037,7 @@ index 7c472d7..216c7f5 100644 printf("O-XU %d\n", eth_ctx->io_cfg.id); printf("HW %d\n", icx_cpu); printf("Num cores %d\n", total_num_cores); -@@ -2453,7 +2473,7 @@ xran_spawn_workers(void) +@@ -2453,7 +2481,7 @@ xran_spawn_workers(void) eth_ctx->time_wrk_cfg.arg = NULL; eth_ctx->time_wrk_cfg.state = 1; @@ -1024,7 +1046,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2475,7 +2495,7 @@ xran_spawn_workers(void) +@@ -2475,7 +2503,7 @@ xran_spawn_workers(void) /* workers */ /** 0 **/ @@ -1033,7 +1055,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2502,7 +2522,7 @@ xran_spawn_workers(void) +@@ -2502,7 +2530,7 @@ xran_spawn_workers(void) } /** 1 - CP GEN **/ @@ -1042,7 +1064,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2536,7 +2556,7 @@ xran_spawn_workers(void) +@@ -2536,7 +2564,7 @@ xran_spawn_workers(void) else p_dev->tx_sym_gen_func = xran_process_tx_sym_cp_on_opt; @@ -1051,7 +1073,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2559,7 +2579,7 @@ xran_spawn_workers(void) +@@ -2559,7 +2587,7 @@ xran_spawn_workers(void) /* workers */ /** 0 **/ @@ -1060,7 +1082,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2586,7 +2606,7 @@ xran_spawn_workers(void) +@@ -2586,7 +2614,7 @@ xran_spawn_workers(void) } /** 1 - CP GEN **/ @@ -1069,7 +1091,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2613,7 +2633,7 @@ xran_spawn_workers(void) +@@ -2613,7 +2641,7 @@ xran_spawn_workers(void) /* workers */ /** 0 **/ @@ -1078,7 +1100,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2628,7 +2648,7 @@ xran_spawn_workers(void) +@@ -2628,7 +2656,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 1 - CP GEN **/ @@ -1087,7 +1109,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2643,7 +2663,7 @@ xran_spawn_workers(void) +@@ -2643,7 +2671,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 2 UP GEN **/ @@ -1096,7 +1118,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2682,7 +2702,7 @@ xran_spawn_workers(void) +@@ -2682,7 +2710,7 @@ xran_spawn_workers(void) /* workers */ /** 0 **/ @@ -1105,7 +1127,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2697,7 +2717,7 @@ xran_spawn_workers(void) +@@ -2697,7 +2725,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 1 - CP GEN **/ @@ -1114,7 +1136,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2712,7 +2732,7 @@ xran_spawn_workers(void) +@@ -2712,7 +2740,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 2 UP GEN **/ @@ -1123,7 +1145,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2727,7 +2747,7 @@ xran_spawn_workers(void) +@@ -2727,7 +2755,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 3 UP GEN **/ @@ -1132,7 +1154,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2766,7 +2786,7 @@ xran_spawn_workers(void) +@@ -2766,7 +2794,7 @@ xran_spawn_workers(void) /* workers */ /** 0 **/ @@ -1141,7 +1163,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2781,7 +2801,7 @@ xran_spawn_workers(void) +@@ -2781,7 +2809,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 1 Eth Tx **/ @@ -1150,7 +1172,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); -@@ -2797,7 +2817,7 @@ xran_spawn_workers(void) +@@ -2797,7 +2825,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 2 - CP GEN **/ @@ -1159,7 +1181,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2812,7 +2832,7 @@ xran_spawn_workers(void) +@@ -2812,7 +2840,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 3 UP GEN **/ @@ -1168,7 +1190,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2827,7 +2847,7 @@ xran_spawn_workers(void) +@@ -2827,7 +2855,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 4 UP GEN **/ @@ -1177,7 +1199,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2861,7 +2881,7 @@ xran_spawn_workers(void) +@@ -2861,7 +2889,7 @@ xran_spawn_workers(void) /* workers */ /** 0 Eth RX */ @@ -1186,7 +1208,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2876,7 +2896,7 @@ xran_spawn_workers(void) +@@ -2876,7 +2904,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 1 FH RX and BBDEV */ @@ -1195,7 +1217,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2891,7 +2911,7 @@ xran_spawn_workers(void) +@@ -2891,7 +2919,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 2 FH RX and BBDEV */ @@ -1204,7 +1226,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2906,7 +2926,7 @@ xran_spawn_workers(void) +@@ -2906,7 +2934,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 3 FH RX and BBDEV */ @@ -1213,7 +1235,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2921,7 +2941,7 @@ xran_spawn_workers(void) +@@ -2921,7 +2949,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** FH TX and BBDEV */ @@ -1222,7 +1244,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -2969,7 +2989,7 @@ xran_spawn_workers(void) +@@ -2969,7 +2997,7 @@ xran_spawn_workers(void) /* p_dev->tx_sym_gen_func = xran_process_tx_sym_cp_on_opt; */ @@ -1231,7 +1253,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3004,7 +3024,7 @@ xran_spawn_workers(void) +@@ -3004,7 +3032,7 @@ xran_spawn_workers(void) /* workers */ /** 0 **/ @@ -1240,7 +1262,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3031,7 +3051,7 @@ xran_spawn_workers(void) +@@ -3031,7 +3059,7 @@ xran_spawn_workers(void) } /** 1 - CP GEN **/ @@ -1249,7 +1271,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3062,7 +3082,7 @@ xran_spawn_workers(void) +@@ -3062,7 +3090,7 @@ xran_spawn_workers(void) /* workers */ /** 0 **/ @@ -1258,7 +1280,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3089,7 +3109,7 @@ xran_spawn_workers(void) +@@ -3089,7 +3117,7 @@ xran_spawn_workers(void) } /** 1 - CP GEN **/ @@ -1267,7 +1289,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3115,7 +3135,7 @@ xran_spawn_workers(void) +@@ -3115,7 +3143,7 @@ xran_spawn_workers(void) /* workers */ /** 0 **/ @@ -1276,7 +1298,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3130,7 +3150,7 @@ xran_spawn_workers(void) +@@ -3130,7 +3158,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 1 - CP GEN **/ @@ -1285,7 +1307,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3145,7 +3165,7 @@ xran_spawn_workers(void) +@@ -3145,7 +3173,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 2 UP GEN **/ @@ -1294,7 +1316,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3184,7 +3204,7 @@ xran_spawn_workers(void) +@@ -3184,7 +3212,7 @@ xran_spawn_workers(void) /* workers */ /** 0 FH RX and BBDEV */ @@ -1303,7 +1325,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3199,7 +3219,7 @@ xran_spawn_workers(void) +@@ -3199,7 +3227,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 1 - CP GEN **/ @@ -1312,7 +1334,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3214,7 +3234,7 @@ xran_spawn_workers(void) +@@ -3214,7 +3242,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 2 UP GEN **/ @@ -1321,7 +1343,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3229,7 +3249,7 @@ xran_spawn_workers(void) +@@ -3229,7 +3257,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 3 UP GEN **/ @@ -1330,7 +1352,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3266,7 +3286,7 @@ xran_spawn_workers(void) +@@ -3266,7 +3294,7 @@ xran_spawn_workers(void) /* workers */ /** 0 **/ @@ -1339,7 +1361,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3281,7 +3301,7 @@ xran_spawn_workers(void) +@@ -3281,7 +3309,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 1 - CP GEN **/ @@ -1348,7 +1370,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3296,7 +3316,7 @@ xran_spawn_workers(void) +@@ -3296,7 +3324,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 2 UP GEN **/ @@ -1357,7 +1379,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3311,7 +3331,7 @@ xran_spawn_workers(void) +@@ -3311,7 +3339,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 3 UP GEN **/ @@ -1366,7 +1388,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3326,7 +3346,7 @@ xran_spawn_workers(void) +@@ -3326,7 +3354,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 4 UP GEN **/ @@ -1375,7 +1397,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3348,7 +3368,7 @@ xran_spawn_workers(void) +@@ -3348,7 +3376,7 @@ xran_spawn_workers(void) /* workers */ /** 0 Eth RX */ @@ -1384,7 +1406,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3363,7 +3383,7 @@ xran_spawn_workers(void) +@@ -3363,7 +3391,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 1 FH RX and BBDEV */ @@ -1393,7 +1415,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3378,7 +3398,7 @@ xran_spawn_workers(void) +@@ -3378,7 +3406,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 2 FH RX and BBDEV */ @@ -1402,7 +1424,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3393,7 +3413,7 @@ xran_spawn_workers(void) +@@ -3393,7 +3421,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 3 FH RX and BBDEV */ @@ -1411,7 +1433,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3408,7 +3428,7 @@ xran_spawn_workers(void) +@@ -3408,7 +3436,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** FH TX and BBDEV */ @@ -1420,7 +1442,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3435,7 +3455,7 @@ xran_spawn_workers(void) +@@ -3435,7 +3463,7 @@ xran_spawn_workers(void) /* workers */ /** 0 Eth RX */ @@ -1429,7 +1451,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3450,7 +3470,7 @@ xran_spawn_workers(void) +@@ -3450,7 +3478,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 1 FH RX and BBDEV */ @@ -1438,7 +1460,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3465,7 +3485,7 @@ xran_spawn_workers(void) +@@ -3465,7 +3493,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 2 FH RX and BBDEV */ @@ -1447,7 +1469,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3480,7 +3500,7 @@ xran_spawn_workers(void) +@@ -3480,7 +3508,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 3 FH RX and BBDEV */ @@ -1456,7 +1478,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3495,7 +3515,7 @@ xran_spawn_workers(void) +@@ -3495,7 +3523,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 4 FH RX and BBDEV */ @@ -1465,7 +1487,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3510,7 +3530,7 @@ xran_spawn_workers(void) +@@ -3510,7 +3538,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** FH TX and BBDEV */ @@ -1474,7 +1496,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3537,7 +3557,7 @@ xran_spawn_workers(void) +@@ -3537,7 +3565,7 @@ xran_spawn_workers(void) /* workers */ /** 0 **/ @@ -1483,7 +1505,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3562,7 +3582,7 @@ xran_spawn_workers(void) +@@ -3562,7 +3590,7 @@ xran_spawn_workers(void) } /** 1 - CP GEN **/ @@ -1492,7 +1514,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3577,7 +3597,7 @@ xran_spawn_workers(void) +@@ -3577,7 +3605,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 2 UP GEN **/ @@ -1501,7 +1523,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3602,7 +3622,7 @@ xran_spawn_workers(void) +@@ -3602,7 +3630,7 @@ xran_spawn_workers(void) } /** 3 UP GEN **/ @@ -1510,7 +1532,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3627,7 +3647,7 @@ xran_spawn_workers(void) +@@ -3627,7 +3655,7 @@ xran_spawn_workers(void) } /** 4 UP GEN **/ @@ -1519,7 +1541,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -3642,7 +3662,7 @@ xran_spawn_workers(void) +@@ -3642,7 +3670,7 @@ xran_spawn_workers(void) eth_ctx->pkt_wrk_cfg[pThCtx->worker_id].arg = pThCtx; /** 5 UP GEN **/ @@ -1528,7 +1550,7 @@ index 7c472d7..216c7f5 100644 if(pThCtx == NULL){ print_err("pThCtx allocation error\n"); return XRAN_STATUS_FAIL; -@@ -4034,6 +4054,24 @@ xran_get_slot_idx (uint32_t PortId, uint32_t *nFrameIdx, uint32_t *nSubframeIdx, +@@ -4034,6 +4062,24 @@ xran_get_slot_idx (uint32_t PortId, uint32_t *nFrameIdx, uint32_t *nSubframeIdx, return tti; } diff --git a/radio/fhi_72/CMakeLists.txt b/radio/fhi_72/CMakeLists.txt index f2161d06a8..fb40852ebb 100644 --- a/radio/fhi_72/CMakeLists.txt +++ b/radio/fhi_72/CMakeLists.txt @@ -17,7 +17,7 @@ add_library(oran_fhlib_5g MODULE ) set(E_VERSION 5.1.6) -set(F_VERSION 6.1.4) +set(F_VERSION 6.1.5) find_package(xran REQUIRED) if(xran_VERSION VERSION_EQUAL E_VERSION) From 2d27e0fc0aed35bd5dd0651bab7533b901d8180e Mon Sep 17 00:00:00 2001 From: Mario Joa-Ng Date: Fri, 18 Jul 2025 20:00:09 -0400 Subject: [PATCH 015/275] Set TX nBeamIndex according to beam allocation Initialize ru_info structure with beam information, and set beams on TX direction. Initialize the number of antennas for both RX/TX in both cases for completeness. --- radio/fhi_72/oaioran.c | 8 ++++++++ radio/fhi_72/oran_isolate.c | 7 +++++++ radio/fhi_72/oran_isolate.h | 8 ++++++++ 3 files changed, 23 insertions(+) diff --git a/radio/fhi_72/oaioran.c b/radio/fhi_72/oaioran.c index fc9f58a492..d6a31f3bc1 100644 --- a/radio/fhi_72/oaioran.c +++ b/radio/fhi_72/oaioran.c @@ -512,6 +512,14 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp) for (idxElm = 0; idxElm < pRbMap->nPrbElm; idxElm++) { struct xran_section_desc *p_sec_desc = NULL; p_prbMapElm = &pRbMap->prbMap[idxElm]; + if (sym_idx == 0) { + // ant_id / no of antenna per beam gives the beam_nb + p_prbMapElm->nBeamIndex = ru->beam_id[ant_id / (ru->nb_tx / ru->num_beams_period)][slot * XRAN_NUM_OF_SYMBOL_PER_SLOT]; + // In phy-f-1.0/fhi_lib/lib/api/xran_pkt_cp.h, beamId:15 is of 15bit. -1 set extension bit ef:1 to 1 mistakenly. + if (p_prbMapElm->nBeamIndex == -1) + p_prbMapElm->nBeamIndex = 0; + } + // assumes one fragment per symbol #ifdef E_RELEASE p_sec_desc = p_prbMapElm->p_sec_desc[sym_id][0]; diff --git a/radio/fhi_72/oran_isolate.c b/radio/fhi_72/oran_isolate.c index 3bdbe1df12..48e95e7344 100644 --- a/radio/fhi_72/oran_isolate.c +++ b/radio/fhi_72/oran_isolate.c @@ -214,7 +214,10 @@ void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot) { ru_info_t ru_info; ru_info.nb_rx = ru->nb_rx * ru->num_beams_period; + ru_info.nb_tx = ru->nb_tx * ru->num_beams_period; ru_info.rxdataF = ru->common.rxdataF; + ru_info.beam_id = ru->common.beam_id; + ru_info.num_beams_period = ru->num_beams_period; ru_info.prach_buf = ru->prach_rxsigF[0]; // index: [prach_oca][ant_id] RU_proc_t *proc = &ru->proc; @@ -267,8 +270,12 @@ void oran_fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { start_meas(&ru->tx_fhaul); ru_info_t ru_info; + ru_info.nb_rx = ru->nb_rx * ru->num_beams_period; ru_info.nb_tx = ru->nb_tx * ru->num_beams_period; ru_info.txdataF_BF = ru->common.txdataF_BF; + ru_info.beam_id = ru->common.beam_id; + ru_info.num_beams_period = ru->num_beams_period; + // printf("south_out:\tframe=%d\tslot=%d\ttimestamp=%ld\n",frame,slot,timestamp); int ret = xran_fh_tx_send_slot(&ru_info, frame, slot, timestamp); diff --git a/radio/fhi_72/oran_isolate.h b/radio/fhi_72/oran_isolate.h index 174c1399a6..7dd61ad739 100644 --- a/radio/fhi_72/oran_isolate.h +++ b/radio/fhi_72/oran_isolate.h @@ -41,6 +41,14 @@ typedef struct ru_info_s { int nb_tx; int32_t **txdataF_BF; + /// \brief Anaglogue beam ID for each OFDM symbol (used when beamforming not done in RU) + /// - first index: concurrent beam + /// - second index: beam_id [0.. symbols_per_frame] + int32_t **beam_id; + + /// number of concurrent analog beams in period + int num_beams_period; + // Needed for Prach int16_t **prach_buf; } ru_info_t; From 65ef3cfa7471db0ecf80d9f534532a14a9e9499d Mon Sep 17 00:00:00 2001 From: Mario Joa-Ng Date: Wed, 13 Aug 2025 16:23:41 +0200 Subject: [PATCH 016/275] Set RX nBeamIndex according to beam allocation Handle CP UL packet at xran_fx_tx_send_slot() instead of xran_fh_rx_read_slot(), as the latter would set this information too late (on reception on samples, but it needs to be filled in the CP packet prior to reception). --- radio/fhi_72/oaioran.c | 69 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/radio/fhi_72/oaioran.c b/radio/fhi_72/oaioran.c index d6a31f3bc1..37661d7ad1 100644 --- a/radio/fhi_72/oaioran.c +++ b/radio/fhi_72/oaioran.c @@ -259,7 +259,14 @@ static bool is_tdd_ul_symbol(const struct xran_frame_config *frame_conf, int slo * slot is not UL). */ static bool is_tdd_dl_guard_slot(const struct xran_frame_config *frame_conf, int slot) { - return !is_tdd_ul_symbol(frame_conf, slot, XRAN_NUM_OF_SYMBOL_PER_SLOT - 1); + return !is_tdd_ul_symbol(frame_conf, slot, 0); +} + +/** @brief Check if current slot is UL or guard/mixed without UL (i.e., current + * slot is not UL). */ +static bool is_tdd_ul_guard_slot(const struct xran_frame_config *frame_conf, int slot) +{ + return is_tdd_ul_symbol(frame_conf, slot, XRAN_NUM_OF_SYMBOL_PER_SLOT - 1); } /** @details Read PRACH and PUSCH data from xran buffers. If @@ -337,7 +344,7 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot) start_ptr = rx_data + (slot_size * slot_offset_rxdata); const struct xran_frame_config *frame_conf = &get_xran_fh_config(ant_id / nb_rx_per_ru)->frame_conf; // skip processing this slot is TX (no RX in this slot) - if (is_tdd_dl_guard_slot(frame_conf, *slot)) + if (!is_tdd_ul_guard_slot(frame_conf, *slot)) continue; // This loop would better be more inner to avoid confusion and maybe also errors. for (int32_t sym_idx = 0; sym_idx < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_idx++) { @@ -484,12 +491,70 @@ int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp) int nPRBs = fh_cfg->nDLRBs; int fftsize = 1 << fh_cfg->ru_conf.fftSize; int nb_tx_per_ru = ru->nb_tx / fh_init->xran_ports; + int nb_rx_per_ru = ru->nb_rx / fh_init->xran_ports; + + // Handle CP UL packet here instead of at xran_fh_rx_read_slot() as oran_fh_if4p5_south_in() lags behind + // oran_fh_if4p5_south_out() (which is invoked at the right time slot) by 4 slots. + // Need to use --continuous-tx so that this routine will be triggered in RX slot. + for (uint16_t cc_id = 0; cc_id < 1 /*nSectorNum*/; cc_id++) { // OAI does not support multiple CC yet. + for (uint8_t ant_id = 0; ant_id < ru->nb_rx; ant_id++) { + int first = 1; // The first UL symbol + const struct xran_frame_config *frame_conf = &get_xran_fh_config(ant_id / nb_rx_per_ru)->frame_conf; + // skip processing this slot is TX (no RX in this slot) + if (!is_tdd_ul_guard_slot(frame_conf, slot)) { + continue; + } + // This loop would better be more inner to avoid confusion and maybe also errors. + for (int32_t sym_idx = 0; sym_idx < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_idx++) { + /* the callback is for mixed and UL slots. In mixed, we have to + * skip DL and guard symbols. */ + if (!is_tdd_ul_symbol(frame_conf, slot, sym_idx)) { + continue; + } + oran_buf_list_t *bufs = get_xran_buffers(ant_id / nb_rx_per_ru); + uint8_t *pPrbMapData = bufs->dstcp[ant_id % nb_rx_per_ru][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData; + struct xran_prb_map *pPrbMap = (struct xran_prb_map *)pPrbMapData; + + struct xran_prb_elm *pRbElm = &pPrbMap->prbMap[0]; + + struct xran_prb_map *pRbMap = pPrbMap; + uint32_t idxElm = 0; + + LOG_D(HW, "pRbMap->nPrbElm %d\n", pRbMap->nPrbElm); + for (idxElm = 0; idxElm < pRbMap->nPrbElm; idxElm++) { + LOG_D(HW, "prbMap[%d] : PRBstart %d nPRBs %d\n", idxElm, pRbMap->prbMap[idxElm].nRBStart, pRbMap->prbMap[idxElm].nRBSize); + pRbElm = &pRbMap->prbMap[idxElm]; + if (first) { + // ant_id / no of antenna per beam gives the beam_nb + pRbElm->nBeamIndex = + ru->beam_id[ant_id / (ru->nb_rx / ru->num_beams_period)][slot * XRAN_NUM_OF_SYMBOL_PER_SLOT + sym_idx]; + // In phy-f-1.0/fhi_lib/lib/api/xran_pkt_cp.h, beamId:15 is of 15bit. -1 set extension bit ef:1 to 1 mistakenly. + if (pRbElm->nBeamIndex == -1) { + pRbElm->nBeamIndex = 0; + } else { + first = 0; + } + } + } + } + } + } for (uint16_t cc_id = 0; cc_id < 1 /*nSectorNum*/; cc_id++) { // OAI does not support multiple CC yet. for (uint8_t ant_id = 0; ant_id < ru->nb_tx; ant_id++) { oran_buf_list_t *bufs = get_xran_buffers(ant_id / nb_tx_per_ru); + const struct xran_frame_config *frame_conf = &get_xran_fh_config(ant_id / nb_tx_per_ru)->frame_conf; + // skip processing this slot is TX (no TX in this slot) + if (!is_tdd_dl_guard_slot(frame_conf, slot)) { + continue; + } // This loop would better be more inner to avoid confusion and maybe also errors. for (int32_t sym_idx = 0; sym_idx < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_idx++) { + /* the callback is for mixed and UL slots. In mixed, we have to + * skip UL and guard symbols. */ + if (is_tdd_ul_symbol(frame_conf, slot, sym_idx)) { + continue; + } uint8_t *pData = bufs->src[ant_id % nb_tx_per_ru][tti % XRAN_N_FE_BUF_LEN].pBuffers[sym_idx % XRAN_NUM_OF_SYMBOL_PER_SLOT].pData; uint8_t *pPrbMapData = bufs->srccp[ant_id % nb_tx_per_ru][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData; From ad1b05cd016647d92a48d35590e49886474f759b Mon Sep 17 00:00:00 2001 From: Mario Joa-Ng Date: Wed, 13 Aug 2025 15:56:02 +0200 Subject: [PATCH 017/275] need to set beam_id for UPLINK_SLOT when using analog BF --- executables/nr-gnb.c | 5 ++++- openair1/SCHED_NR/nr_ru_procedures.c | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 1021aba1ce..4fb74c1a08 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -114,7 +114,10 @@ static void tx_func(processingData_L1tx_t *info) pushNotifiedFIFO(&gNB->resp_L1, res); int tx_slot_type = nr_slot_select(cfg, frame_tx, slot_tx); - if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx || IS_SOFTMODEM_RFSIM) { + // TODO check for analog_beam_list is a workaround while no beam API for beam + // selection is implemented + if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx || IS_SOFTMODEM_RFSIM + || cfg->analog_beamforming_ve.analog_beam_list) { start_meas(&info->gNB->phy_proc_tx); phy_procedures_gNB_TX(info, frame_tx, slot_tx, 1); diff --git a/openair1/SCHED_NR/nr_ru_procedures.c b/openair1/SCHED_NR/nr_ru_procedures.c index 0546455e36..f121914f11 100644 --- a/openair1/SCHED_NR/nr_ru_procedures.c +++ b/openair1/SCHED_NR/nr_ru_procedures.c @@ -185,9 +185,6 @@ void nr_feptx_prec(RU_t *ru, int frame_tx, int slot_tx) int txdataF_offset = slot_tx * fp->samples_per_slot_wCP; start_meas(&ru->precoding_stats); - if (nr_slot_select(cfg,frame_tx,slot_tx) == NR_UPLINK_SLOT) - return; - if (gNB->common_vars.analog_bf) { for (int i = 0; i < ru->num_beams_period; i++) { memcpy((void*) &ru->common.beam_id[i][slot_tx * fp->symbols_per_slot], @@ -196,6 +193,9 @@ void nr_feptx_prec(RU_t *ru, int frame_tx, int slot_tx) } } + if (nr_slot_select(cfg,frame_tx,slot_tx) == NR_UPLINK_SLOT) + return; + // If there is no digital beamforming we just need to copy the data to RU if (ru->config.dbt_config.num_dig_beams == 0 || ru->gNB_list[0]->common_vars.analog_bf) { for (int b = 0; b < ru->num_beams_period; b++) { From ae690217dd51a37b76fc0ee9e2433a0324c6b211 Mon Sep 17 00:00:00 2001 From: Sakthivel Velumani Date: Wed, 9 Apr 2025 11:05:54 -0400 Subject: [PATCH 018/275] Clean-up nr_slot_fep 1.Create new funtion nr_symbol_fep() to do OFDM demod of one symbol on all antennas. This is called by nr_slot_fep() for now. In the upcoming MR, this function will be called directly after reading each OFDM symbol from the radio. 2.Removed redundant memory alignment. DFT function performs memory alignment if the input memory is not aligned. --- executables/nr-gnb.c | 16 +- openair1/PHY/MODULATION/nr_modulation.h | 14 +- openair1/PHY/MODULATION/slot_fep_nr.c | 193 +++++++++++++----------- openair1/SIMULATION/NR_PHY/ulsim.c | 16 +- 4 files changed, 123 insertions(+), 116 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 505adf825c..a63c7984fb 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -193,14 +193,14 @@ static void rx_func(processingData_L1_t *info) int soffset = (slot_rx & 3) * gNB->frame_parms.symbols_per_slot * gNB->frame_parms.ofdm_symbol_size; for (int bb = 0; bb < gNB->common_vars.num_beams_period; bb++) { for (int aa = 0; aa < gNB->frame_parms.nb_antennas_rx; aa++) { - apply_nr_rotation_RX(&gNB->frame_parms, - gNB->common_vars.rxdataF[bb][aa], - gNB->frame_parms.symbol_rotation[1], - slot_rx, - gNB->frame_parms.N_RB_UL, - soffset, - 0, - gNB->frame_parms.Ncp == EXTENDED ? 12 : 14); + const uint max_symb = (gNB->frame_parms.Ncp == EXTENDED) ? 12 : 14; + for (int sym = 0; sym < max_symb; sym++) + apply_nr_rotation_symbol_RX(&gNB->frame_parms, + gNB->common_vars.rxdataF[bb][aa] + soffset + sym * gNB->frame_parms.ofdm_symbol_size, + gNB->frame_parms.symbol_rotation[1], + gNB->frame_parms.N_RB_UL, + slot_rx, + sym); } } } diff --git a/openair1/PHY/MODULATION/nr_modulation.h b/openair1/PHY/MODULATION/nr_modulation.h index 64886a2809..996a66b343 100644 --- a/openair1/PHY/MODULATION/nr_modulation.h +++ b/openair1/PHY/MODULATION/nr_modulation.h @@ -117,14 +117,12 @@ void init_symbol_rotation(NR_DL_FRAME_PARMS *fp); void init_timeshift_rotation(NR_DL_FRAME_PARMS *fp); -void apply_nr_rotation_RX(const NR_DL_FRAME_PARMS *frame_parms, - c16_t *rxdataF, - const c16_t *rot, - int slot, - int nb_rb, - int soffset, - int first_symbol, - int nsymb); +void apply_nr_rotation_symbol_RX(const NR_DL_FRAME_PARMS *frame_parms, + c16_t *rxdataF, + const c16_t *rot, + int nb_rb, + int slot, + int symbol); /*! \brief Perform NR precoding. TS 38.211 V15.4.0 subclause 6.3.1.5 @param[in] datatx_F_precoding, Pointer to n_layers*re data array diff --git a/openair1/PHY/MODULATION/slot_fep_nr.c b/openair1/PHY/MODULATION/slot_fep_nr.c index cad3f92c33..50e19d8563 100644 --- a/openair1/PHY/MODULATION/slot_fep_nr.c +++ b/openair1/PHY/MODULATION/slot_fep_nr.c @@ -28,6 +28,36 @@ #include "PHY/nr_phy_common/inc/nr_phy_common.h" #include +/* rxdataF should be 16 bytes aligned */ +void nr_symbol_fep(const NR_DL_FRAME_PARMS *frame_parms, + const int slot, + const unsigned char symbol, + const int link_type, + c16_t *rxdata[frame_parms->nb_antennas_rx], + c16_t *rxdataF[frame_parms->nb_antennas_rx], + time_stats_t* dft_stats) +{ + AssertFatal(symbol < frame_parms->symbols_per_slot, + "slot_fep: symbol must be between 0 and %d\n", + frame_parms->symbols_per_slot - 1); + AssertFatal(slot < frame_parms->slots_per_frame, "slot_fep: Ns must be between 0 and %d\n", frame_parms->slots_per_frame - 1); + + dft_size_idx_t dftsize = get_dft(frame_parms->ofdm_symbol_size); + for (unsigned char aa = 0; aa < frame_parms->nb_antennas_rx; aa++) { + if (dft_stats) start_meas(dft_stats); + dft(dftsize, (int16_t *)rxdata[aa], (int16_t *)rxdataF[aa], 1); + if (dft_stats) stop_meas(dft_stats); + + const bool is_sl = (link_type == link_type_sl); + apply_nr_rotation_symbol_RX(frame_parms, + rxdataF[aa], + frame_parms->symbol_rotation[link_type], + is_sl ? frame_parms->N_RB_SL : frame_parms->N_RB_DL, + slot, + symbol); + } +} + int nr_slot_fep(PHY_VARS_NR_UE *ue, const NR_DL_FRAME_PARMS *frame_parms, unsigned int slot, @@ -37,35 +67,23 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue, uint32_t sample_offset, c16_t **rxdata) { - AssertFatal(symbol < frame_parms->symbols_per_slot, "slot_fep: symbol must be between 0 and %d\n", frame_parms->symbols_per_slot-1); + AssertFatal(symbol < frame_parms->symbols_per_slot, + "slot_fep: symbol must be between 0 and %d\n", + frame_parms->symbols_per_slot - 1); AssertFatal(slot < frame_parms->slots_per_frame, "slot_fep: Ns must be between 0 and %d\n", frame_parms->slots_per_frame - 1); - bool is_sl = (linktype == link_type_sl) ? true : false; + bool is_sl = (linktype == link_type_sl); bool is_synchronized = (ue) ? ue->is_synchronized : false; - unsigned int nb_prefix_samples; - unsigned int nb_prefix_samples0; + unsigned int nb_prefix_samples = frame_parms->nb_prefix_samples; + unsigned int nb_prefix_samples0 = (is_synchronized || is_sl) ? frame_parms->nb_prefix_samples0 : nb_prefix_samples; + // For Sidelink 16 frames worth of samples is processed to find SSB, for 5G-NR 2. - unsigned int total_samples = (is_sl) ? 16 * frame_parms->samples_per_frame : 2 * frame_parms->samples_per_frame; - - int N_RB = (is_sl) ? frame_parms->N_RB_SL : frame_parms->N_RB_DL; - - if (is_synchronized || is_sl) { - nb_prefix_samples = frame_parms->nb_prefix_samples; - nb_prefix_samples0 = frame_parms->nb_prefix_samples0; - } else { - nb_prefix_samples = frame_parms->nb_prefix_samples; - nb_prefix_samples0 = frame_parms->nb_prefix_samples; - AssertFatal(slot == 0, "Slot should be 0\n"); - } - - dft_size_idx_t dftsize = get_dft(frame_parms->ofdm_symbol_size); - // This is for misalignment issues - c16_t tmp_dft_in[frame_parms->ofdm_symbol_size] __attribute__ ((aligned (32))); + const unsigned int total_samples = (is_sl) ? 16 * frame_parms->samples_per_frame : 2 * frame_parms->samples_per_frame; unsigned int rx_offset = frame_parms->get_samples_slot_timestamp(slot, frame_parms, 0); - unsigned int abs_symbol = slot * frame_parms->symbols_per_slot + symbol; + const unsigned int abs_symbol = slot * frame_parms->symbols_per_slot + symbol; for (int idx_symb = slot * frame_parms->symbols_per_slot; idx_symb <= abs_symbol; idx_symb++) - rx_offset += (idx_symb%(0x7<numerology_index)) ? nb_prefix_samples : nb_prefix_samples0; + rx_offset += (idx_symb % (0x7 << frame_parms->numerology_index)) ? nb_prefix_samples : nb_prefix_samples0; rx_offset += frame_parms->ofdm_symbol_size * symbol; rx_offset += sample_offset; @@ -73,48 +91,47 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue, // use OFDM symbol from within 1/8th of the CP to avoid ISI rx_offset -= (nb_prefix_samples / frame_parms->ofdm_offset_divisor); - rx_offset %= total_samples; - - for (unsigned char aa=0; aanb_antennas_rx; aa++) { - c16_t *rxdata_ptr = &rxdata[aa][rx_offset]; + LOG_D(PHY, + "slot_fep: slot %d, symbol %d, nb_prefix_samples %u, nb_prefix_samples0 %u, rx_offset %u energy %d\n", + slot, + symbol, + nb_prefix_samples, + nb_prefix_samples0, + rx_offset, + dB_fixed(signal_energy((int32_t *)&rxdata[0][rx_offset], frame_parms->ofdm_symbol_size))); + c16_t tmp_dft_in[frame_parms->nb_antennas_rx][frame_parms->ofdm_symbol_size] __attribute__((aligned(32))); + c16_t *rxdata_symb_ptr[frame_parms->nb_antennas_rx]; + c16_t *rxdataF_symb_ptr[frame_parms->nb_antennas_rx]; + for (unsigned char aa = 0; aa < frame_parms->nb_antennas_rx; aa++) { + rxdataF_symb_ptr[aa] = &rxdataF[aa][frame_parms->ofdm_symbol_size * symbol]; // This happens only during initial sync if (rx_offset + frame_parms->ofdm_symbol_size > total_samples) { - // rxdata is 2 frames len // we have to wrap on the end - - memcpy((void *)&tmp_dft_in[0], (void *)&rxdata[aa][rx_offset], (total_samples - rx_offset) * sizeof(int32_t)); - memcpy((void *)&tmp_dft_in[total_samples - rx_offset], - (void *)&rxdata[aa][0], + memcpy(&tmp_dft_in[aa][0], &rxdata[aa][rx_offset], (total_samples - rx_offset) * sizeof(int32_t)); + memcpy(&tmp_dft_in[aa][total_samples - rx_offset], + &rxdata[aa][0], (frame_parms->ofdm_symbol_size - (total_samples - rx_offset)) * sizeof(int32_t)); - rxdata_ptr = tmp_dft_in; - - } else if ((rx_offset & 7) != 0) { // if input to dft is not 256-bit aligned - memcpy((void *)&tmp_dft_in[0], (void *)&rxdata[aa][rx_offset], frame_parms->ofdm_symbol_size * sizeof(int32_t)); - rxdata_ptr = tmp_dft_in; + rxdata_symb_ptr[aa] = tmp_dft_in[aa]; + } else { + rxdata_symb_ptr[aa] = &rxdata[aa][rx_offset]; } if (ue && ue->cont_fo_comp) { start_meas_nr_ue_phy(ue, RX_FO_COMPENSATION_STATS); - nr_fo_compensation(ue->freq_offset, frame_parms->samples_per_subframe, rx_offset, rxdata_ptr, tmp_dft_in, frame_parms->ofdm_symbol_size); - rxdata_ptr = tmp_dft_in; + nr_fo_compensation(ue->freq_offset, + frame_parms->samples_per_subframe, + rx_offset, + rxdata_symb_ptr[aa], + tmp_dft_in[aa], + frame_parms->ofdm_symbol_size); stop_meas_nr_ue_phy(ue, RX_FO_COMPENSATION_STATS); + rxdata_symb_ptr[aa] = tmp_dft_in[aa]; } - - if (ue) - start_meas_nr_ue_phy(ue, RX_DFT_STATS); - - dft(dftsize, - (int16_t *)rxdata_ptr, - (int16_t *)&rxdataF[aa][frame_parms->ofdm_symbol_size*symbol], - 1); - - if (ue) - stop_meas_nr_ue_phy(ue, RX_DFT_STATS); - - apply_nr_rotation_RX(frame_parms, rxdataF[aa], frame_parms->symbol_rotation[linktype], slot, N_RB, 0, symbol, 1); } - + time_stats_t* dft_stats = NULL; + if (ue) dft_stats = &ue->phy_cpu_stats.cpu_time_stats[RX_DFT_STATS]; + nr_symbol_fep(frame_parms, slot, symbol, linktype, rxdata_symb_ptr, rxdataF_symb_ptr, dft_stats); return 0; } @@ -177,54 +194,46 @@ int nr_slot_fep_ul(NR_DL_FRAME_PARMS *frame_parms, return 0; } -void apply_nr_rotation_RX(const NR_DL_FRAME_PARMS *frame_parms, - c16_t *rxdataF, - const c16_t *rot, - int slot, - int nb_rb, - int soffset, - int first_symbol, - int nsymb) +void apply_nr_rotation_symbol_RX(const NR_DL_FRAME_PARMS *frame_parms, + c16_t *rxdataF, + const c16_t *rot, + int nb_rb, + int slot, + int symbol) { - AssertFatal(first_symbol + nsymb <= NR_NUMBER_OF_SYMBOLS_PER_SLOT, - "First symbol %d and number of symbol %d not compatible with number of symbols in a slot %d\n", - first_symbol, nsymb, NR_NUMBER_OF_SYMBOLS_PER_SLOT); - int symb_offset = (slot % frame_parms->slots_per_subframe) * frame_parms->symbols_per_slot; + const int symb_offset = (slot % frame_parms->slots_per_subframe) * frame_parms->symbols_per_slot; - for (int symbol = first_symbol; symbol < first_symbol + nsymb; symbol++) { - - c16_t rot2 = rot[symbol + symb_offset]; - rot2.i = -rot2.i; - LOG_D(PHY,"slot %d, symb_offset %d rotating by %d.%d\n", slot, symb_offset, rot2.r, rot2.i); - const c16_t *shift_rot = frame_parms->timeshift_symbol_rotation; - c16_t *this_symbol = &rxdataF[soffset + (frame_parms->ofdm_symbol_size * symbol)]; + c16_t rot2 = rot[symbol + symb_offset]; + rot2.i = -rot2.i; + LOG_D(PHY,"slot %d, symb_offset %d rotating by %d.%d\n", slot, symb_offset, rot2.r, rot2.i); + const c16_t *shift_rot = frame_parms->timeshift_symbol_rotation; + c16_t *this_symbol = rxdataF; - if (nb_rb & 1) { - rotate_cpx_vector(this_symbol, &rot2, this_symbol, - (nb_rb + 1) * 6, 15); - rotate_cpx_vector(this_symbol + frame_parms->first_carrier_offset - 6, - &rot2, - this_symbol + frame_parms->first_carrier_offset - 6, - (nb_rb + 1) * 6, 15); - mult_cpx_vector(this_symbol, shift_rot, this_symbol, (nb_rb + 1) * 6, 15); - mult_cpx_vector(this_symbol + frame_parms->first_carrier_offset - 6, - shift_rot + frame_parms->first_carrier_offset - 6, + if (nb_rb & 1) { + rotate_cpx_vector(this_symbol, &rot2, this_symbol, (nb_rb + 1) * 6, 15); + rotate_cpx_vector(this_symbol + frame_parms->first_carrier_offset - 6, + &rot2, this_symbol + frame_parms->first_carrier_offset - 6, (nb_rb + 1) * 6, 15); - } else { - rotate_cpx_vector(this_symbol, &rot2, this_symbol, - nb_rb * 6, 15); - rotate_cpx_vector(this_symbol + frame_parms->first_carrier_offset, - &rot2, - this_symbol + frame_parms->first_carrier_offset, - nb_rb * 6, 15); - mult_cpx_vector(this_symbol, shift_rot, this_symbol, nb_rb * 6, 15); - mult_cpx_vector(this_symbol + frame_parms->first_carrier_offset, - shift_rot + frame_parms->first_carrier_offset, + mult_cpx_vector(this_symbol, shift_rot, this_symbol, (nb_rb + 1) * 6, 15); + mult_cpx_vector(this_symbol + frame_parms->first_carrier_offset - 6, + shift_rot + frame_parms->first_carrier_offset - 6, + this_symbol + frame_parms->first_carrier_offset - 6, + (nb_rb + 1) * 6, + 15); + } else { + rotate_cpx_vector(this_symbol, &rot2, this_symbol, nb_rb * 6, 15); + rotate_cpx_vector(this_symbol + frame_parms->first_carrier_offset, + &rot2, this_symbol + frame_parms->first_carrier_offset, nb_rb * 6, 15); - } + mult_cpx_vector(this_symbol, shift_rot, this_symbol, nb_rb * 6, 15); + mult_cpx_vector(this_symbol + frame_parms->first_carrier_offset, + shift_rot + frame_parms->first_carrier_offset, + this_symbol + frame_parms->first_carrier_offset, + nb_rb * 6, + 15); } } diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index c4b2f854c2..b7f93cdf3a 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1468,14 +1468,14 @@ int main(int argc, char *argv[]) } int offset = (slot & 3) * gNB->frame_parms.symbols_per_slot * gNB->frame_parms.ofdm_symbol_size; for (int aa = 0; aa < gNB->frame_parms.nb_antennas_rx; aa++) { - apply_nr_rotation_RX(&gNB->frame_parms, - gNB->common_vars.rxdataF[0][aa], - gNB->frame_parms.symbol_rotation[1], - slot, - gNB->frame_parms.N_RB_UL, - offset, - 0, - gNB->frame_parms.Ncp == EXTENDED ? 12 : 14); + const unsigned int max_symb = (gNB->frame_parms.Ncp == EXTENDED) ? 12 : 14; + for (int sym = 0; sym < max_symb; sym++) + apply_nr_rotation_symbol_RX(&gNB->frame_parms, + gNB->common_vars.rxdataF[0][aa] + offset + sym * gNB->frame_parms.ofdm_symbol_size, + gNB->frame_parms.symbol_rotation[1], + gNB->frame_parms.N_RB_UL, + slot, + sym); } ul_proc_error = phy_procedures_gNB_uespec_RX(gNB, frame, slot, &UL_INFO); From e1b06d177e2082046355f58d534dba71e881eb2a Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Wed, 20 Aug 2025 16:25:05 +0200 Subject: [PATCH 019/275] Remove unused parameters from nr_pdcch_dmrs_rx --- openair1/PHY/NR_REFSIG/nr_dmrs_rx.c | 4 +--- openair1/PHY/NR_REFSIG/refsig_defs_ue.h | 4 +--- openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c index 7fd703ea30..e25d9700da 100644 --- a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c +++ b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c @@ -150,9 +150,7 @@ int nr_pdsch_dmrs_rx(const PHY_VARS_NR_UE *ue, return(0); } -int nr_pdcch_dmrs_rx(const PHY_VARS_NR_UE *ue, - unsigned int Ns, - const unsigned int *nr_gold_pdcch, +int nr_pdcch_dmrs_rx(const unsigned int *nr_gold_pdcch, c16_t *output, unsigned short p, unsigned short nb_rb_coreset) diff --git a/openair1/PHY/NR_REFSIG/refsig_defs_ue.h b/openair1/PHY/NR_REFSIG/refsig_defs_ue.h index 1d2d4b87ae..414dae9976 100644 --- a/openair1/PHY/NR_REFSIG/refsig_defs_ue.h +++ b/openair1/PHY/NR_REFSIG/refsig_defs_ue.h @@ -35,9 +35,7 @@ void nr_pbch_dmrs_rx(int dmrss, const unsigned int *nr_gold_pbch, c16_t *output, /*!\brief This function generates the NR Gold sequence (38-211, Sec 5.2.1) for the PDCCH DMRS. @param PHY_VARS_NR_UE* ue structure provides configuration, frame parameters and the pointers to the 32 bits sequence storage tables */ -int nr_pdcch_dmrs_rx(const PHY_VARS_NR_UE *ue, - unsigned int Ns, - const unsigned int *nr_gold_pdcch, +int nr_pdcch_dmrs_rx(const unsigned int *nr_gold_pdcch, c16_t *output, unsigned short p, unsigned short nb_rb_corset); diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index 296606a426..e77fe87069 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -852,7 +852,7 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, c16_t pilot[(nb_rb_coreset + dmrs_ref) * 3] __attribute__((aligned(16))); // Note: pilot returned by the following function is already the complex conjugate of the transmitted DMRS const uint32_t *gold = nr_gold_pdcch(ue->frame_parms.N_RB_DL, ue->frame_parms.symbols_per_slot, scrambling_id, slot, symbol); - nr_pdcch_dmrs_rx(ue, slot, gold, pilot, 2000, (nb_rb_coreset + dmrs_ref)); + nr_pdcch_dmrs_rx(gold, pilot, 2000, (nb_rb_coreset + dmrs_ref)); for (aarx=0; aarxframe_parms.nb_antennas_rx; aarx++) { From 34052bf0ce93ced000fdd5648785916bc3569fd2 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Wed, 20 Aug 2025 17:49:00 +0200 Subject: [PATCH 020/275] Split PBCH and PDCCH processing in NR UE --- executables/nr-ue.c | 3 +- openair1/SCHED_NR_UE/defs.h | 3 +- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 81 +++++++++++---------- openair1/SIMULATION/NR_PHY/dlsim.c | 5 +- 4 files changed, 49 insertions(+), 43 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index f789484465..e7132e15cf 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -564,7 +564,8 @@ static int UE_dl_preprocessing(PHY_VARS_NR_UE *UE, UE->if_inst->dl_indication(&dl_indication); } - sampleShift = pbch_pdcch_processing(UE, proc, phy_data); + sampleShift = pbch_processing(UE, proc, phy_data); + pdcch_processing(UE, proc, phy_data); if (phy_data->dlsch[0].active && (phy_data->dlsch[0].rnti_type == TYPE_C_RNTI_ || phy_data->dlsch[0].rnti_type == TYPE_RA_RNTI_)) { // indicate to tx thread to wait for DLSCH decoding diff --git a/openair1/SCHED_NR_UE/defs.h b/openair1/SCHED_NR_UE/defs.h index c7f0deb118..e3c92ba5d4 100644 --- a/openair1/SCHED_NR_UE/defs.h +++ b/openair1/SCHED_NR_UE/defs.h @@ -92,7 +92,8 @@ */ 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); -int pbch_pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data); +int pbch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data); +void pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data); void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data); diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index f22d13c5fc..7e4ddd1067 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -1027,14 +1027,55 @@ static bool is_ssb_index_transmitted(const PHY_VARS_NR_UE *ue, const int index) return ue->frame_parms.ssb_index == index; } -int pbch_pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data) +void pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data) +{ + TracyCZone(ctx); + NR_DL_FRAME_PARMS *fp = &ue->frame_parms; + NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; + const uint32_t rxdataF_sz = ue->frame_parms.samples_per_slot_wCP; + __attribute__ ((aligned(32))) c16_t rxdataF[ue->frame_parms.nb_antennas_rx][rxdataF_sz]; + + uint8_t nb_symb_pdcch = phy_pdcch_config->nb_search_space > 0 ? phy_pdcch_config->pdcch_config[0].coreset.duration : 0; + int first_symb_pdcch = phy_pdcch_config->nb_search_space > 0 ? phy_pdcch_config->pdcch_config[0].coreset.StartSymbolIndex : 0; + int last_symb_pdcch = first_symb_pdcch + nb_symb_pdcch; + for (int l = first_symb_pdcch; l < last_symb_pdcch; l++) { + nr_slot_fep(ue, fp, proc->nr_slot_rx, l, rxdataF, link_type_dl, 0, ue->common_vars.rxdata); + } + + // Hold the channel estimates in frequency domain. + int32_t pdcch_est_size = ((((fp->symbols_per_slot*(fp->ofdm_symbol_size+LTE_CE_FILTER_LENGTH))+15)/16)*16); + __attribute__((aligned(16))) c16_t pdcch_dl_ch_estimates[4 * fp->nb_antennas_rx][pdcch_est_size]; + + uint8_t dci_cnt = 0; + for(int n_ss = 0; n_ssnb_search_space; n_ss++) { + for (int l = first_symb_pdcch; l < last_symb_pdcch; l++) { + // note: this only works if RBs for PDCCH are contigous! + nr_pdcch_channel_estimation(ue, + proc, + l, + &phy_pdcch_config->pdcch_config[n_ss].coreset, + fp->first_carrier_offset, + phy_pdcch_config->pdcch_config[n_ss].BWPStart, + pdcch_est_size, + pdcch_dl_ch_estimates, + rxdataF); + } + dci_cnt = dci_cnt + nr_ue_pdcch_procedures(ue, proc, pdcch_est_size, pdcch_dl_ch_estimates, phy_data, n_ss, rxdataF); + } + LOG_D(PHY, "[UE %d] Frame %d, nr_slot_rx %d: found %d DCIs\n", ue->Mod_id, proc->frame_rx, proc->nr_slot_rx, dci_cnt); + phy_pdcch_config->nb_search_space = 0; + TracyCZoneEnd(ctx); + if (ue->phy_sim_rxdataF) + memcpy(ue->phy_sim_rxdataF, rxdataF[0], sizeof(int32_t) * nb_symb_pdcch * ue->frame_parms.ofdm_symbol_size); +} + +int pbch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data) { TracyCZone(ctx, true); int frame_rx = proc->frame_rx; int nr_slot_rx = proc->nr_slot_rx; int gNB_id = proc->gNB_id; NR_DL_FRAME_PARMS *fp = &ue->frame_parms; - NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; int sampleShift = INT_MAX; nr_ue_dlsch_init(phy_data->dlsch, NR_MAX_NB_LAYERS>4 ? 2:1, ue->max_ldpc_iterations); @@ -1163,43 +1204,7 @@ int pbch_pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_ } // for i } // for rsc_id } // for gNB_id - - LOG_D(PHY," ------ --> PDCCH ChannelComp/LLR Frame.slot %d.%d ------ \n", frame_rx%1024, nr_slot_rx); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PDCCH, VCD_FUNCTION_IN); - - uint8_t nb_symb_pdcch = phy_pdcch_config->nb_search_space > 0 ? phy_pdcch_config->pdcch_config[0].coreset.duration : 0; - int first_symb_pdcch = phy_pdcch_config->nb_search_space > 0 ? phy_pdcch_config->pdcch_config[0].coreset.StartSymbolIndex : 0; - int last_symb_pdcch = first_symb_pdcch + nb_symb_pdcch; - for (int l = first_symb_pdcch; l < last_symb_pdcch; l++) { - nr_slot_fep(ue, fp, proc->nr_slot_rx, l, rxdataF, link_type_dl, 0, ue->common_vars.rxdata); - } - - // Hold the channel estimates in frequency domain. - int32_t pdcch_est_size = ((((fp->symbols_per_slot*(fp->ofdm_symbol_size+LTE_CE_FILTER_LENGTH))+15)/16)*16); - __attribute__((aligned(16))) c16_t pdcch_dl_ch_estimates[4 * fp->nb_antennas_rx][pdcch_est_size]; - - uint8_t dci_cnt = 0; - for(int n_ss = 0; n_ssnb_search_space; n_ss++) { - for (int l = first_symb_pdcch; l < last_symb_pdcch; l++) { - // note: this only works if RBs for PDCCH are contigous! - nr_pdcch_channel_estimation(ue, - proc, - l, - &phy_pdcch_config->pdcch_config[n_ss].coreset, - fp->first_carrier_offset, - phy_pdcch_config->pdcch_config[n_ss].BWPStart, - pdcch_est_size, - pdcch_dl_ch_estimates, - rxdataF); - } - dci_cnt = dci_cnt + nr_ue_pdcch_procedures(ue, proc, pdcch_est_size, pdcch_dl_ch_estimates, phy_data, n_ss, rxdataF); - } - LOG_D(PHY, "[UE %d] Frame %d, nr_slot_rx %d: found %d DCIs\n", ue->Mod_id, frame_rx, nr_slot_rx, dci_cnt); - phy_pdcch_config->nb_search_space = 0; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PDCCH, VCD_FUNCTION_OUT); TracyCZoneEnd(ctx); - if (ue->phy_sim_rxdataF) - memcpy(ue->phy_sim_rxdataF, rxdataF[0], sizeof(int32_t) * nb_symb_pdcch * ue->frame_parms.ofdm_symbol_size); return sampleShift; } diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 6329d5849f..752211fa10 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -1216,9 +1216,8 @@ int main(int argc, char **argv) ue_dci_configuration(UE_mac, &dl_config, frame, slot); nr_ue_scheduled_response(&scheduled_response); - pbch_pdcch_processing(UE, - &UE_proc, - &phy_data); + pbch_processing(UE, &UE_proc, &phy_data); + pdcch_processing(UE, &UE_proc, &phy_data); pdsch_processing(UE, &UE_proc, &phy_data); From 22a335b1a8f22866330db4f9469f3aedb875850e Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Wed, 10 Sep 2025 07:54:30 +0200 Subject: [PATCH 021/275] Fix too large array in UE PDCCH RX --- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 7e4ddd1067..f18862e0aa 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -1044,7 +1044,7 @@ void pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_ // Hold the channel estimates in frequency domain. int32_t pdcch_est_size = ((((fp->symbols_per_slot*(fp->ofdm_symbol_size+LTE_CE_FILTER_LENGTH))+15)/16)*16); - __attribute__((aligned(16))) c16_t pdcch_dl_ch_estimates[4 * fp->nb_antennas_rx][pdcch_est_size]; + __attribute__((aligned(16))) c16_t pdcch_dl_ch_estimates[fp->nb_antennas_rx][pdcch_est_size]; uint8_t dci_cnt = 0; for(int n_ss = 0; n_ssnb_search_space; n_ss++) { From dcb69986ebddde7a0dbf5ef62eca1f5aba6d6a81 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Mon, 8 Sep 2025 17:07:22 +0200 Subject: [PATCH 022/275] Fix segfault in nr_pdcch_extract_rbs_single Fixed an issue where skipping unused PDCCH RBGs would cause the function to write/read from outside the symbol boundary --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 71 +++++++++++++-------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 43b27e3932..47ebae8891 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -249,44 +249,43 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, * the position 0+c_rb-N_RB_DL/2 in rxdataF and we have to point the pointer at (1+c_rb-N_RB_DL/2) in rxdataF */ - int c_rb = 0; - for (int rb = 0; rb < coreset_nbr_rb; rb++, c_rb++) { - int c_rb_by6 = c_rb / 6; + for (int rb_group = 0; rb_group < coreset_nbr_rb / 6; rb_group++) { + if ((coreset_freq_dom[rb_group / 8] & (1 << (7 - (rb_group & 7)))) == 0) { + continue; + } + for (int rb = 0; rb < 6; rb++) { + int c_rb = rb_group * 6 + rb; + c16_t *rxF = NULL; + if ((frame_parms->N_RB_DL & 1) == 0) { + if ((c_rb + n_BWP_start) < frame_parms->N_RB_DL / 2) + // if RB to be treated is lower than middle system bandwidth then rxdataF pointed + // at (offset + c_br + symbol * ofdm_symbol_size): even case + rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); + else + // number of RBs is even and c_rb is higher than half system bandwidth (we don't skip DC) + // if these conditions are true the pointer has to be situated at the 1st part of the rxdataF + // we point at the 1st part of the rxdataF in symbol + rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2); + } else { + if ((c_rb + n_BWP_start) <= frame_parms->N_RB_DL / 2) + // if RB to be treated is lower than middle system bandwidth then rxdataF pointed + // at (offset + c_br + symbol * ofdm_symbol_size): odd case + rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); + else + // number of RBs is odd and c_rb is higher than half system bandwidth + 1 + // if these conditions are true the pointer has to be situated at the 1st part of + // the rxdataF just after the first IQ symbols of the RB containing DC + // we point at the 1st part of the rxdataF in symbol + rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2) - 6; + } - // skip zeros in frequency domain bitmap - while ((coreset_freq_dom[c_rb_by6 / 8] & (1 << (7 - (c_rb_by6 & 7)))) == 0) { - c_rb += 6; - c_rb_by6 = c_rb / 6; + const int valid_re[RE_PER_RB_OUT_DMRS] = {0, 2, 3, 4, 6, 7, 8, 10, 11}; + for (int i = 0; i < sizeofArray(valid_re); i++) { + *rxF_ext++ = rxF[valid_re[i]]; + *dl_ch0_ext++ = dl_ch0[valid_re[i]]; + } + dl_ch0 += RE_PER_RB; } - c16_t *rxF = NULL; - if ((frame_parms->N_RB_DL & 1) == 0) { - if ((c_rb + n_BWP_start) < frame_parms->N_RB_DL / 2) - // if RB to be treated is lower than middle system bandwidth then rxdataF pointed - // at (offset + c_br + symbol * ofdm_symbol_size): even case - rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); - else - // number of RBs is even and c_rb is higher than half system bandwidth (we don't skip DC) - // if these conditions are true the pointer has to be situated at the 1st part of the rxdataF - // we point at the 1st part of the rxdataF in symbol - rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2); - } else { - if ((c_rb + n_BWP_start) <= frame_parms->N_RB_DL / 2) - // if RB to be treated is lower than middle system bandwidth then rxdataF pointed - // at (offset + c_br + symbol * ofdm_symbol_size): odd case - rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); - else - // number of RBs is odd and c_rb is higher than half system bandwidth + 1 - // if these conditions are true the pointer has to be situated at the 1st part of - // the rxdataF just after the first IQ symbols of the RB containing DC - // we point at the 1st part of the rxdataF in symbol - rxF = rxFbase + RE_PER_RB * (c_rb + n_BWP_start - frame_parms->N_RB_DL / 2) - 6; - } - const int valid_re[RE_PER_RB_OUT_DMRS] = {0, 2, 3, 4, 6, 7, 8, 10, 11}; - for (int i = 0; i < sizeofArray(valid_re); i++) { - *rxF_ext++ = rxF[valid_re[i]]; - *dl_ch0_ext++ = dl_ch0[valid_re[i]]; - } - dl_ch0 += RE_PER_RB; } } } From 07ce1ed145e4742ba1b923634ab5183e5077383d Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Tue, 9 Sep 2025 16:32:29 +0200 Subject: [PATCH 023/275] Fix reading reading out of bounds in nr_pdcch_extract_rbs_single Fix reading out of bounds of rxdataF in the middle PRB in case of odd cell bandwidth. --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 47ebae8891..44c560f6c7 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -249,6 +249,7 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, * the position 0+c_rb-N_RB_DL/2 in rxdataF and we have to point the pointer at (1+c_rb-N_RB_DL/2) in rxdataF */ + c16_t middle_prb_buffer[RE_PER_RB]; for (int rb_group = 0; rb_group < coreset_nbr_rb / 6; rb_group++) { if ((coreset_freq_dom[rb_group / 8] & (1 << (7 - (rb_group & 7)))) == 0) { continue; @@ -270,7 +271,15 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, if ((c_rb + n_BWP_start) <= frame_parms->N_RB_DL / 2) // if RB to be treated is lower than middle system bandwidth then rxdataF pointed // at (offset + c_br + symbol * ofdm_symbol_size): odd case - rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); + // Reassemble the middle PRB + if (c_rb + n_BWP_start == frame_parms->N_RB_DL / 2) { + memcpy(middle_prb_buffer, rxFbase + frame_parms->ofdm_symbol_size - RE_PER_RB / 2, sizeof(c16_t) * RE_PER_RB / 2); + memcpy(middle_prb_buffer + RE_PER_RB / 2, rxFbase, sizeof(c16_t) * RE_PER_RB / 2); + rxF = middle_prb_buffer; + } else { + rxF = rxFbase + frame_parms->first_carrier_offset + RE_PER_RB * (c_rb + n_BWP_start); + } + else // number of RBs is odd and c_rb is higher than half system bandwidth + 1 // if these conditions are true the pointer has to be situated at the 1st part of From ece9d51db30323a2aae568ec274ed103b5ecc370 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Thu, 21 Aug 2025 08:49:31 +0200 Subject: [PATCH 024/275] Symbol based PDCCH processing 1. Refactor PDCCH PHY functions to take rxdataF of one OFDM symbol and produce LLR for each search space. The modified functions take rxdataF in a different format than that produced by nr_slot_fep(). So for now a memcpy is used to change the array structure. Once nr_slot_fep() is modified, this memcpy will no longer be needed and will be removed. 2. Modify DCI functions to decode DCI in the last PDCCH symbol and inidcate to MAC once. 3. Fix PDCCH monitoring for start symbol > 0. 4. Fix multiple PDCCH monitoring within slot. Co-authored-by: Sakthivel Velumani --- .../nfapi/public_inc/fapi_nr_ue_interface.h | 1 + .../nr_dl_channel_estimation.c | 28 +- openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h | 2 +- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 321 ++++++++++++------ .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 20 +- openair1/SCHED_NR_UE/defs.h | 19 +- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 127 +++---- openair1/SIMULATION/NR_PHY/pbchsim.c | 11 - .../NR_MAC_UE/nr_ue_dci_configuration.c | 7 +- 9 files changed, 307 insertions(+), 229 deletions(-) diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h index dc52f1de2a..faebaffc6f 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h @@ -73,6 +73,7 @@ typedef struct { /// frequency_domain_resource; uint8_t frequency_domain_resource[6]; uint8_t StartSymbolIndex; + uint16_t StartSymbolBitmap; uint8_t duration; uint8_t CceRegMappingType; // interleaved or noninterleaved uint8_t RegBundleSize; // valid if CCE to REG mapping type is interleaved type diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index e77fe87069..6a49a17608 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -805,17 +805,12 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, uint16_t BWPStart, int32_t pdcch_est_size, c16_t pdcch_dl_ch_estimates[][pdcch_est_size], - c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]) + c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size]) { int slot = proc->nr_slot_rx; unsigned char aarx; unsigned short k; unsigned int pilot_cnt; - int ch_offset,symbol_offset; - - ch_offset = ue->frame_parms.ofdm_symbol_size*symbol; - - symbol_offset = ue->frame_parms.ofdm_symbol_size*symbol; int nb_rb_coreset=0; int coreset_start_rb=0; @@ -830,8 +825,7 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, unsigned short coreset_start_subcarrier = first_carrier_offset+(BWPStart + coreset_start_rb)*12; #ifdef DEBUG_PDCCH - printf("PDCCH Channel Estimation : ch_offset %d, OFDM size %d, Ncp=%d, slot=%d, symbol %d\n", - ch_offset, + printf("PDCCH Channel Estimation : OFDM size %d, Ncp=%d, slot=%d, symbol %d\n", ue->frame_parms.ofdm_symbol_size, ue->frame_parms.Ncp, slot, @@ -858,8 +852,8 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, k = coreset_start_subcarrier; c16_t *pil = &pilot[dmrs_ref * 3]; - c16_t *rxF = &rxdataF[aarx][symbol_offset + k + 1]; - c16_t *dl_ch = &pdcch_dl_ch_estimates[aarx][ch_offset]; + c16_t *rxF = &rxdataF[aarx][k + 1]; + c16_t *dl_ch = &pdcch_dl_ch_estimates[aarx][0]; memset(dl_ch, 0, sizeof(c16_t) * ue->frame_parms.ofdm_symbol_size); @@ -879,29 +873,29 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, if (k >= ue->frame_parms.ofdm_symbol_size) { k -= ue->frame_parms.ofdm_symbol_size; - rxF = &rxdataF[aarx][(symbol_offset + k + 1)]; + rxF = &rxdataF[aarx][k + 1]; } multadd_real_vector_complex_scalar(fm, c16mulShift(*pil++, *rxF, 15), dl_ch, 16); k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][(symbol_offset + k + 1)]; + rxF = (c16_t *)&rxdataF[aarx][k + 1)]; multadd_real_vector_complex_scalar(fr, c16mulShift(*pil++, *rxF, 15), dl_ch, 16); dl_ch += 12; k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][(symbol_offset + k + 1)]; + rxF = (c16_t *)&rxdataF[aarx][k + 1]; for (pilot_cnt=3; pilot_cnt<(3*nb_rb_coreset); pilot_cnt += 3) { multadd_real_vector_complex_scalar(fl, c16mulShift(*pil++, *rxF, 15), dl_ch, 16); k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][(symbol_offset + k + 1)]; + rxF = (c16_t *)&rxdataF[aarx][k + 1]; multadd_real_vector_complex_scalar(fm, c16mulShift(*pil++, *rxF, 15), dl_ch, 16); k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][(symbol_offset + k + 1)]; + rxF = (c16_t *)&rxdataF[aarx][k + 1]; multadd_real_vector_complex_scalar(fr, c16mulShift(*pil++, *rxF, 15), dl_ch, 16); dl_ch += 12; k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][(symbol_offset + k + 1)]; + rxF = (c16_t *)&rxdataF[aarx][k + 1]; } #else //ELSE CH_INTERP c32_t ch_sum = {0, 0}; @@ -915,7 +909,7 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, ch_sum.r += ch.r; ch_sum.i += ch.i; k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][(symbol_offset + k + 1)]; + rxF = (c16_t *)&rxdataF[aarx][k + 1]; if (pilot_cnt % 3 == 2) { ch.r = ch_sum.r / 3; diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h index 66339e25a2..85b8999118 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h @@ -52,7 +52,7 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, uint16_t BWPStart, int32_t pdcch_est_size, c16_t pdcch_dl_ch_estimates[][pdcch_est_size], - c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]); + c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size]); c32_t nr_pbch_dmrs_correlation(const NR_DL_FRAME_PARMS *fp, const UE_nr_rxtx_proc_t *proc, diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 44c560f6c7..9e48f52a03 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -41,6 +41,7 @@ #include "common/utils/nr/nr_common.h" #include #include "openair1/PHY/NR_REFSIG/nr_refsig_common.h" +#include "PHY/NR_UE_ESTIMATION/nr_estimation.h" #include "assertions.h" #include "T.h" @@ -72,10 +73,10 @@ #define RE_PER_RB_OUT_DMRS 9 static void nr_pdcch_demapping_deinterleaving(uint32_t coreset_nbr_rb, - c16_t llr[][coreset_nbr_rb * RE_PER_RB_OUT_DMRS], + c16_t *llr, c16_t *e_rx, uint8_t coreset_time_dur, - uint8_t reg_bundle_size_L, + uint8_t reg_bundle_size_L_in, uint8_t coreset_interleaver_size_R, uint8_t n_shift, uint8_t number_of_candidates, @@ -118,17 +119,11 @@ static void nr_pdcch_demapping_deinterleaving(uint32_t coreset_nbr_rb, | REG 0 (bundle 0) bundle 0 bundle 0 */ - - uint32_t coreset_C = 0; - int coreset_interleaved = 0; const int N_regs = coreset_nbr_rb * coreset_time_dur; - - if (reg_bundle_size_L != 0) { // interleaving will be done only if reg_bundle_size_L != 0 - coreset_interleaved = 1; - coreset_C = (uint32_t)(N_regs / (coreset_interleaver_size_R * reg_bundle_size_L)); - } else { - reg_bundle_size_L = 6; - } + /* interleaving will be done only if reg_bundle_size_L != 0 */ + const int coreset_C = (reg_bundle_size_L_in != 0) ? (uint32_t)(N_regs / (coreset_interleaver_size_R * reg_bundle_size_L_in)) : 0; + const int coreset_interleaved = (reg_bundle_size_L_in != 0) ? 1 : 0; + const int reg_bundle_size_L = (reg_bundle_size_L_in != 0) ? reg_bundle_size_L_in : 6; int B_rb = reg_bundle_size_L / coreset_time_dur; // nb of RBs occupied by each REG bundle int num_bundles_per_cce = 6 / reg_bundle_size_L; @@ -173,7 +168,7 @@ static void nr_pdcch_demapping_deinterleaving(uint32_t coreset_nbr_rb, for (int cce_count = 0; cce_count < L[c_id]; cce_count++) { for (int k = 0; k < NR_NB_REG_PER_CCE / reg_bundle_size_L; k++) { // loop over REG bundles int f = f_bundle_j_list_ord[c_id][k + NR_NB_REG_PER_CCE * cce_count / reg_bundle_size_L]; - c16_t *in = llr[symbol_idx] + f * B_rb * RE_PER_RB_OUT_DMRS; + c16_t *in = llr + (f * B_rb + symbol_idx * coreset_nbr_rb) * RE_PER_RB_OUT_DMRS; // loop over the RBs of the bundle memcpy(e_rx + RE_PER_RB_OUT_DMRS * rb_count, in, B_rb * RE_PER_RB_OUT_DMRS * sizeof(*e_rx)); rb_count += B_rb; @@ -200,16 +195,15 @@ static void nr_pdcch_llr(uint32_t sz, c16_t *rxF, c16_t *llr) // This function will extract the mapped DM-RS PDCCH REs as per 38.211 Section 7.4.1.3.2 (Mapping to physical resources) static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, - uint32_t coreset_nbr_rb, c16_t rxdataF[][rxdataF_sz], int32_t est_size, c16_t dl_ch_estimates[][est_size], int arraySz, c16_t rxdataF_ext[][arraySz], c16_t dl_ch_estimates_ext[][arraySz], - int symbol, NR_DL_FRAME_PARMS *frame_parms, uint8_t *coreset_freq_dom, + uint32_t coreset_nbr_rb, uint32_t n_BWP_start) { /* @@ -224,8 +218,8 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, */ for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { - const c16_t *dl_ch0 = dl_ch_estimates[aarx] + frame_parms->ofdm_symbol_size * symbol; - c16_t *rxFbase = rxdataF[aarx] + frame_parms->ofdm_symbol_size * symbol; + c16_t *dl_ch0 = dl_ch_estimates[aarx]; + c16_t *rxFbase = rxdataF[aarx]; LOG_DDD("dl_ch0 = &dl_ch_estimates[aarx = (%d)][0]\n", aarx); c16_t *dl_ch0_ext = dl_ch_estimates_ext[aarx]; @@ -326,92 +320,222 @@ static void nr_pdcch_detection_mrc(int sz, c16_t rxdataF_comp[][sz]) } } -void nr_rx_pdcch(PHY_VARS_NR_UE *ue, - const UE_nr_rxtx_proc_t *proc, - int32_t pdcch_est_size, - c16_t pdcch_dl_ch_estimates[][pdcch_est_size], - c16_t *pdcch_e_rx, - fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15, - c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]) +/* Produce LLRs from received PDCCH signal */ +static void nr_rx_pdcch_symbol(PHY_VARS_NR_UE *ue, + const UE_nr_rxtx_proc_t *proc, + int symbol, + int rel_symb_monOcc, + int ss_idx, + nr_phy_data_t *phy_data, + int llr_size_symbol, + c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size], + c16_t llr[llr_size_symbol]) { - NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms; + NR_DL_FRAME_PARMS *fp = &ue->frame_parms; + NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; + fapi_nr_coreset_t *coreset = &phy_pdcch_config->pdcch_config[ss_idx].coreset; + int32_t pdcch_est_size = ((((fp->ofdm_symbol_size + LTE_CE_FILTER_LENGTH) + 15) / 16) * 16); + __attribute__((aligned(16))) c16_t pdcch_dl_ch_estimates[fp->nb_antennas_rx][pdcch_est_size]; - int n_rb,rb_offset; - get_coreset_rballoc(rel15->coreset.frequency_domain_resource,&n_rb,&rb_offset); - const int antRx = frame_parms->nb_antennas_rx; + nr_pdcch_channel_estimation(ue, + proc, + symbol, + coreset, + fp->first_carrier_offset, + phy_pdcch_config->pdcch_config[ss_idx].BWPStart, + pdcch_est_size, + pdcch_dl_ch_estimates, + rxdataF); - // Pointer to llrs - const int symb_size = n_rb * RE_PER_RB_OUT_DMRS; - c16_t llr[rel15->coreset.duration][symb_size]; + const int32_t rx_size = ((4 * fp->N_RB_DL * 12 + 31) >> 5) << 5; + __attribute__((aligned(32))) c16_t rxdataF_ext[fp->nb_antennas_rx][rx_size]; + __attribute__((aligned(32))) c16_t rxdataF_comp[fp->nb_antennas_rx][rx_size]; + __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[fp->nb_antennas_rx][rx_size]; + memset(rxdataF_comp, 0, sizeof(rxdataF_comp)); - LOG_D(NR_PHY_DCI, - "pdcch coreset: freq %x, n_rb %d, rb_offset %d\n", - rel15->coreset.frequency_domain_resource[0], - n_rb, - rb_offset); - for (int s = 0; s < rel15->coreset.duration; s++) { - LOG_D(NR_PHY_DCI, "in nr_pdcch_extract_rbs_single(rxdataF -> rxdataF_ext || dl_ch_estimates -> dl_ch_estimates_ext)\n"); - const int arraySz = ceil_mod(symb_size, 32); - __attribute__((aligned(32))) c16_t rxdataF_ext[antRx][arraySz]; - __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[antRx][arraySz]; - nr_pdcch_extract_rbs_single(ue->frame_parms.samples_per_slot_wCP, - n_rb, - rxdataF, - pdcch_est_size, - pdcch_dl_ch_estimates, - arraySz, + int n_rb; + int rb_offset; + get_coreset_rballoc(coreset->frequency_domain_resource, &n_rb, &rb_offset); + + nr_pdcch_extract_rbs_single(ue->frame_parms.ofdm_symbol_size, + rxdataF, + pdcch_est_size, + pdcch_dl_ch_estimates, + rx_size, + rxdataF_ext, + pdcch_dl_ch_estimates_ext, + fp, + coreset->frequency_domain_resource, + n_rb, + phy_pdcch_config->pdcch_config[ss_idx].BWPStart); + + LOG_D(NR_PHY_DCI, "in channel level function (dl_ch_estimates_ext -> dl_ch_estimates_ext)\n"); + int avg[fp->nb_antennas_rx]; + nr_channel_level(0, rx_size, pdcch_dl_ch_estimates_ext, fp->nb_antennas_rx, 1, avg, n_rb * RE_PER_RB_OUT_DMRS); + int avgs = avg[0]; + for (int i = 1; i < fp->nb_antennas_rx; i++) + avgs = cmax(avgs, avg[i]); + const int log2_maxh = (log2_approx(avgs) / 2) + 5; //+frame_parms->nb_antennas_rx; + + nr_pdcch_channel_compensation(rx_size, rxdataF_ext, pdcch_dl_ch_estimates_ext, - rel15->coreset.StartSymbolIndex + s, - frame_parms, - rel15->coreset.frequency_domain_resource, - rel15->BWPStart); + rxdataF_comp, + fp->nb_antennas_rx, + log2_maxh); // log2_maxh+I0_shift - LOG_D(NR_PHY_DCI, "in channel level function (dl_ch_estimates_ext -> dl_ch_estimates_ext)\n"); - // compute channel level based on ofdm symbol 0 - int avg[antRx]; - nr_channel_level(0, arraySz, pdcch_dl_ch_estimates_ext, antRx, 1, avg, n_rb); - int avgs = avg[0]; - for (int i = 1; i < antRx; i++) - avgs = cmax(avgs, avg[i]); - uint8_t log2_maxh = (log2_approx(avgs) / 2) + 5; //+antRx; + UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), fp->nb_antennas_rx, rx_size, 0); -#ifdef UE_DEBUG_TRACE - LOG_D(NR_PHY_DCI, "slot %d: pdcch log2_maxh = %d (%d,%d)\n", proc->nr_slot_rx, log2_maxh, avgP[0], avgs); -#endif -#if T_TRACER - T(T_UE_PHY_PDCCH_ENERGY, T_INT(0), T_INT(0), T_INT(proc->frame_rx % 1024), T_INT(proc->nr_slot_rx), T_INT(avgs)); -#endif - LOG_D(NR_PHY_DCI, "we enter nr_pdcch_channel_compensation(log2_maxh=%d)\n", log2_maxh); - LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_compensation(rxdataF_ext x dl_ch_estimates_ext -> rxdataF_comp)\n"); - // compute LLRs for ofdm symbol 0 only - __attribute__((aligned(32))) c16_t rxdataF_comp[antRx][arraySz]; - nr_pdcch_channel_compensation(arraySz, - rxdataF_ext, - pdcch_dl_ch_estimates_ext, - rxdataF_comp, - antRx, - log2_maxh); // log2_maxh+I0_shift - - UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), antRx, symb_size, 0); - - if (antRx > 1) - nr_pdcch_detection_mrc(arraySz, rxdataF_comp); - - nr_pdcch_llr(symb_size, rxdataF_comp[0], llr[s]); + if (fp->nb_antennas_rx > 1) { + nr_pdcch_detection_mrc(rx_size, rxdataF_comp); } - UEscopeCopy(ue, pdcchLlr, llr, sizeof(c16_t), 1, rel15->coreset.duration * symb_size, 0); - nr_pdcch_demapping_deinterleaving(n_rb, - llr, - pdcch_e_rx, - rel15->coreset.duration, - rel15->coreset.RegBundleSize, - rel15->coreset.InterleaverSize, - rel15->coreset.ShiftIndex, - rel15->number_of_candidates, - rel15->CCE, - rel15->L); + nr_pdcch_llr(llr_size_symbol, rxdataF_comp[0], llr); +} + +bool is_start_symbol_in_ss(const fapi_nr_dl_config_dci_dl_pdu_rel15_t *ss, const int symbol) +{ + return ((ss->coreset.StartSymbolBitmap >> (NR_SYMBOLS_PER_SLOT - 1 - symbol)) & 1); +} + +int get_pdcch_mon_occasions_slot(const fapi_nr_dl_config_dci_dl_pdu_rel15_t *ss, uint8_t start_symb[NR_SYMBOLS_PER_SLOT]) +{ + int sum = 0; + for (int s = 0; s < NR_SYMBOLS_PER_SLOT; s++) { + if (is_start_symbol_in_ss(ss, s)) { + if (start_symb != NULL) + start_symb[sum] = s; + sum++; + } + } + + return sum; +} + +int get_max_pdcch_monOcc(const NR_UE_PDCCH_CONFIG *phy_pdcch_config) +{ + int monOcc = 0; + for (int ss = 0; ss < phy_pdcch_config->nb_search_space; ss++) { + monOcc = max(monOcc, get_pdcch_mon_occasions_slot(&phy_pdcch_config->pdcch_config[ss], NULL)); + } + return monOcc; +} + +static void nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue, + const UE_nr_rxtx_proc_t *proc, + c16_t *pdcch_e_rx, + fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15, + fapi_nr_dci_indication_t *dci_ind); + +void set_first_last_pdcch_symb(const NR_UE_PDCCH_CONFIG *phy_pdcch_config, int *first_symb, int *last_symb) +{ + *first_symb = NR_SYMBOLS_PER_SLOT; // max first pdcch symbol + *last_symb = 0; // min last pdcch symbol + for (int ss = 0; ss < phy_pdcch_config->nb_search_space; ss++) { + for (int symb = 0; symb < NR_SYMBOLS_PER_SLOT; symb++) { + if (is_start_symbol_in_ss(&phy_pdcch_config->pdcch_config[ss], symb)) { + const int duration = phy_pdcch_config->pdcch_config[ss].coreset.duration; + *first_symb = min(*first_symb, symb); + *last_symb = max(*last_symb, symb + duration - 1); + } + } + } +} + +/* Generates PDCCH LLRs from received symbol for each Search-Space */ +void nr_pdcch_generate_llr(PHY_VARS_NR_UE *ue, + const UE_nr_rxtx_proc_t *proc, + int symbol, + nr_phy_data_t *phy_data, + int llr_size_symbol, + int num_monitoring_occ, + int max_symb, + c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size], + c16_t pdcch_llr[phy_data->phy_pdcch_config.nb_search_space][num_monitoring_occ][max_symb * llr_size_symbol]) +{ + const NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; + + // Loop over search spaces + for (int ss_idx = 0; ss_idx < phy_pdcch_config->nb_search_space; ss_idx++) { + uint8_t start_symb[NR_SYMBOLS_PER_SLOT] = {0}; + const int num_monOcc = get_pdcch_mon_occasions_slot(&phy_pdcch_config->pdcch_config[ss_idx], start_symb); + // Loop over monitoring occations within the slot in this ss + for (int occ = 0; occ < num_monOcc; occ++) { + const int first_symb = start_symb[occ]; + const int last_symb = first_symb + phy_pdcch_config->pdcch_config[ss_idx].coreset.duration; + // Decode PDCCH and generate LLR for each ss in this OFDM symbol + if ((symbol >= first_symb) && (symbol < last_symb)) { + const int rel_symb_monOcc = symbol - first_symb; + nr_rx_pdcch_symbol(ue, + proc, + symbol, + rel_symb_monOcc, + ss_idx, + phy_data, + llr_size_symbol, + rxdataF, + &pdcch_llr[ss_idx][occ][rel_symb_monOcc * llr_size_symbol]); + } + } + } +} + +/* Decode DCI from LLRs for each Search-Space and send to MAC */ +void nr_pdcch_dci_indication(const UE_nr_rxtx_proc_t *proc, + int llr_size, + int max_monOcc, + PHY_VARS_NR_UE *ue, + nr_phy_data_t *phy_data, + c16_t llr[phy_data->phy_pdcch_config.nb_search_space][max_monOcc][llr_size]) +{ + NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; + + nr_downlink_indication_t dl_indication; + fapi_nr_dci_indication_t dci_ind = {.SFN = proc->frame_rx, .slot = proc->nr_slot_rx}; + + for (int ss_idx = 0; ss_idx < phy_pdcch_config->nb_search_space; ss_idx++) { + fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &phy_pdcch_config->pdcch_config[ss_idx]; + uint8_t unused_start_symb[NR_SYMBOLS_PER_SLOT] = {0}; + const int num_monitoring_occ = get_pdcch_mon_occasions_slot(rel15, unused_start_symb); + + int n_rb; + int rb_offset; + get_coreset_rballoc(rel15->coreset.frequency_domain_resource, &n_rb, &rb_offset); + + for (int m = 0; m < num_monitoring_occ; m++) { + /// PDCCH/DCI e-sequence (input to rate matching). + c16_t pdcch_e_rx[NR_MAX_PDCCH_SIZE]; + + nr_pdcch_demapping_deinterleaving(n_rb, + llr[ss_idx][m], + pdcch_e_rx, + rel15->coreset.duration, + rel15->coreset.RegBundleSize, + rel15->coreset.InterleaverSize, + rel15->coreset.ShiftIndex, + rel15->number_of_candidates, + rel15->CCE, + rel15->L); + + nr_dci_decoding_procedure(ue, proc, pdcch_e_rx, rel15, &dci_ind); + } + } + + for (int i = 0; i < dci_ind.number_of_dcis; i++) { + LOG_D(PHY, + "Frame.slot: %d.%d: DCI %i of %d total DCIs found --> rnti %x : format %d\n", + proc->frame_rx, + proc->nr_slot_rx, + i + 1, + dci_ind.number_of_dcis, + dci_ind.dci_list[i].rnti, + dci_ind.dci_list[i].dci_format); + } + + /* Send to MAC */ + nr_fill_dl_indication(&dl_indication, &dci_ind, NULL, proc, ue, phy_data); + ue->if_inst->dl_indication(&dl_indication); + phy_pdcch_config->nb_search_space = 0; } static void nr_pdcch_unscrambling(c16_t *e_rx, @@ -433,14 +557,13 @@ static void nr_pdcch_unscrambling(c16_t *e_rx, } } -void nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue, - const UE_nr_rxtx_proc_t *proc, - c16_t *pdcch_e_rx, - fapi_nr_dci_indication_t *dci_ind, - fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15) +static void nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue, + const UE_nr_rxtx_proc_t *proc, + c16_t *pdcch_e_rx, + fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15, + fapi_nr_dci_indication_t *dci_ind) { int e_rx_cand_idx = 0; - *dci_ind = (fapi_nr_dci_indication_t){.SFN = proc->frame_rx, .slot = proc->nr_slot_rx}; // if DCI for SIB we don't break after finding 1st DCI with that RNTI // there might be SIB1 and otherSIB in the same slot with the same length bool is_SI = rel15->rnti == SI_RNTI; 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 41747a9ef1..ed348986e8 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -69,6 +69,12 @@ typedef struct pdsch_scope_req_s { void nr_ue_dlsch_init(NR_UE_DLSCH_t *dlsch_list, int num_dlsch, uint8_t max_ldpc_iterations); void nr_conjch0_mult_ch1(c16_t *ch0, c16_t *ch1, c16_t *ch0conj_ch1, unsigned short nb_rb, unsigned char output_shift0); +void set_first_last_pdcch_symb(const NR_UE_PDCCH_CONFIG *phy_pdcch_config, int *first_symb, int *last_symb); + +int get_pdcch_mon_occasions_slot(const fapi_nr_dl_config_dci_dl_pdu_rel15_t *ss, uint8_t start_symb[NR_SYMBOLS_PER_SLOT]); + +int get_max_pdcch_monOcc(const NR_UE_PDCCH_CONFIG *phy_pdcch_config); + /** \brief This is the alternative top-level entry point for DLSCH decoding in UE. It handles all the HARQ processes in only one call. The routine first computes the segmentation information and then call LDPC decoder on the @@ -180,14 +186,6 @@ void nr_dlsch_unscrambling(int16_t* llr, uint32_t Nid, uint32_t n_RNTI); -void nr_rx_pdcch(PHY_VARS_NR_UE *ue, - const UE_nr_rxtx_proc_t *proc, - int32_t pdcch_est_size, - c16_t pdcch_dl_ch_estimates[][pdcch_est_size], - c16_t *pdcch_e_rx, - fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15, - c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]); - /*! \brief Performs detection of SSS to find cell ID and other framing parameters (FDD/TDD, normal/extended prefix) @param phy_vars_ue Pointer to UE variables @param tot_metric Pointer to variable containing maximum metric under framing hypothesis (to be compared to other hypotheses @@ -279,12 +277,6 @@ void nr_sl_rf_card_config_freq(PHY_VARS_NR_UE *ue, openair0_config_t *openair0_cfg, int freq_offset); -void nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue, - const UE_nr_rxtx_proc_t *proc, - c16_t *pdcch_e_rx, - fapi_nr_dci_indication_t *dci_ind, - fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15); - /** \brief This function is the top-level entry point to PDSCH demodulation, after frequency-domain transformation and channel estimation. It performs - RB extraction (signal and channel estimates) diff --git a/openair1/SCHED_NR_UE/defs.h b/openair1/SCHED_NR_UE/defs.h index e3c92ba5d4..3ba5226542 100644 --- a/openair1/SCHED_NR_UE/defs.h +++ b/openair1/SCHED_NR_UE/defs.h @@ -138,13 +138,22 @@ void nr_fill_rx_indication(fapi_nr_rx_indication_t *rx_ind, void *typeSpecific, uint8_t *b); -int nr_ue_pdcch_procedures(PHY_VARS_NR_UE *ue, +void nr_pdcch_generate_llr(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, - int32_t pdcch_est_size, - c16_t pdcch_dl_ch_estimates[][pdcch_est_size], + int symbol, nr_phy_data_t *phy_data, - int n_ss, - c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]); + int llr_size_symbol, + int num_monitoring_occ, + int max_symb, + c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size], + c16_t pdcch_llr[phy_data->phy_pdcch_config.nb_search_space][num_monitoring_occ][max_symb * llr_size_symbol]); + +void nr_pdcch_dci_indication(const UE_nr_rxtx_proc_t *proc, + int llr_size, + int max_monOcc, + PHY_VARS_NR_UE *ue, + nr_phy_data_t *phy_data, + c16_t llr[phy_data->phy_pdcch_config.nb_search_space][max_monOcc][llr_size]); void nr_ue_csi_im_procedures(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index f18862e0aa..490b06c83c 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -527,53 +527,6 @@ static int nr_ue_pbch_procedures(PHY_VARS_NR_UE *ue, return ret; } -int nr_ue_pdcch_procedures(PHY_VARS_NR_UE *ue, - const UE_nr_rxtx_proc_t *proc, - int32_t pdcch_est_size, - c16_t pdcch_dl_ch_estimates[][pdcch_est_size], - nr_phy_data_t *phy_data, - int n_ss, - c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]) -{ - int frame_rx = proc->frame_rx; - int nr_slot_rx = proc->nr_slot_rx; - NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; - - fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &phy_pdcch_config->pdcch_config[n_ss]; - - start_meas_nr_ue_phy(ue, DLSCH_RX_PDCCH_STATS); - - /// PDCCH/DCI e-sequence (input to rate matching). - int32_t pdcch_e_rx_size = NR_MAX_PDCCH_SIZE; - c16_t pdcch_e_rx[pdcch_e_rx_size]; - - nr_rx_pdcch(ue, proc, pdcch_est_size, pdcch_dl_ch_estimates, pdcch_e_rx, rel15, rxdataF); - - fapi_nr_dci_indication_t dci_ind; - nr_dci_decoding_procedure(ue, proc, pdcch_e_rx, &dci_ind, rel15); - - for (int i = 0; i < dci_ind.number_of_dcis; i++) { - LOG_D(PHY, - "[UE %d] AbsSubFrame %d.%d: DCI %i of %d total DCIs found --> rnti %x : format %d\n", - ue->Mod_id, - frame_rx % 1024, - nr_slot_rx, - i + 1, - dci_ind.number_of_dcis, - dci_ind.dci_list[i].rnti, - dci_ind.dci_list[i].dci_format); - } - - nr_downlink_indication_t dl_indication; - // fill dl_indication message - nr_fill_dl_indication(&dl_indication, &dci_ind, NULL, proc, ue, phy_data); - // send to mac - ue->if_inst->dl_indication(&dl_indication); - stop_meas_nr_ue_phy(ue, DLSCH_RX_PDCCH_STATS); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_PDCCH_PROCEDURES, VCD_FUNCTION_OUT); - return (dci_ind.number_of_dcis); -} - static freq_alloc_bitmap_t set_start_end_from_bitmap(int size, int alloc_size, const uint8_t bitmap[alloc_size]) { freq_alloc_bitmap_t alloc = { @@ -1027,46 +980,68 @@ static bool is_ssb_index_transmitted(const PHY_VARS_NR_UE *ue, const int index) return ue->frame_parms.ssb_index == index; } +static int get_pdcch_max_rbs(NR_UE_PDCCH_CONFIG *phy_pdcch_config) +{ + int nb_rb = 0; + int rb_offset = 0; + for (int i = 0; i < phy_pdcch_config->nb_search_space; i++) { + int tmp = 0; + get_coreset_rballoc(phy_pdcch_config->pdcch_config[i].coreset.frequency_domain_resource, &tmp, &rb_offset); + if (tmp > nb_rb) + nb_rb = tmp; + } + return nb_rb; +} + +static int get_max_pdcch_symb(const NR_UE_PDCCH_CONFIG *phy_pdcch_config) +{ + int max_pdcch_symb = 0; + for (int i = 0; i < phy_pdcch_config->nb_search_space; i++) + if (phy_pdcch_config->pdcch_config[i].coreset.duration > max_pdcch_symb) + max_pdcch_symb = phy_pdcch_config->pdcch_config[i].coreset.duration; + + return max_pdcch_symb; +} + void pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data) { - TracyCZone(ctx); - NR_DL_FRAME_PARMS *fp = &ue->frame_parms; + TracyCZone(ctx, true); + /* process PDCCH */ + LOG_D(PHY, " ------ --> PDCCH ChannelComp/LLR Frame.slot %d.%d ------ \n", proc->frame_rx % 1024, proc->nr_slot_rx); + start_meas_nr_ue_phy(ue, DLSCH_RX_PDCCH_STATS); NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; + int num_monitoring_occ = get_max_pdcch_monOcc(phy_pdcch_config); + int max_nb_symb_pdcch = get_max_pdcch_symb(phy_pdcch_config); + int llr_size_symbol = get_pdcch_max_rbs(phy_pdcch_config) * 9; + c16_t pdcch_llr[phy_pdcch_config->nb_search_space][num_monitoring_occ][max_nb_symb_pdcch * llr_size_symbol]; + + int start_symb_pdcch, last_symb_pdcch; + set_first_last_pdcch_symb(phy_pdcch_config, &start_symb_pdcch, &last_symb_pdcch); + + /* Temporarily loop over symbols in the slot, perform OFDM demod and process PDCCH. + When symbol based proc design is fully merged, this function will be called to process only one symbol + and OFDM demod will be removed from here. */ const uint32_t rxdataF_sz = ue->frame_parms.samples_per_slot_wCP; - __attribute__ ((aligned(32))) c16_t rxdataF[ue->frame_parms.nb_antennas_rx][rxdataF_sz]; + __attribute__((aligned(32))) c16_t rxdataF[ue->frame_parms.nb_antennas_rx][rxdataF_sz]; - uint8_t nb_symb_pdcch = phy_pdcch_config->nb_search_space > 0 ? phy_pdcch_config->pdcch_config[0].coreset.duration : 0; - int first_symb_pdcch = phy_pdcch_config->nb_search_space > 0 ? phy_pdcch_config->pdcch_config[0].coreset.StartSymbolIndex : 0; - int last_symb_pdcch = first_symb_pdcch + nb_symb_pdcch; - for (int l = first_symb_pdcch; l < last_symb_pdcch; l++) { - nr_slot_fep(ue, fp, proc->nr_slot_rx, l, rxdataF, link_type_dl, 0, ue->common_vars.rxdata); - } + for (int symbol = start_symb_pdcch; symbol <= last_symb_pdcch; symbol++) { + NR_DL_FRAME_PARMS *fp = &ue->frame_parms; + nr_slot_fep(ue, fp, proc->nr_slot_rx, symbol, rxdataF, link_type_dl, 0, ue->common_vars.rxdata); + __attribute__((aligned(32))) c16_t rxdataF_symb[fp->nb_antennas_rx][((fp->ofdm_symbol_size + 7) / 8) * 8]; - // Hold the channel estimates in frequency domain. - int32_t pdcch_est_size = ((((fp->symbols_per_slot*(fp->ofdm_symbol_size+LTE_CE_FILTER_LENGTH))+15)/16)*16); - __attribute__((aligned(16))) c16_t pdcch_dl_ch_estimates[fp->nb_antennas_rx][pdcch_est_size]; + for (int ant = 0; ant < fp->nb_antennas_rx; ant++) + memcpy(rxdataF_symb[ant], &rxdataF[ant][symbol * fp->ofdm_symbol_size], sizeof(c16_t) * fp->ofdm_symbol_size); - uint8_t dci_cnt = 0; - for(int n_ss = 0; n_ssnb_search_space; n_ss++) { - for (int l = first_symb_pdcch; l < last_symb_pdcch; l++) { - // note: this only works if RBs for PDCCH are contigous! - nr_pdcch_channel_estimation(ue, - proc, - l, - &phy_pdcch_config->pdcch_config[n_ss].coreset, - fp->first_carrier_offset, - phy_pdcch_config->pdcch_config[n_ss].BWPStart, - pdcch_est_size, - pdcch_dl_ch_estimates, - rxdataF); + nr_pdcch_generate_llr(ue, proc, symbol, phy_data, llr_size_symbol, num_monitoring_occ, max_nb_symb_pdcch, rxdataF_symb, pdcch_llr); + if (symbol == last_symb_pdcch) { + nr_pdcch_dci_indication(proc, llr_size_symbol * max_nb_symb_pdcch, num_monitoring_occ, ue, phy_data, pdcch_llr); + UEscopeCopy(ue, pdcchLlr, pdcch_llr, sizeof(c16_t), 1, sizeof(pdcch_llr), 0); } - dci_cnt = dci_cnt + nr_ue_pdcch_procedures(ue, proc, pdcch_est_size, pdcch_dl_ch_estimates, phy_data, n_ss, rxdataF); } - LOG_D(PHY, "[UE %d] Frame %d, nr_slot_rx %d: found %d DCIs\n", ue->Mod_id, proc->frame_rx, proc->nr_slot_rx, dci_cnt); - phy_pdcch_config->nb_search_space = 0; + stop_meas_nr_ue_phy(ue, DLSCH_RX_PDCCH_STATS); TracyCZoneEnd(ctx); if (ue->phy_sim_rxdataF) - memcpy(ue->phy_sim_rxdataF, rxdataF[0], sizeof(int32_t) * nb_symb_pdcch * ue->frame_parms.ofdm_symbol_size); + memcpy(ue->phy_sim_rxdataF, rxdataF[0], sizeof(int32_t) * max_nb_symb_pdcch * ue->frame_parms.ofdm_symbol_size); } int pbch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data) diff --git a/openair1/SIMULATION/NR_PHY/pbchsim.c b/openair1/SIMULATION/NR_PHY/pbchsim.c index 6279663e74..4b44b34091 100644 --- a/openair1/SIMULATION/NR_PHY/pbchsim.c +++ b/openair1/SIMULATION/NR_PHY/pbchsim.c @@ -110,17 +110,6 @@ void nr_fill_rx_indication(fapi_nr_rx_indication_t *rx_ind, { } -int nr_ue_pdcch_procedures(PHY_VARS_NR_UE *ue, - const UE_nr_rxtx_proc_t *proc, - int32_t pdcch_est_size, - c16_t pdcch_dl_ch_estimates[][pdcch_est_size], - nr_phy_data_t *phy_data, - int n_ss, - c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]) -{ - return 0; -} - configmodule_interface_t *uniqCfg = NULL; int main(int argc, char **argv) { diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 218a513ec7..8a2074422e 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -307,12 +307,7 @@ static void config_dci_pdu(NR_UE_MAC_INST_t *mac, break; } - for (int i = 0; i < sps; i++) { - if ((monitoringSymbolsWithinSlot >> (sps - 1 - i)) & 1) { - rel15->coreset.StartSymbolIndex = i; - break; - } - } + rel15->coreset.StartSymbolBitmap = monitoringSymbolsWithinSlot; uint32_t Y = 0; if (ss->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific) Y = get_Y(ss, slot, rel15->rnti); From 7807016ae62ba5a078a99dabfaf0c9133ddfb815 Mon Sep 17 00:00:00 2001 From: Sakthivel Velumani Date: Fri, 12 Sep 2025 18:51:54 -0400 Subject: [PATCH 025/275] pdcch: return pdcch processing is no ss configured --- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 490b06c83c..3f476ec7db 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -1005,11 +1005,14 @@ static int get_max_pdcch_symb(const NR_UE_PDCCH_CONFIG *phy_pdcch_config) void pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data) { + NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; + if (phy_pdcch_config->nb_search_space == 0) + return; + TracyCZone(ctx, true); /* process PDCCH */ LOG_D(PHY, " ------ --> PDCCH ChannelComp/LLR Frame.slot %d.%d ------ \n", proc->frame_rx % 1024, proc->nr_slot_rx); start_meas_nr_ue_phy(ue, DLSCH_RX_PDCCH_STATS); - NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; int num_monitoring_occ = get_max_pdcch_monOcc(phy_pdcch_config); int max_nb_symb_pdcch = get_max_pdcch_symb(phy_pdcch_config); int llr_size_symbol = get_pdcch_max_rbs(phy_pdcch_config) * 9; From 48773b8517cf4795a73ec08a61fea0b5cc4544f2 Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 6 Oct 2025 17:51:17 +0200 Subject: [PATCH 026/275] fix handling of CSI measurement reports at L2 UE --- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 12 +++++-- openair2/LAYER2/NR_MAC_UE/main_ue_nr.c | 2 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 33 +++++++++++--------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index b7ebd5a651..f8b9f18c11 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -431,6 +431,14 @@ typedef struct { float_t ssb_sinr_dB; } NR_SSB_meas_t; +typedef struct { + int rsrp_dBm; + uint8_t ri; + uint16_t i1; + uint8_t i2; + uint8_t cqi; +} NR_CSIRS_meas_t; + typedef enum ta_type { no_ta = 0, adjustment_ta, @@ -601,9 +609,6 @@ typedef struct NR_UE_MAC_INST_s { nr_csi_report_t csi_report_template[MAX_CSI_REPORTCONFIG]; - /// measurements from SS or CSI-RS - fapi_nr_l1_measurements_t l1_measurements; - //// FAPI-like interface message fapi_nr_ul_config_request_t *ul_config_request; fapi_nr_dl_config_request_t *dl_config_request; @@ -633,6 +638,7 @@ typedef struct NR_UE_MAC_INST_s { int ssb_start_subcarrier; NR_SSB_meas_t ssb_measurements[MAX_NB_SSB]; + NR_CSIRS_meas_t csirs_measurements; dci_pdu_rel15_t def_dci_pdu_rel15[NR_MAX_SLOTS_PER_FRAME][8]; diff --git a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c index 1f35df5c4b..c78d2217b4 100644 --- a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c +++ b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c @@ -307,7 +307,7 @@ void release_mac_configuration(NR_UE_MAC_INST_t *mac, NR_UE_MAC_reset_cause_t ca release_ul_BWP(mac, i); memset(&mac->ssb_measurements, 0, sizeof(mac->ssb_measurements)); - memset(&mac->l1_measurements, 0, sizeof(mac->l1_measurements)); + memset(&mac->csirs_measurements, 0, sizeof(mac->csirs_measurements)); memset(&mac->ul_time_alignment, 0, sizeof(mac->ul_time_alignment)); for (int i = mac->TAG_list.count; i > 0 ; i--) asn_sequence_del(&mac->TAG_list, i - 1, 1); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 7818b08362..28e7b7d42b 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -1508,13 +1508,18 @@ nr_dci_format_t nr_ue_process_dci_indication_pdu(NR_UE_MAC_INST_t *mac, frame_t void nr_ue_process_l1_measurements(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_l1_measurements_t *l1_measurements) { - LOG_D(NR_MAC, "(%d.%d) Received CSI-RS measurements\n", frame, slot); - memcpy(&mac->l1_measurements, l1_measurements, sizeof(*l1_measurements)); + LOG_D(NR_MAC, "(%d.%d) Received measurements from L1\n", frame, slot); bool csi_meas = l1_measurements->meas_type == NFAPI_NR_CSI_MEAS; if (!csi_meas && !l1_measurements->is_neighboring_cell) { int ssb_index = l1_measurements->ssb_index; mac->ssb_measurements[ssb_index].ssb_rsrp_dBm = l1_measurements->rsrp_dBm; mac->ssb_measurements[ssb_index].ssb_sinr_dB = l1_measurements->sinr_dB; + } else if (csi_meas) { + mac->csirs_measurements.rsrp_dBm = l1_measurements->rsrp_dBm; + mac->csirs_measurements.i1 = l1_measurements->i1; + mac->csirs_measurements.i2 = l1_measurements->i2; + mac->csirs_measurements.cqi = l1_measurements->cqi; + mac->csirs_measurements.ri = l1_measurements->rank_indicator; } nr_mac_rrc_meas_ind_ue(mac->ue_id, l1_measurements->gNB_index, @@ -3026,28 +3031,28 @@ static csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac, AssertFatal(csi_report, "Couldn't find CSI report with ID %ld\n", csi_reportconfig->reportConfigId); int cri_bitlen = csi_report->csi_meas_bitlen.cri_bitlen; int ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen; - int pmi_x1_bitlen = csi_report->csi_meas_bitlen.pmi_x1_bitlen[mac->l1_measurements.rank_indicator]; - int pmi_x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[mac->l1_measurements.rank_indicator]; - int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[mac->l1_measurements.rank_indicator]; + int pmi_x1_bitlen = csi_report->csi_meas_bitlen.pmi_x1_bitlen[mac->csirs_measurements.ri]; + int pmi_x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[mac->csirs_measurements.ri]; + int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[mac->csirs_measurements.ri]; int padding_bitlen = 0; // TODO: Improvements will be needed to cri_bitlen>0 and pmi_x1_bitlen>0 if (mapping_type == ON_PUSCH) { p1_bits = cri_bitlen + ri_bitlen + cqi_bitlen; p2_bits = pmi_x1_bitlen + pmi_x2_bitlen; temp_payload_1 = (0/*mac->csi_measurements.cri*/ << (cqi_bitlen + ri_bitlen)) | - (mac->l1_measurements.rank_indicator << cqi_bitlen) | - (mac->l1_measurements.cqi); - temp_payload_2 = (mac->l1_measurements.i1 << pmi_x2_bitlen) | - mac->l1_measurements.i2; + (mac->csirs_measurements.ri << cqi_bitlen) | + (mac->csirs_measurements.cqi); + temp_payload_2 = (mac->csirs_measurements.i1 << pmi_x2_bitlen) | + mac->csirs_measurements.i2; } else { p1_bits = nr_get_csi_bitlen(csi_report); padding_bitlen = p1_bits - (cri_bitlen + ri_bitlen + pmi_x1_bitlen + pmi_x2_bitlen + cqi_bitlen); temp_payload_1 = (0/*mac->csi_measurements.cri*/ << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen + ri_bitlen)) | - (mac->l1_measurements.rank_indicator << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen)) | - (mac->l1_measurements.i1 << (cqi_bitlen + pmi_x2_bitlen)) | - (mac->l1_measurements.i2 << (cqi_bitlen)) | - (mac->l1_measurements.cqi); + (mac->csirs_measurements.ri << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen)) | + (mac->csirs_measurements.i1 << (cqi_bitlen + pmi_x2_bitlen)) | + (mac->csirs_measurements.i2 << (cqi_bitlen)) | + (mac->csirs_measurements.cqi); } temp_payload_1 = reverse_bits(temp_payload_1, p1_bits); @@ -3106,7 +3111,7 @@ static csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac, } // TODO: Improvements will be needed to cri_ssbri_bitlen>0 - temp_payload = get_rsrp_index(mac->l1_measurements.rsrp_dBm); + temp_payload = get_rsrp_index(mac->csirs_measurements.rsrp_dBm); temp_payload = reverse_bits(temp_payload, n_bits); LOG_D(NR_MAC, "cri_ssbri_bitlen = %d\n", cri_ssbri_bitlen); From df3a768811a4d340b8cf3491330e9d140077179c Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 7 Oct 2025 15:36:28 +0200 Subject: [PATCH 027/275] fix updating nrofReportedRS after reconfiguration --- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index a2b7c26678..da436142ec 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -3867,8 +3867,15 @@ void update_cellGroupConfig(NR_CellGroupConfig_t *cellGroupConfig, NR_CSI_MeasConfig_t *csi_MeasConfig = spCellConfigDedicated->csi_MeasConfig->choice.setup; for (int report = 0; report < csi_MeasConfig->csi_ReportConfigToAddModList->list.count; report++) { NR_CSI_ReportConfig_t *csirep = csi_MeasConfig->csi_ReportConfigToAddModList->list.array[report]; - if(csirep->codebookConfig) + if (csirep->codebookConfig) config_csi_codebook(&configuration->pdsch_AntennaPorts, *pdsch_servingcellconfig->ext1->maxMIMO_Layers, csirep->codebookConfig); + if (csirep->groupBasedBeamReporting.present == NR_CSI_ReportConfig__groupBasedBeamReporting_PR_disabled + && csirep->groupBasedBeamReporting.choice.disabled + && csirep->groupBasedBeamReporting.choice.disabled->nrofReportedRS) + *csirep->groupBasedBeamReporting.choice.disabled->nrofReportedRS = config_nrofReportedRS(uecap, + get_ssb_bitmap(scc), + scc, + configuration->max_num_rsrp); } NR_UplinkConfig_t *uplinkConfig = spCellConfigDedicated->uplinkConfig; From 5b5b9b3a300ba56848ebd531cab965320057796f Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Tue, 21 Oct 2025 15:02:01 +0200 Subject: [PATCH 028/275] CI: remove nssai_sd UE config when default value is used The SD value defaults to 0xffffff (16777215 in decimal) when not explicitly set on the UE. --- ci-scripts/conf_files/nrue.band78.106prb.l2sim.conf | 1 - ci-scripts/conf_files/ue.sa.conf | 1 - ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ci-scripts/conf_files/nrue.band78.106prb.l2sim.conf b/ci-scripts/conf_files/nrue.band78.106prb.l2sim.conf index 45d192dc04..0dfd31a789 100644 --- a/ci-scripts/conf_files/nrue.band78.106prb.l2sim.conf +++ b/ci-scripts/conf_files/nrue.band78.106prb.l2sim.conf @@ -14,7 +14,6 @@ key = "fec86ba6eb707ed08905757b1bb44b8f"; opc= "C42449363BBAD02B66D16BC975D77CC1"; dnn= "oai"; nssai_sst=1; -nssai_sd=16777215; } MACRLCs = ( diff --git a/ci-scripts/conf_files/ue.sa.conf b/ci-scripts/conf_files/ue.sa.conf index 9101c7e8e9..dc744fb732 100644 --- a/ci-scripts/conf_files/ue.sa.conf +++ b/ci-scripts/conf_files/ue.sa.conf @@ -4,7 +4,6 @@ uicc0 = { opc= "c42449363bbad02b66d16bc975d77cc1"; dnn= "oai"; nssai_sst=1; - nssai_sd=66051; } thread-pool = "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1" diff --git a/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml index aac787a916..9fb80ad065 100644 --- a/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml +++ b/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml @@ -17,7 +17,7 @@ services: #entrypoint: /bin/bash -c "sleep infinity" environment: TZ: Europe/Paris - USE_ADDITIONAL_OPTIONS: --usrp-args type=n3xx,name=ni-n3xx-31641B9,addr=192.168.10.2 -r 106 --numerology 1 --band 78 -C 3425010000 --ssb 509 --ue-fo-compensation --ue-rxgain 65 --tune-offset 20000000 -A 90 --log_config.global_log_options level,nocolor,time --uicc0.imsi 001020000000111 --uicc0.nssai_sd 16777215 + USE_ADDITIONAL_OPTIONS: --usrp-args type=n3xx,name=ni-n3xx-31641B9,addr=192.168.10.2 -r 106 --numerology 1 --band 78 -C 3425010000 --ssb 509 --ue-fo-compensation --ue-rxgain 65 --tune-offset 20000000 -A 90 --log_config.global_log_options level,nocolor,time --uicc0.imsi 001020000000111 volumes: - ../../conf_files/ue.sa.conf:/opt/oai-nr-ue/etc/nr-ue.conf healthcheck: From a0f58d7104614ca8d6c5e97fdee50292706a5b44 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Tue, 21 Oct 2025 15:22:10 +0200 Subject: [PATCH 029/275] CI: Update CI conf files Update based on !3645 Note: For some test configurations we still need to provide ra_ResponseWindow, for more details, check commit message of 41d14b571c80c43a77078e83d8c115547b8748c5 --- .../conf_files/gnb.sa.band77.273prb.fhi72.4x4.2L-metanoia.conf | 3 +-- ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ci-scripts/conf_files/gnb.sa.band77.273prb.fhi72.4x4.2L-metanoia.conf b/ci-scripts/conf_files/gnb.sa.band77.273prb.fhi72.4x4.2L-metanoia.conf index 4be45f6e19..41e9974caf 100644 --- a/ci-scripts/conf_files/gnb.sa.band77.273prb.fhi72.4x4.2L-metanoia.conf +++ b/ci-scripts/conf_files/gnb.sa.band77.273prb.fhi72.4x4.2L-metanoia.conf @@ -106,8 +106,7 @@ gNBs = pucchGroupHopping = 0; hoppingId = 40; p0_nominal = -90; -# ssb_PositionsInBurs_BitmapPR -# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_Bitmap = 1; # ssb_periodicityServingCell # 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 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 6c03a02443..f3b75c1471 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 @@ -150,7 +150,6 @@ services: --telnetsrv --telnetsrv.listenaddr 192.168.71.171 --telnetsrv.shrmod ci --rfsimulator.serveraddr 192.168.71.190 - --gNBs.[0].servingCellConfigCommon.[0].ra_ResponseWindow 5 ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 depends_on: - oai-cucp-0 From 70d23e43df436768b5e8d98a83713a063a67c89c Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 23 Apr 2025 20:24:05 +0200 Subject: [PATCH 030/275] Fetch sdap configuration from sdap-config and store in new struct sdap_config_t This function is processing the NR_SDAP_Config_t received (from RRC) and is storing the SDAP configuration in a structure of type sdap_config_t. --- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 34 ++++++++++++++++++++++++++ openair2/SDAP/nr_sdap/nr_sdap_entity.h | 14 +++++++++++ 2 files changed, 48 insertions(+) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 6860fba6e6..4c02318e57 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -653,6 +653,40 @@ bool nr_sdap_delete_ue_entities(ue_id_t ue_id) return ret; } +/** @brief This function gets the relevant SDAP config from the received SDAP-Config */ +sdap_config_t nr_sdap_get_config(const int is_gnb, const NR_SDAP_Config_t *sdap_Config, const int drb_id) +{ + DevAssert(sdap_Config); + sdap_config_t sdapConfig = {0}; + sdapConfig.drb_id = drb_id; + sdapConfig.sdap_rx = is_sdap_rx(is_gnb, sdap_Config); + sdapConfig.sdap_tx = is_sdap_tx(is_gnb, sdap_Config); + sdapConfig.defaultDRB = sdap_Config->defaultDRB; + // 3GPP TS 38.331 The network sets sdap-HeaderUL to present if the field defaultDRB is set to true + if (sdapConfig.defaultDRB && (sdap_Config->sdap_HeaderUL != NR_SDAP_Config__sdap_HeaderUL_present)) + LOG_D(SDAP, "Received SDAP-Config with defaultDRB but sdap-HeaderUL is not present\n"); + if (sdap_Config->mappedQoS_FlowsToAdd) { + sdapConfig.mappedQFIs2AddCount = sdap_Config->mappedQoS_FlowsToAdd->list.count; + DevAssert(sdapConfig.mappedQFIs2AddCount <= SDAP_MAX_QFI); + LOG_D(SDAP, "DRB %d: mapped QFIs = %d \n", sdapConfig.drb_id, sdapConfig.mappedQFIs2AddCount); + for (int i = 0; i < sdapConfig.mappedQFIs2AddCount; i++){ + DevAssert(i < SDAP_MAX_QFI); + sdapConfig.mappedQFIs2Add[i] = *sdap_Config->mappedQoS_FlowsToAdd->list.array[i]; + LOG_D(SDAP, "Captured mappedQoS_FlowsToAdd[%d] from RRC: %ld\n", i, sdapConfig.mappedQFIs2Add[i]); + } + } + sdapConfig.pdusession_id = sdap_Config->pdu_Session; + if (sdap_Config->mappedQoS_FlowsToRelease) { + sdapConfig.mappedQFIs2ReleaseCount = sdap_Config->mappedQoS_FlowsToRelease->list.count; + DevAssert(sdapConfig.mappedQFIs2ReleaseCount <= SDAP_MAX_QFI); + for (int i = 0; i < sdapConfig.mappedQFIs2ReleaseCount; i++) { + DevAssert(i < SDAP_MAX_QFI); + sdapConfig.mappedQFIs2Release[i] = *sdap_Config->mappedQoS_FlowsToRelease->list.array[i]; + } + } + return sdapConfig; +} + /** * @brief SDAP Entity reconfiguration at UE according to TS 37.324 * and triggered by RRC reconfiguration events according to clause 5.3.5.6.5 of TS 38.331. diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index 3a42c6c076..3e3d95c3db 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -77,6 +77,18 @@ typedef struct qfi2drb_s { void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); +typedef struct { + int pdusession_id; + int drb_id; + bool sdap_rx; + bool sdap_tx; + bool defaultDRB; + NR_QFI_t mappedQFIs2Add[SDAP_MAX_QFI]; + uint8_t mappedQFIs2AddCount; + NR_QFI_t mappedQFIs2Release[SDAP_MAX_QFI]; + uint8_t mappedQFIs2ReleaseCount; +} sdap_config_t; + typedef struct nr_sdap_entity_s { ue_id_t ue_id; rb_id_t default_drb; @@ -180,6 +192,8 @@ nr_sdap_entity_t *new_nr_sdap_entity(int is_gnb, /* Entity Handling Related Functions */ nr_sdap_entity_t *nr_sdap_get_entity(ue_id_t ue_id, int pdusession_id); +sdap_config_t nr_sdap_get_config(const int is_gnb, const NR_SDAP_Config_t *sdap_Config, const int drb_id); + void nr_sdap_release_drb(ue_id_t ue_id, int drb_id, int pdusession_id); /** From e3aa91dec8acdc92c1111b5e442684ec8a91a660 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 22 Oct 2025 09:24:54 +0200 Subject: [PATCH 031/275] remove unused ulsch measurements at gNB --- .../PHY/NR_ESTIMATION/nr_measurements_gNB.c | 71 ------------------- openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h | 6 -- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 7 +- openair1/PHY/defs_gNB.h | 8 --- 4 files changed, 1 insertion(+), 91 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c index 2e6e4a54a6..fc6f81500a 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c +++ b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c @@ -188,74 +188,3 @@ void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_sy } } } - -// Scope: This function computes the UL SNR from the UL channel estimates -// -// Todo: -// - averaging IIR filter for RX power and noise -void nr_gnb_measurements(PHY_VARS_gNB *gNB, - NR_gNB_ULSCH_t *ulsch, - NR_gNB_PUSCH *pusch_vars, - unsigned char symbol, - uint8_t nrOfLayers) -{ - uint32_t rx_power_tot = 0; - unsigned short rx_power_avg_dB; - unsigned short rx_power_tot_dB; - RU_t *ru = gNB->RU_list[0]; - double rx_gain, rx_gain_offset; - if (ru && ru->rfdevice.openair0_cfg) { - rx_gain = ru->rfdevice.openair0_cfg->rx_gain[0]; - rx_gain_offset = ru->rfdevice.openair0_cfg->rx_gain_offset[0]; - } else if (ru && ru->ifdevice.openair0_cfg) { - rx_gain = ru->ifdevice.openair0_cfg->rx_gain[0]; - rx_gain_offset = ru->ifdevice.openair0_cfg->rx_gain_offset[0]; - } else { - LOG_W(NR_PHY, "no device for rx gain, take arbitrary gain = 1)\n"); - rx_gain = 1; - rx_gain_offset = 0; - } - PHY_MEASUREMENTS_gNB *meas = &gNB->measurements; - NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - int ch_offset = fp->ofdm_symbol_size * symbol; - int N_RB_UL = ulsch->harq_process->ulsch_pdu.rb_size; - ulsch_measurements_gNB *ulsch_measurements = &ulsch->ulsch_measurements; - - int rx_power[fp->nb_antennas_rx]; - int rx_spatial_power[nrOfLayers][fp->nb_antennas_rx]; - for (int aarx = 0; aarx < fp->nb_antennas_rx; aarx++){ - rx_power[aarx] = 0; - - for (int aatx = 0; aatx < nrOfLayers; aatx++){ - rx_spatial_power[aatx][aarx] = - (signal_energy_nodc((c16_t*)&pusch_vars->ul_ch_estimates[aatx * fp->nb_antennas_rx + aarx][ch_offset], - N_RB_UL * NR_NB_SC_PER_RB)); - - if (rx_spatial_power[aatx][aarx] < 0) { - rx_spatial_power[aatx][aarx] = 0; - } - - rx_power[aarx] += rx_spatial_power[aatx][aarx]; - } - LOG_D(PHY, "[RNTI %04x] RX power in antenna %d = %d\n", ulsch->rnti, aarx, rx_power[aarx]); - - rx_power_tot += rx_power[aarx]; - } - - rx_power_tot_dB = (unsigned short)dB_fixed(rx_power_tot); - rx_power_avg_dB = rx_power_tot_dB; - - ulsch_measurements->wideband_cqi_tot = dB_fixed2(rx_power_tot, meas->n0_power_tot); - ulsch_measurements->rx_rssi_dBm = - rx_power_avg_dB + 30 - SQ15_SQUARED_NORM_FACTOR_DB - (rx_gain - rx_gain_offset) - dB_fixed(fp->ofdm_symbol_size); - - LOG_D(PHY, - "[RNTI %04x] RSSI %d dBm/RE, RSSI (digital) %d dB (N_RB_UL %d), WBand CQI tot %d dB, N0 Power tot %d, RX Power tot %d\n", - ulsch->rnti, - ulsch_measurements->rx_rssi_dBm, - rx_power_avg_dB, - N_RB_UL, - ulsch_measurements->wideband_cqi_tot, - meas->n0_power_tot, - rx_power_tot); -} diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index 04377db1b8..7e0a2a97bd 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -59,12 +59,6 @@ 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 nr_gnb_measurements(PHY_VARS_gNB *gNB, - NR_gNB_ULSCH_t *ulsch, - NR_gNB_PUSCH *pusch_vars, - unsigned char symbol, - uint8_t nrOfLayers); - int nr_est_timing_advance_srs(const NR_DL_FRAME_PARMS *frame_parms, const c16_t srs_estimated_channel_time[][frame_parms->ofdm_symbol_size]); diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 28f3e2cc67..3a32deab88 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -1188,12 +1188,7 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, pusch_ch_est_dmrs_pos_slot_mem); nvar += nvar_tmp; } - // measure the SNR from the channel estimation - nr_gnb_measurements(gNB, - &gNB->ulsch[ulsch_id], - pusch_vars, - symbol, - rel15_ul->nrOfLayers); + allocCast2D(n0_subband_power, unsigned int, gNB->measurements.n0_subband_power, diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index fb549d4734..ebf459402e 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -215,13 +215,6 @@ static inline int crcType(int nbSeg, int len) return len > NR_MAX_PDSCH_TBS ? CRC24_A : CRC16; } -typedef struct { - //! estimated rssi (dBm) - int rx_rssi_dBm; - /// Wideband CQI (sum of all RX antennas, in dB) - char wideband_cqi_tot; -} ulsch_measurements_gNB; - typedef struct { uint32_t frame; uint32_t slot; @@ -243,7 +236,6 @@ typedef struct { /// 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; - ulsch_measurements_gNB ulsch_measurements; } NR_gNB_ULSCH_t; typedef struct { From c3eaef704f37d04eee5fe344d160db1045a2c186 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 25 Sep 2025 18:46:25 +0200 Subject: [PATCH 032/275] Add monolithic N2 handover test pipeline with B210 and Quectel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add XML pipeline for N2 handover testing - Add Docker compose for two monolithic gNBs - Features: * Two monolithic gNBs (gNB1, gNB2) with proper CU+DU parameters * Gradual attenuation control using RC4DAT attenuator (5s duration) * N2 handover sequence: gNB1 โ†’ gNB2 โ†’ gNB1 * Proper channel mapping: ch1-2 for gNB1, ch3-4 for gNB2 * Telnet server configuration for CI commands * Handover-specific neighbor configuration * Clean test state management with attenuator reset --- .../gnb.sa.band77.51prb.usrpb200.n2-ho.conf | 237 ++++++++++++++++++ .../container_sa_n2_ho_b210_quectel.xml | 230 +++++++++++++++++ .../5g_sa_n2_ho_b210/docker-compose.yml | 61 +++++ 3 files changed, 528 insertions(+) create mode 100644 ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf create mode 100644 ci-scripts/xml_files/container_sa_n2_ho_b210_quectel.xml create mode 100644 ci-scripts/yaml_files/5g_sa_n2_ho_b210/docker-compose.yml diff --git a/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf b/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf new file mode 100644 index 0000000000..dc24284c18 --- /dev/null +++ b/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf @@ -0,0 +1,237 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 001; mnc = 03; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); + + // Include neighbour configuration for N2 handover + @include "neighbour-config-ho.conf" + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3610.56 MHz + absoluteFrequencySSB = 679104; + dl_frequencyBand = 77; + # this is 3599.94 MHz + dl_absoluteFrequencyPointA = 678388; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 51; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 13750; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 77; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 51; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 13750; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#one (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; + + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ({ ipv4 = "172.21.6.109"; }); + + + NETWORK_INTERFACES : + { + GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.19.98"; + GNB_IPV4_ADDRESS_FOR_NGU = "172.21.19.98"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 200; + pucch_TargetSNRx10 = 200; + ul_prbblack_SNR_threshold = 10; +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; + pucch0_dtx_threshold = 100; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 +} +); + +RUs = ( +{ + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0; + att_rx = 0; + bands = [77]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 114; + eNB_instances = [0]; + clock_src = "external"; + time_src = "external"; +} +); + + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : +{ + global_log_level ="info"; + hw_log_level ="info"; + phy_log_level ="info"; + mac_log_level ="info"; + rlc_log_level ="info"; + pdcp_log_level ="info"; + rrc_log_level ="info"; + ngap_log_level ="debug"; + f1ap_log_level ="debug"; +}; + diff --git a/ci-scripts/xml_files/container_sa_n2_ho_b210_quectel.xml b/ci-scripts/xml_files/container_sa_n2_ho_b210_quectel.xml new file mode 100644 index 0000000000..b67522b220 --- /dev/null +++ b/ci-scripts/xml_files/container_sa_n2_ho_b210_quectel.xml @@ -0,0 +1,230 @@ + + + TEST-MONO-N2-HO-B210 + Monolithic N2 Handover 20 MHz TDD SA + tasks + + 000001 + 000002 + 000023 + 000003 + 000004 + 000005 + 000006 + 000007 + 000008 + 000009 + 000010 + 000011 + 000012 + 000013 + 000014 + 000015 + 000017 + 000018 + 000019 + 000016 + 000020 + 000021 + 000022 + + + + + DeployCoreNetwork + Initialize 5G Core + oc-cn5g-00103-ho + + + + Pull_Local_Registry + Pull Images from Local Registry + oai-gnb + groot + + + + Pull_Local_Registry + Pull Images from Local Registry + oai-gnb + rocket + + + + Create_Workspace + Create new Workspace for groot + groot + + + + Create_Workspace + Create new Workspace on rocket + rocket + + + + Custom_Command + Set initial attenuation on target gNB + rocket + python3 %%workspace%%/ci-scripts/attenuatorctl.py -s 1,2 0 -s 3,4 15 + + + + Deploy_Object + Deploy Monolithic gNB-PCI0 (TDD/Band77/20MHz/B210) in a container + ci-scripts/yaml_files/5g_sa_n2_ho_b210 + groot + oai-gnb-pci0 + + + + Deploy_Object + Deploy Monolithic gNB-PCI1 (TDD/Band77/20MHz/B210) in a container + ci-scripts/yaml_files/5g_sa_n2_ho_b210 + rocket + oai-gnb-pci1 + + + + Attach_UE + Attach Quectel + raspix + + + + Ping + Ping: 100pings in 20sec + raspix + oc-cn5g-00103-ho + -c 100 -i 0.2 + 5 + + + + IdleSleep + Sleep + 5 + + + + Custom_Command + Gradually attenuate gNB-PCI0 (ch1-2=15dB) and gNB-PCI1 (ch3-4=0dB) over 5s to trigger N2 Handover to gNB-PCI1 + rocket + python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 15 -r 3,4 0 -d 5 + + + + Ping + Ping: 50pings in 10sec + raspix + oc-cn5g-00103-ho + -c 50 -i 0.2 + 5 + + + + Custom_Command + UE connected to gNB-PCI1? + rocket + echo ci fetch_du_by_ue_id 1 | ncat 127.0.0.1 9090 | grep "3585" + + + + Custom_Command + Gradually attenuate gNB-PCI0 (ch1-2=0dB) and gNB-PCI1 (ch3-4=15dB) over 5s to trigger N2 Handover back to gNB-PCI0 + rocket + python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 15 -d 5 + + + + Ping + Ping: 50pings in 10sec + raspix + oc-cn5g-00103-ho + -c 50 -i 0.2 + 5 + + + + Custom_Command + UE connected to gNB-PCI0? + groot + echo ci fetch_du_by_ue_id 1 | ncat 127.0.0.1 9090 | grep "3584" + + + + Custom_Command + true + Reset all attenuators to 0dB + rocket + python3 %%workspace%%/ci-scripts/attenuatorctl.py -s 1-4 0 + + + + Detach_UE + true + Detach Quectel + raspix + + + + Undeploy_Object + true + Undeploy Monolithic gNB-PCI0 + groot + ci-scripts/yaml_files/5g_sa_n2_ho_b210 + + + + Undeploy_Object + true + Undeploy Monolithic gNB-PCI1 + rocket + ci-scripts/yaml_files/5g_sa_n2_ho_b210 + + + + Clean_Test_Server_Images + true + Clean Test Images on Test Server + groot + oai-gnb + + + + Clean_Test_Server_Images + true + Clean Test Images on Test Server + rocket + oai-gnb + + + + UndeployCoreNetwork + true + Terminate 5G Core + oc-cn5g-00103-ho + + + diff --git a/ci-scripts/yaml_files/5g_sa_n2_ho_b210/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_n2_ho_b210/docker-compose.yml new file mode 100644 index 0000000000..6704b434db --- /dev/null +++ b/ci-scripts/yaml_files/5g_sa_n2_ho_b210/docker-compose.yml @@ -0,0 +1,61 @@ +services: + oai-gnb-pci0: + image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + container_name: oai-gnb-pci0 + cap_drop: + - ALL + cap_add: + - SYS_NICE + - IPC_LOCK + ulimits: + core: -1 # for core dumps + environment: + USE_B2XX: 'yes' + USE_ADDITIONAL_OPTIONS: --RUs.[0].sdr_addrs serial=30AD30F + --telnetsrv --telnetsrv.shrmod ci + --log_config.global_log_options level,nocolor,utc_time,line_num,function + ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 + devices: + - /dev/bus/usb/:/dev/bus/usb/ + volumes: + - ../../conf_files/neighbour-config-ho.conf:/opt/oai-gnb/etc/neighbour-config-ho.conf + - ../../conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf:/opt/oai-gnb/etc/gnb.conf + network_mode: "host" + healthcheck: + test: /bin/bash -c "ps aux | grep -v grep | grep -c softmodem" + interval: 10s + timeout: 5s + retries: 5 + + oai-gnb-pci1: + image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + container_name: oai-gnb-pci1 + cap_drop: + - ALL + cap_add: + - SYS_NICE + - IPC_LOCK + ulimits: + core: -1 # for core dumps + environment: + USE_B2XX: 'yes' + USE_ADDITIONAL_OPTIONS: --RUs.[0].sdr_addrs serial=31F8010 + --gNBs.[0].nr_cellid 11111111 + --gNBs.[0].gNB_ID 0xe01 + --gNBs.[0].servingCellConfigCommon.[0].physCellId 1 + --gNBs.[0].servingCellConfigCommon.[0].ssb_PositionsInBurst_Bitmap 2 + --gNBs.[0].NETWORK_INTERFACES.GNB_IPV4_ADDRESS_FOR_NG_AMF 172.21.19.99 + --gNBs.[0].NETWORK_INTERFACES.GNB_IPV4_ADDRESS_FOR_NGU 172.21.19.99 + --telnetsrv --telnetsrv.shrmod ci + --log_config.global_log_options level,nocolor,utc_time,line_num,function + devices: + - /dev/bus/usb/:/dev/bus/usb/ + volumes: + - ../../conf_files/neighbour-config-ho.conf:/opt/oai-gnb/etc/neighbour-config-ho.conf + - ../../conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf:/opt/oai-gnb/etc/gnb.conf + network_mode: "host" + healthcheck: + test: /bin/bash -c "ps aux | grep -v grep | grep -c softmodem" + interval: 10s + timeout: 5s + retries: 5 From 7ec646a8d266eef995d835b85d1d59fa0b9beace Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Mon, 20 Oct 2025 10:19:51 +0200 Subject: [PATCH 033/275] NGAP: fix PLMN/cell location for InitialUE and UplinkNAS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit is removing the storage of selected PLMN in NGAP UE context. We store the Serving PLMN in the RRC UE context instead. The Selected PLMN is the PLMN that the UE chose (or was directed to) for registration and it is sent in the RRCSetupComplete. This is different from the selected PLMN mentioned in the NG INITIAL UE MESSAGE for network sharing. Selected PLMN: determines which core network and AMF the UE is registered with GUAMI = Identifies the serving AMF and includes the PLMN of that AMF Serving PLMN: the PLMN that is currently serving the UE (e.g. contained in GUAMI) The AMFโ€™s PLMN may match the selected PLMN, or not (especially in network sharing or roaming). Regardless, the UE always have a Serving PLMN. Initial UE message does not have a TAI IE per se, but TAI is a mandatory member of the mandatory UserLocationInformation CHOICE. NR CGI is also mandatory and carried inside UserLocationInformation: in network sharing, the PLMN ID in TAI takes on additional significance: RAN is shared but CN are different, so in this case PLMN in TAI is needed for the AMF route the message to the correct operatorโ€™s core. If network sharing applies (e.g., shared RAN between PLMN-A and PLMN-B): the UE or network may select PLMN-B instead, and that PLMN identity is included in the Initial UE Messageโ€™s TAI IE. Conclusion: 1) UE always sends the selected PLMN during the registration, regardless of whether it is shared or not. 2) Selected PLMN Identity is an actual IE sent in the RRCSetupComplete while in NGAP there's no Selected PLMN Identity IE, only PLMN Identity IEs in both TAI and NR CGI. It is a subtle difference. 3) The serving PLMN of the UE needs to be stored in RRC UE Context for later use in NAS procedures. e.g. at initial registration it matches the Selected PLMN 4) No need to store PLMN in NGAP UE Context, params are passed by RRC 5) TAI and NR CGI, which contain the PLMN identity, are mandatory in UserLocationInformation IE CHOICE, and should reflect current status Changes - Initial UE Message: pass UE-selected PLMN and NR cell id from RRC to NGAP - Uplink NAS Transport: use PLMN, TAC, and cell id provided by the message; - Build NR CGI from gNB ID + cell id - Store UE-selected PLMN only in RRC UE context at NAS_FIRST_REQ for later Uplink NAS, stop storing PLMN in NGAP UE context --- openair2/COMMON/ngap_messages_types.h | 6 ++++++ openair2/RRC/NR/nr_rrc_defs.h | 2 ++ openair2/RRC/NR/rrc_gNB_NGAP.c | 23 +++++++++++++++------- openair3/NGAP/ngap_gNB_nas_procedures.c | 26 ++++++++++++------------- openair3/NGAP/ngap_gNB_ue_context.h | 5 ----- 5 files changed, 37 insertions(+), 25 deletions(-) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index 2c4b0f8a0a..5be5e316ef 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -421,6 +421,8 @@ typedef struct ngap_nas_first_req_s { /* PLMN: Selected PLMN Identity (optional) * User Location Information (mandatory) */ plmn_id_t plmn; + // NR Cell ID for NR CGI (mandatory) + uint32_t nr_cell_id; // RRC Establishment Cause (mandatory) ngap_rrc_establishment_cause_t establishment_cause; // NAS-PDU (mandatory) @@ -434,6 +436,10 @@ typedef struct ngap_uplink_nas_s { uint32_t gNB_ue_ngap_id; /* NAS pdu */ byte_array_t nas_pdu; + /* UserLocationInformation (mandatory) */ + plmn_id_t plmn; // CGI and TAI + uint32_t nr_cell_id; // CGI + uint32_t tac; // TAI } ngap_uplink_nas_t; typedef struct target_cell_id_s { diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index e76802338c..eaeacae5ab 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -209,6 +209,8 @@ typedef struct gNB_RRC_UE_s { uint64_t amf_ue_ngap_id; // Globally Unique AMF Identifier nr_guami_t ue_guami; + // Serving PLMN of the UE + plmn_id_t serving_plmn; ngap_security_capabilities_t security_capabilities; //NSA block diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 7d75384a9b..90b51ba118 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -223,17 +223,22 @@ void rrc_gNB_send_NGAP_NAS_FIRST_REQ(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, NR_RRC req->nas_pdu = create_byte_array(rrcSetupComplete->dedicatedNAS_Message.size, rrcSetupComplete->dedicatedNAS_Message.buf); /* Selected PLMN Identity (Optional) - * selectedPLMN-Identity in RRCSetupComplete: Index of the PLMN selected by the UE from the plmn-IdentityInfoList (SIB1) - * Selected PLMN Identity in INITIAL UE MESSAGE: Indicates the selected PLMN id for the non-3GPP access.*/ - if (rrcSetupComplete->selectedPLMN_Identity > rrc->configuration.num_plmn) { + * selectedPLMN-Identity in RRCSetupComplete: Index of the PLMN selected by the UE from the plmn-IdentityInfoList (SIB1) */ + int idx = rrcSetupComplete->selectedPLMN_Identity - 1; // Convert 1-based PLMN Identity IE to 0-based index + if (idx < 0 || idx >= rrc->configuration.num_plmn) { LOG_E(NGAP, - "Failed to send Initial UE Message: selected PLMN (%ld) identity is out of bounds (%d)\n", - rrcSetupComplete->selectedPLMN_Identity, + "Failed to send Initial UE Message: selected PLMN index (%d) is out of bounds [0..%d)\n", + idx, rrc->configuration.num_plmn); return; } - int selected_plmn_identity = rrcSetupComplete->selectedPLMN_Identity - 1; // Convert 1-based PLMN Identity IE to 0-based index - req->plmn = rrc->configuration.plmn[selected_plmn_identity]; // Select from the stored list + req->plmn = rrc->configuration.plmn[idx]; // Select from the stored list + /* UE is connected to only one PLMN at a time: store this as the serving PLMN */ + UE->serving_plmn = req->plmn; + + // Cell ID (NR CGI) + req->nr_cell_id = UE->nr_cellid; + // PLMN (NR CGI and TAI) plmn_id_t *p = &req->plmn; LOG_I(NGAP, "Selected PLMN in the NG Initial UE Message: MCC=%03d MNC=%0*d\n", p->mcc, p->mnc_digit_length, p->mnc); @@ -721,6 +726,10 @@ void rrc_gNB_send_NGAP_UPLINK_NAS(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_ NGAP_UPLINK_NAS(msg_p).gNB_ue_ngap_id = UE->rrc_ue_id; NGAP_UPLINK_NAS(msg_p).nas_pdu.len = nas->size; NGAP_UPLINK_NAS(msg_p).nas_pdu.buf = buf; + /* Fill PLMN and location info: use serving PLMN of the UE */ + NGAP_UPLINK_NAS(msg_p).plmn = UE->serving_plmn; + NGAP_UPLINK_NAS(msg_p).nr_cell_id = rrc->nr_cellid; + NGAP_UPLINK_NAS(msg_p).tac = rrc->configuration.tac; itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p); } diff --git a/openair3/NGAP/ngap_gNB_nas_procedures.c b/openair3/NGAP/ngap_gNB_nas_procedures.c index cff9cd0b72..55cd7d61f4 100644 --- a/openair3/NGAP/ngap_gNB_nas_procedures.c +++ b/openair3/NGAP/ngap_gNB_nas_procedures.c @@ -181,7 +181,6 @@ int ngap_gNB_handle_nas_first_req(instance_t instance, ngap_nas_first_req_t *UEf .amf_ref = amf, .gNB_ue_ngap_id = UEfirstReq->gNB_ue_ngap_id, .gNB_instance = instance_p, - .selected_plmn_identity = UEfirstReq->plmn, }; // RAN UE NGAP ID (M) @@ -211,16 +210,14 @@ int ngap_gNB_handle_nas_first_req(instance_t instance, ngap_nas_first_req_t *UEf ie->value.choice.UserLocationInformation.present = NGAP_UserLocationInformation_PR_userLocationInformationNR; asn1cCalloc(ie->value.choice.UserLocationInformation.choice.userLocationInformationNR, userinfo_nr_p); - /* Set nRCellIdentity. default userLocationInformationNR */ - MACRO_GNB_ID_TO_CELL_IDENTITY(instance_p->gNB_id, - 0, // Cell ID - &userinfo_nr_p->nR_CGI.nRCellIdentity); + /* NR CGI: use gNB ID and Cell ID passed from RRC */ + MACRO_GNB_ID_TO_CELL_IDENTITY(instance_p->gNB_id, UEfirstReq->nr_cell_id, &userinfo_nr_p->nR_CGI.nRCellIdentity); - plmn_id_t *plmn = &ue_desc_p.selected_plmn_identity; + /* Use UE's selected PLMN for nR-CGI (from Initial UE Message) */ + plmn_id_t *plmn = &UEfirstReq->plmn; MCC_MNC_TO_TBCD(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &userinfo_nr_p->nR_CGI.pLMNIdentity); - /* In case of network sharing, - the selected PLMN is indicated by the PLMN Identity IE within the TAI IE */ + /* Set TAI - use UE's selected PLMN */ INT24_TO_OCTET_STRING(instance_p->tac, &userinfo_nr_p->tAI.tAC); MCC_MNC_TO_PLMNID(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &userinfo_nr_p->tAI.pLMNIdentity); } @@ -444,17 +441,20 @@ int ngap_gNB_nas_uplink(instance_t instance, ngap_uplink_nas_t *ngap_uplink_nas_ ie->value.choice.UserLocationInformation.present = NGAP_UserLocationInformation_PR_userLocationInformationNR; asn1cCalloc(ie->value.choice.UserLocationInformation.choice.userLocationInformationNR, userinfo_nr_p); - /* Set nRCellIdentity. default userLocationInformationNR */ + /* Set nRCellIdentity: use gNB ID and Cell ID */ MACRO_GNB_ID_TO_CELL_IDENTITY(ngap_gNB_instance_p->gNB_id, - 0, // Cell ID + ngap_uplink_nas_p->nr_cell_id, &userinfo_nr_p->nR_CGI.nRCellIdentity); - plmn_id_t *plmn = &ue_context_p->selected_plmn_identity; + + /* NR CGI */ + plmn_id_t *plmn = &ngap_uplink_nas_p->plmn; MCC_MNC_TO_TBCD(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &userinfo_nr_p->nR_CGI.pLMNIdentity); - /* Set TAI */ - INT24_TO_OCTET_STRING(ngap_gNB_instance_p->tac, &userinfo_nr_p->tAI.tAC); + /* TAI */ + INT24_TO_OCTET_STRING(ngap_uplink_nas_p->tac, &userinfo_nr_p->tAI.tAC); MCC_MNC_TO_PLMNID(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &userinfo_nr_p->tAI.pLMNIdentity); } + if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) { NGAP_ERROR("Failed to encode uplink NAS transport\n"); /* Encode procedure has failed... */ diff --git a/openair3/NGAP/ngap_gNB_ue_context.h b/openair3/NGAP/ngap_gNB_ue_context.h index 1022f2f4c3..8053e8f8c6 100644 --- a/openair3/NGAP/ngap_gNB_ue_context.h +++ b/openair3/NGAP/ngap_gNB_ue_context.h @@ -69,11 +69,6 @@ typedef struct ngap_gNB_ue_context_s { /* Reference to AMF data this UE is attached to */ struct ngap_gNB_amf_data_s *amf_ref; - /* Signaled by the UE in RRC Connection Setup Complete and used in NAS Uplink - * to route NAS messages correctly. 0-based, not 1-based as in TS 36.331 - * 6.2.2 RRC Connection Setup Complete! */ - plmn_id_t selected_plmn_identity; - /* Reference to gNB data this UE is attached to */ ngap_gNB_instance_t *gNB_instance; } ngap_gNB_ue_context_t; From 32eae0a0a8014e746abce7d24e3acb25f9fd006a Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 21 Oct 2025 10:37:56 +0200 Subject: [PATCH 034/275] Adopt plmn_id_t in nr_guami_t --- common/5g_platform_types.h | 4 +--- openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c | 12 +++++------- openair2/RRC/NR/rrc_gNB_NGAP.c | 4 +--- openair3/NGAP/ngap_common.c | 2 +- openair3/NGAP/ngap_gNB_nas_procedures.c | 12 ++++++------ openair3/NGAP/ngap_gNB_nnsf.c | 12 +++++------- 6 files changed, 19 insertions(+), 27 deletions(-) diff --git a/common/5g_platform_types.h b/common/5g_platform_types.h index 4ed7f33e47..7cae0afde7 100644 --- a/common/5g_platform_types.h +++ b/common/5g_platform_types.h @@ -37,9 +37,7 @@ typedef struct nssai_s { // Globally Unique AMF Identifier typedef struct nr_guami_s { - uint16_t mcc; - uint16_t mnc; - uint8_t mnc_len; + plmn_id_t plmn; uint8_t amf_region_id; uint16_t amf_set_id; uint8_t amf_pointer; diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c index a81a01d394..5d725f1df6 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c @@ -38,13 +38,11 @@ ue_id_e2sm_t fill_e2sm_gnb_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, __attr ue_id.gnb.amf_ue_ngap_id = auni < (1LL << 40) ? auni : 0; // Mandatory - //GUAMI 6.2.3.17 - ue_id.gnb.guami.plmn_id = (e2sm_plmn_t) { - .mcc = rrc_ue_context->ue_guami.mcc, - .mnc = rrc_ue_context->ue_guami.mnc, - .mnc_digit_len = rrc_ue_context->ue_guami.mnc_len - }; - + //GUAMI 6.2.3.17 + ue_id.gnb.guami.plmn_id = (e2sm_plmn_t){.mcc = rrc_ue_context->ue_guami.plmn.mcc, + .mnc = rrc_ue_context->ue_guami.plmn.mnc, + .mnc_digit_len = rrc_ue_context->ue_guami.plmn.mnc_digit_length}; + ue_id.gnb.guami.amf_region_id = rrc_ue_context->ue_guami.amf_region_id; ue_id.gnb.guami.amf_set_id = rrc_ue_context->ue_guami.amf_set_id; ue_id.gnb.guami.amf_ptr = rrc_ue_context->ue_guami.amf_pointer; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 90b51ba118..15c5aa19e9 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -165,9 +165,7 @@ static nr_guami_t get_guami(const uint32_t amf_Id, const plmn_id_t plmn) guami.amf_region_id = (amf_Id >> 16) & 0xff; guami.amf_set_id = (amf_Id >> 6) & 0x3ff; guami.amf_pointer = amf_Id & 0x3f; - guami.mcc = plmn.mcc; - guami.mnc = plmn.mnc; - guami.mnc_len = plmn.mnc_digit_length; + guami.plmn = plmn; return guami; } diff --git a/openair3/NGAP/ngap_common.c b/openair3/NGAP/ngap_common.c index c3865cff10..8be57349ec 100644 --- a/openair3/NGAP/ngap_common.c +++ b/openair3/NGAP/ngap_common.c @@ -106,7 +106,7 @@ ngap_cause_t decode_ngap_cause(const NGAP_Cause_t *in) nr_guami_t decode_ngap_guami(const NGAP_GUAMI_t *in) { nr_guami_t out = {0}; - TBCD_TO_MCC_MNC(&in->pLMNIdentity, out.mcc, out.mnc, out.mnc_len); + TBCD_TO_MCC_MNC(&in->pLMNIdentity, out.plmn.mcc, out.plmn.mnc, out.plmn.mnc_digit_length); OCTET_STRING_TO_INT8(&in->aMFRegionID, out.amf_region_id); OCTET_STRING_TO_INT16(&in->aMFSetID, out.amf_set_id); OCTET_STRING_TO_INT8(&in->aMFPointer, out.amf_pointer); diff --git a/openair3/NGAP/ngap_gNB_nas_procedures.c b/openair3/NGAP/ngap_gNB_nas_procedures.c index 55cd7d61f4..7dd6359b7a 100644 --- a/openair3/NGAP/ngap_gNB_nas_procedures.c +++ b/openair3/NGAP/ngap_gNB_nas_procedures.c @@ -76,9 +76,9 @@ static ngap_gNB_amf_data_t *select_amf(ngap_gNB_instance_t *instance_p, const ng const nr_guami_t *guami = &msg->ue_identity.guami; LOG_D(NGAP, "GUAMI is present: MCC=%03d MNC=%0*d RegionID=%d SetID=%d Pointer=%d\n", - guami->mcc, - guami->mnc_len, - guami->mnc, + guami->plmn.mcc, + guami->plmn.mnc_digit_length, + guami->plmn.mnc, guami->amf_region_id, guami->amf_set_id, guami->amf_pointer); @@ -89,9 +89,9 @@ static ngap_gNB_amf_data_t *select_amf(ngap_gNB_instance_t *instance_p, const ng msg->gNB_ue_ngap_id, amf->amf_name, amf->assoc_id, - guami->mcc, - guami->mnc_len, - guami->mnc, + guami->plmn.mcc, + guami->plmn.mnc_digit_length, + guami->plmn.mnc, guami->amf_region_id, guami->amf_set_id, guami->amf_pointer); diff --git a/openair3/NGAP/ngap_gNB_nnsf.c b/openair3/NGAP/ngap_gNB_nnsf.c index f895737a0d..5fbc3e4d11 100644 --- a/openair3/NGAP/ngap_gNB_nnsf.c +++ b/openair3/NGAP/ngap_gNB_nnsf.c @@ -286,8 +286,7 @@ ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_guami(ngap_gNB_instance_t *inst struct plmn_identity_s *served_plmn_p = NULL; STAILQ_FOREACH(served_plmn_p, &guami_p->served_plmns, next) { - if ((served_plmn_p->mcc == guami.mcc) && - (served_plmn_p->mnc == guami.mnc)) { + if ((served_plmn_p->mcc == guami.plmn.mcc) && (served_plmn_p->mnc == guami.plmn.mnc)) { break; } } @@ -322,9 +321,9 @@ ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_guami(ngap_gNB_instance_t *inst } NGAP_WARN("No matching AMF found for GUAMI (MCC=%03d MNC=%0*d RegionID=%d SetID=%d Pointer=%d)\n", - guami.mcc, - guami.mnc_len, - guami.mnc, + guami.plmn.mcc, + guami.plmn.mnc_digit_length, + guami.plmn.mnc, guami.amf_region_id, guami.amf_set_id, guami.amf_pointer); @@ -373,8 +372,7 @@ ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_guami_no_cause(ngap_gNB_instanc struct plmn_identity_s *served_plmn_p = NULL; STAILQ_FOREACH(served_plmn_p, &guami_p->served_plmns, next) { - if ((served_plmn_p->mcc == guami.mcc) && - (served_plmn_p->mnc == guami.mnc)) { + if ((served_plmn_p->mcc == guami.plmn.mcc) && (served_plmn_p->mnc == guami.plmn.mnc)) { break; } } From 5f261d4f4c74e9cad2d390e42e6fbef11429b7eb Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 21 Oct 2025 11:12:13 +0200 Subject: [PATCH 035/275] Add PLMN validation for handover requests and store serving PLMN Prevent assertion in ngap_gNB_nas_uplink() due to uninitialized selected_plmn_identity: the selected_plmn_identity field was not being set during N2 handover, causing the assertion ((plmn->mnc_digit_length == 3) || (plmn->mnc_digit_length == 2)) to fail when processing subsequent NAS messages. The issue is that the Serving PLMN is not stored by RRC. During N2 mobility, RRC can get this info from GUAMI. When receiving the Handover Request the target gNB knows from GUAMI which serving PLMN is the AMF belonging to. In current design, allowed PLMNs are stored in rrc.configuration. The incoming PLMN needs to be validate against the PLMNs in the list, then stored in the UE context as Serving PLMN. This change adds PLMN validation when processing handover requests from the source gNB. The validation is needed because: - Target gNB must verify that the PLMN from the GUAMI in the handover request is allowed by its configuration before accepting the handover to prevent handover to unsupported PLMNs. - When PLMN validation fails, the target gNB now properly sends NGAP_HANDOVER_FAILURE - UE Context must store serving PLMN for later use --- openair2/RRC/NR/rrc_gNB_NGAP.c | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 15c5aa19e9..62fb68d92b 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -110,6 +110,24 @@ static const uint16_t NGAP_INTEGRITY_NIA3_MASK = 0x2000; static void set_UE_security_algos(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const ngap_security_capabilities_t *cap); +/** @brief Validates PLMN against allowed PLMN list and returns pointer to matching PLMN + * @param rrc RRC instance + * @param plmn PLMN to validate + * @return pointer to matching PLMN in configuration, or NULL if not found */ +static const plmn_id_t *get_serving_plmn(gNB_RRC_INST *rrc, const plmn_id_t *plmn) +{ + // Find matching PLMN in configuration + for (int idx = 0; idx < rrc->configuration.num_plmn; idx++) { + const plmn_id_t *allowed = &rrc->configuration.plmn[idx]; + if (allowed->mcc == plmn->mcc && allowed->mnc == plmn->mnc && allowed->mnc_digit_length == plmn->mnc_digit_length) { + LOG_D(NR_RRC, "PLMN (MCC:%d, MNC:%*d) matched with allowed PLMN[%d]\n", plmn->mcc, plmn->mnc_digit_length, plmn->mnc, idx); + return allowed; + } + } + LOG_W(NR_RRC, "PLMN (MCC:%d, MNC:%*d) not found in allowed PLMN list\n", plmn->mcc, plmn->mnc_digit_length, plmn->mnc); + return NULL; +} + /*! *\brief save security key. *\param UE UE context. @@ -1161,6 +1179,20 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail); return -1; } + + // Validate PLMN from GUAMI against allowed PLMN list + const plmn_id_t *serving_plmn = get_serving_plmn(rrc, &msg->guami.plmn); + if (!serving_plmn) { + LOG_E(NR_RRC, "PLMN from GUAMI not supported - rejecting handover\n"); + ngap_handover_failure_t fail = { + .amf_ue_ngap_id = msg->amf_ue_ngap_id, + .cause.type = NGAP_CAUSE_RADIO_NETWORK, + .cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_TARGET_NOT_ALLOWED, + }; + rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail); + return -1; + } + uint16_t pci = du->setup_req->cell[0].info.nr_pci; LOG_I(NR_RRC, "Received Handover Request (on NR Cell ID=%lu, PCI=%u) \n", msg->nr_cell_id, pci); @@ -1176,6 +1208,9 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga // Store IDs in UE context UE->amf_ue_ngap_id = msg->amf_ue_ngap_id; UE->ue_guami = msg->guami; + // Store the serving PLMN + UE->serving_plmn = *serving_plmn; + UE->ho_context->target->ue_ho_prep_info = copy_byte_array(msg->ue_ho_prep_info); // store the received UE Security Capabilities in the UE context FREE_AND_ZERO_BYTE_ARRAY(UE->ue_cap_buffer); From 244819c6275396098247f2dcf9a3545d8cfe9004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Soares=20Silva?= Date: Tue, 26 Aug 2025 15:10:46 +0100 Subject: [PATCH 036/275] Allocate VNF_info directly in config instead of global variable This allows access to the VNF configuration in the various transport mechanisms straight from config, without this, the transport specific thread would cause a segmentation fault when trying to access the pack/unpack function pointers. Add 2 functions to access the P5 and P7 VNF structures, to avoid having to always cast user_data and p7_vnfs --- nfapi/oai_integration/aerial/fapi_nvIPC.c | 27 +++-- nfapi/oai_integration/aerial/fapi_nvIPC.h | 2 +- nfapi/oai_integration/nfapi_vnf.c | 99 +++++++++++-------- nfapi/oai_integration/nfapi_vnf.h | 4 +- nfapi/oai_integration/socket/socket_pnf.c | 5 +- nfapi/oai_integration/socket/socket_vnf.c | 9 +- .../wls_integration/include/wls_vnf.h | 1 - .../oai_integration/wls_integration/wls_vnf.c | 16 +-- openair1/SCHED/nfapi_lte_dummy.c | 3 - 9 files changed, 87 insertions(+), 79 deletions(-) diff --git a/nfapi/oai_integration/aerial/fapi_nvIPC.c b/nfapi/oai_integration/aerial/fapi_nvIPC.c index b1bfae5415..9cd964cc0c 100644 --- a/nfapi/oai_integration/aerial/fapi_nvIPC.c +++ b/nfapi/oai_integration/aerial/fapi_nvIPC.c @@ -63,12 +63,8 @@ char cpu_buf_recv[RECV_BUF_LEN]; uint16_t sfn = 0, slot = 0; nv_ipc_t *ipc; -nfapi_vnf_config_t *vnf_config = 0; -void set_config(nfapi_vnf_config_t *conf) -{ - vnf_config = conf; -} + static uint16_t old_sfn = 0; static uint16_t old_slot = 0; //////////////////////////////////////////////////////////////////////// @@ -84,6 +80,7 @@ static int ipc_handle_rx_msg(nv_ipc_t *ipc, nv_ipc_msg_t *msg) uint8_t *end = msg->msg_buf + msg->msg_len; // unpack FAPI messages and handle them + nfapi_vnf_config_t * vnf_config = get_config(); if (vnf_config != 0) { // first, unpack the header fapi_phy_api_msg fapi_msg; @@ -576,13 +573,13 @@ int nvIPC_Init(nvipc_params_t nvipc_params_s) int oai_fapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req) { - nfapi_vnf_p7_config_t *p7_config = (((vnf_info *)vnf_config->user_data)->p7_vnfs[0].config); + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); ul_tti_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! ul_tti_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST; // int retval = nfapi_vnf_p7_ul_tti_req(p7_config, ul_tti_req); - int retval = p7_config->send_p7_msg((vnf_p7_t *)p7_config, &ul_tti_req->header); + int retval = p7_config->send_p7_msg(get_p7_vnf(), &ul_tti_req->header); if (retval != 0) { LOG_E(PHY, "%s() Problem sending retval:%d\n", __FUNCTION__, retval); @@ -598,11 +595,11 @@ int oai_fapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req) int oai_fapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req) { - nfapi_vnf_p7_config_t *p7_config = (((vnf_info *)vnf_config->user_data)->p7_vnfs[0].config); + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); ul_dci_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! ul_dci_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST; // int retval = nfapi_vnf_p7_ul_dci_req(p7_config, ul_dci_req); - int retval = p7_config->send_p7_msg((vnf_p7_t *)p7_config, &ul_dci_req->header); + int retval = p7_config->send_p7_msg(get_p7_vnf(), &ul_dci_req->header); if (retval != 0) { LOG_E(PHY, "%s() Problem sending retval:%d\n", __FUNCTION__, retval); } else { @@ -613,12 +610,12 @@ int oai_fapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req) int oai_fapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req) { - nfapi_vnf_p7_config_t *p7_config = (((vnf_info *)vnf_config->user_data)->p7_vnfs[0].config); + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); tx_data_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! tx_data_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST; // LOG_D(PHY, "[VNF] %s() TX_REQ sfn_sf:%d number_of_pdus:%d\n", __FUNCTION__, NFAPI_SFNSF2DEC(tx_data_req->SFN), // tx_data_req->Number_of_PDUs); int retval = nfapi_vnf_p7_tx_data_req(p7_config, tx_data_req); - int retval = p7_config->send_p7_msg((vnf_p7_t *)p7_config, &tx_data_req->header); + int retval = p7_config->send_p7_msg(get_p7_vnf(), &tx_data_req->header); if (retval != 0) { LOG_E(PHY, "%s() Problem sending retval:%d\n", __FUNCTION__, retval); } else { @@ -630,10 +627,10 @@ int oai_fapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req) int oai_fapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) { - nfapi_vnf_p7_config_t *p7_config = (((vnf_info *)vnf_config->user_data)->p7_vnfs[0].config); + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); dl_config_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST; dl_config_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!! - int retval = p7_config->send_p7_msg((vnf_p7_t *)p7_config, &dl_config_req->header); + int retval = p7_config->send_p7_msg(get_p7_vnf(), &dl_config_req->header); dl_config_req->dl_tti_request_body.nPDUs = 0; dl_config_req->dl_tti_request_body.nGroup = 0; @@ -644,9 +641,9 @@ int oai_fapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) } int oai_fapi_send_end_request(int cell, uint32_t frame, uint32_t slot){ - nfapi_vnf_p7_config_t *p7_config = (((vnf_info *)vnf_config->user_data)->p7_vnfs[0].config); + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); nfapi_nr_slot_indication_scf_t nr_slot_resp = {.header.message_id = 0x8F, .sfn = frame, .slot = slot}; - int retval = p7_config->send_p7_msg((vnf_p7_t *)p7_config, &nr_slot_resp.header); + int retval = p7_config->send_p7_msg(get_p7_vnf(), &nr_slot_resp.header); if (retval != 0) { LOG_E(PHY, "%s() Problem sending retval:%d\n", __FUNCTION__, retval); } diff --git a/nfapi/oai_integration/aerial/fapi_nvIPC.h b/nfapi/oai_integration/aerial/fapi_nvIPC.h index 482d8103f0..72cf66bde7 100644 --- a/nfapi/oai_integration/aerial/fapi_nvIPC.h +++ b/nfapi/oai_integration/aerial/fapi_nvIPC.h @@ -56,7 +56,7 @@ bool aerial_send_P7_msg_with_data(void *packedBuf, void *dataBuf, uint32_t dataLength, nfapi_nr_p7_message_header_t *header); -void set_config(nfapi_vnf_config_t *conf); + int nvIPC_Init(nvipc_params_t nvipc_params_s); int oai_fapi_send_end_request(int cell_id, uint32_t frame, uint32_t slot); int oai_fapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req); diff --git a/nfapi/oai_integration/nfapi_vnf.c b/nfapi/oai_integration/nfapi_vnf.c index 5ea99de7ea..c2193fcaa3 100644 --- a/nfapi/oai_integration/nfapi_vnf.c +++ b/nfapi/oai_integration/nfapi_vnf.c @@ -70,10 +70,25 @@ #endif #define TEST - +static nfapi_vnf_config_t *config; extern RAN_CONTEXT_t RC; extern UL_RCC_IND_t UL_RCC_INFO; +nfapi_vnf_config_t * get_config() +{ + return config; +} +vnf_p7_t *get_p7_vnf() +{ + vnf_info *vnf = config->user_data; + return (vnf_p7_t *)vnf->p7_vnfs->config; +} + +nfapi_vnf_p7_config_t *get_p7_vnf_config() +{ + return &get_p7_vnf()->_public; +} + int vnf_pack_vendor_extension_tlv(void *ve, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *codec) { //NFAPI_TRACE(NFAPI_TRACE_INFO, "vnf_pack_vendor_extension_tlv\n"); nfapi_tl_t *tlv = (nfapi_tl_t *)ve; @@ -1341,7 +1356,6 @@ void *configure_nr_p7_vnf(void *ptr) p7_vnf->config->hdr_unpack_func = &fapi_nr_p7_message_header_unpack; p7_vnf->config->pack_func = &fapi_nr_p7_message_pack; p7_vnf->config->send_p7_msg = &wls_vnf_nr_send_p7_message; - wls_vnf_set_p7_config(p7_vnf->config); #endif #ifdef ENABLE_SOCKET @@ -1735,32 +1749,30 @@ void vnf_start_thread(void *ptr) { nfapi_vnf_start((nfapi_vnf_config_t *)ptr); } -static vnf_info vnf; - void configure_nr_nfapi_vnf(eth_params_t params) { #ifndef ENABLE_AERIAL nfapi_setmode(NFAPI_MODE_VNF); #endif - memset(&vnf, 0, sizeof(vnf)); - memset(vnf.p7_vnfs, 0, sizeof(vnf.p7_vnfs)); - vnf.p7_vnfs[0].timing_window = 30; - vnf.p7_vnfs[0].periodic_timing_enabled = 0; - vnf.p7_vnfs[0].aperiodic_timing_enabled = 0; - vnf.p7_vnfs[0].periodic_timing_period = 1; - vnf.p7_vnfs[0].config = nfapi_vnf_p7_config_create(); + vnf_info *vnf = calloc(1, sizeof(vnf_info)); + memset(vnf->p7_vnfs, 0, sizeof(vnf->p7_vnfs)); + vnf->p7_vnfs[0].timing_window = 30; + vnf->p7_vnfs[0].periodic_timing_enabled = 0; + vnf->p7_vnfs[0].aperiodic_timing_enabled = 0; + vnf->p7_vnfs[0].periodic_timing_period = 1; + vnf->p7_vnfs[0].config = nfapi_vnf_p7_config_create(); #ifndef ENABLE_AERIAL NFAPI_TRACE(NFAPI_TRACE_INFO, - "[VNF] %s() vnf.p7_vnfs[0].config:%p VNF ADDRESS:%s:%d\n", + "[VNF] %s() vnf->p7_vnfs[0].config:%p VNF ADDRESS:%s:%d\n", __FUNCTION__, - vnf.p7_vnfs[0].config, + vnf->p7_vnfs[0].config, params.my_addr, params.my_portc); - strcpy(vnf.p7_vnfs[0].local_addr, params.my_addr); - vnf.p7_vnfs[0].local_port = params.my_portd; + strcpy(vnf->p7_vnfs[0].local_addr, params.my_addr); + vnf->p7_vnfs[0].local_port = params.my_portd; #endif - vnf.p7_vnfs[0].mac = (mac_t *)malloc(sizeof(mac_t)); - nfapi_vnf_config_t *config = nfapi_vnf_config_create(); + vnf->p7_vnfs[0].mac = malloc(sizeof(mac_t)); + config = nfapi_vnf_config_create(); config->malloc = malloc; config->free = free; config->vnf_p5_port = params.my_portc; @@ -1780,7 +1792,7 @@ void configure_nr_nfapi_vnf(eth_params_t params) config->nr_start_resp = &nr_start_resp_cb; config->nr_error_ind = &nr_error_ind_cb; config->vendor_ext = &vendor_nr_ext_cb; - config->user_data = &vnf; + config->user_data = vnf; // To allow custom vendor extentions to be added to nfapi config->codec_config.unpack_vendor_extension_tlv = &vnf_nr_unpack_vendor_extension_tlv; config->codec_config.pack_vendor_extension_tlv = &vnf_nr_pack_vendor_extension_tlv; @@ -1817,7 +1829,6 @@ void configure_nr_nfapi_vnf(eth_params_t params) config->hdr_unpack_func = &fapi_nr_message_header_unpack; config->pack_func = &fapi_nr_p5_message_pack; config->send_p5_msg = &aerial_nr_send_p5_message; - set_config(config); NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Created VNF NFAPI start thread %s\n", __FUNCTION__); nfapi_vnf_pnf_info_t *pnf = (nfapi_vnf_pnf_info_t *)malloc(sizeof(nfapi_vnf_pnf_info_t)); NFAPI_TRACE(NFAPI_TRACE_INFO, "MALLOC nfapi_vnf_pnf_info_t for pnf_list pnf:%p\n", pnf); @@ -1826,7 +1837,6 @@ void configure_nr_nfapi_vnf(eth_params_t params) pnf->connected = 1; // Add needed parameters - vnf_info *vnf = (vnf_info *)(config->user_data); pnf_info *pnf_info = vnf->pnfs; for (int i = 0; i < 1; ++i) { @@ -1863,18 +1873,23 @@ void configure_nr_nfapi_vnf(eth_params_t params) void configure_nfapi_vnf(char *vnf_addr, int vnf_p5_port, char *pnf_ip_addr, int pnf_p7_port, int vnf_p7_port) { nfapi_setmode(NFAPI_MODE_VNF); - memset(&vnf, 0, sizeof(vnf)); - memset(vnf.p7_vnfs, 0, sizeof(vnf.p7_vnfs)); - vnf.p7_vnfs[0].timing_window = 32; - vnf.p7_vnfs[0].periodic_timing_enabled = 1; - vnf.p7_vnfs[0].aperiodic_timing_enabled = 0; - vnf.p7_vnfs[0].periodic_timing_period = 10; - vnf.p7_vnfs[0].config = nfapi_vnf_p7_config_create(); - NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] %s() vnf.p7_vnfs[0].config:%p VNF ADDRESS:%s:%d\n", __FUNCTION__, vnf.p7_vnfs[0].config, vnf_addr, vnf_p5_port); - strcpy(vnf.p7_vnfs[0].local_addr, vnf_addr); - vnf.p7_vnfs[0].local_port = vnf_p7_port; - vnf.p7_vnfs[0].mac = (mac_t *)malloc(sizeof(mac_t)); - nfapi_vnf_config_t *config = nfapi_vnf_config_create(); + vnf_info *vnf = calloc(1, sizeof(vnf_info)); + memset(vnf->p7_vnfs, 0, sizeof(vnf->p7_vnfs)); + vnf->p7_vnfs[0].timing_window = 32; + vnf->p7_vnfs[0].periodic_timing_enabled = 1; + vnf->p7_vnfs[0].aperiodic_timing_enabled = 0; + vnf->p7_vnfs[0].periodic_timing_period = 10; + vnf->p7_vnfs[0].config = nfapi_vnf_p7_config_create(); + NFAPI_TRACE(NFAPI_TRACE_INFO, + "[VNF] %s() vnf.p7_vnfs[0].config:%p VNF ADDRESS:%s:%d\n", + __FUNCTION__, + vnf->p7_vnfs[0].config, + vnf_addr, + vnf_p5_port); + strcpy(vnf->p7_vnfs[0].local_addr, vnf_addr); + vnf->p7_vnfs[0].local_port = vnf_p7_port; + vnf->p7_vnfs[0].mac = malloc(sizeof(mac_t)); + config = nfapi_vnf_config_create(); config->malloc = malloc; config->free = free; config->vnf_p5_port = vnf_p5_port; @@ -1893,7 +1908,7 @@ void configure_nfapi_vnf(char *vnf_addr, int vnf_p5_port, char *pnf_ip_addr, int config->config_resp = &config_resp_cb; config->start_resp = &start_resp_cb; config->vendor_ext = &vendor_ext_cb; - config->user_data = &vnf; + config->user_data = vnf; // To allow custom vendor extentions to be added to nfapi config->codec_config.unpack_vendor_extension_tlv = &vnf_unpack_vendor_extension_tlv; config->codec_config.pack_vendor_extension_tlv = &vnf_pack_vendor_extension_tlv; @@ -1910,7 +1925,7 @@ void configure_nfapi_vnf(char *vnf_addr, int vnf_p5_port, char *pnf_ip_addr, int } int oai_nfapi_dl_config_req(nfapi_dl_config_request_t *dl_config_req) { - nfapi_vnf_p7_config_t *p7_config = vnf.p7_vnfs[0].config; + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); dl_config_req->header.phy_id = 1; // HACK TODO FIXME - need to pass this around!!!! dl_config_req->header.message_id = NFAPI_DL_CONFIG_REQUEST; LOG_D(PHY, "[VNF] %s() DL_CONFIG_REQ sfn_sf:%d_%d number_of_pdus:%d\n", __FUNCTION__, @@ -1946,7 +1961,7 @@ int oai_nfapi_dl_config_req(nfapi_dl_config_request_t *dl_config_req) { int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) { LOG_D(NR_PHY, "Entering oai_nfapi_nr_dl_config_req sfn:%d,slot:%d\n", dl_config_req->SFN, dl_config_req->Slot); - nfapi_vnf_p7_config_t *p7_config = vnf.p7_vnfs[0].config; + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); dl_config_req->header.message_id= NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST; dl_config_req->header.phy_id = 1; // HACK TODO FIXME - need to pass this around!!!! @@ -1965,7 +1980,7 @@ int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req) int oai_nfapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req) { LOG_D(NR_PHY, "Entering oai_nfapi_nr_tx_data_req sfn:%d,slot:%d\n", tx_data_req->SFN, tx_data_req->Slot); - nfapi_vnf_p7_config_t *p7_config = vnf.p7_vnfs[0].config; + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); tx_data_req->header.phy_id = 1; // HACK TODO FIXME - need to pass this around!!!! tx_data_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST; //LOG_D(PHY, "[VNF] %s() TX_REQ sfn_sf:%d number_of_pdus:%d\n", __FUNCTION__, NFAPI_SFNSF2DEC(tx_req->sfn_sf), tx_req->tx_request_body.number_of_pdus); @@ -1982,7 +1997,7 @@ int oai_nfapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req) int oai_nfapi_tx_req(nfapi_tx_request_t *tx_req) { - nfapi_vnf_p7_config_t *p7_config = vnf.p7_vnfs[0].config; + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); tx_req->header.phy_id = 1; // HACK TODO FIXME - need to pass this around!!!! tx_req->header.message_id = NFAPI_TX_REQUEST; //LOG_D(PHY, "[VNF] %s() TX_REQ sfn_sf:%d number_of_pdus:%d\n", __FUNCTION__, NFAPI_SFNSF2DEC(tx_req->sfn_sf), tx_req->tx_request_body.number_of_pdus); @@ -1998,7 +2013,7 @@ int oai_nfapi_tx_req(nfapi_tx_request_t *tx_req) } int oai_nfapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req) { - nfapi_vnf_p7_config_t *p7_config = vnf.p7_vnfs[0].config; + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); ul_dci_req->header.phy_id = 1; // HACK TODO FIXME - need to pass this around!!!! ul_dci_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST; //LOG_D(PHY, "[VNF] %s() HI_DCI0_REQ sfn_sf:%d dci:%d hi:%d\n", __FUNCTION__, NFAPI_SFNSF2DEC(hi_dci0_req->sfn_sf), hi_dci0_req->hi_dci0_request_body.number_of_dci, hi_dci0_req->hi_dci0_request_body.number_of_hi); @@ -2014,7 +2029,7 @@ int oai_nfapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req) { } int oai_nfapi_hi_dci0_req(nfapi_hi_dci0_request_t *hi_dci0_req) { - nfapi_vnf_p7_config_t *p7_config = vnf.p7_vnfs[0].config; + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); hi_dci0_req->header.phy_id = 1; // HACK TODO FIXME - need to pass this around!!!! hi_dci0_req->header.message_id = NFAPI_HI_DCI0_REQUEST; //LOG_D(PHY, "[VNF] %s() HI_DCI0_REQ sfn_sf:%d dci:%d hi:%d\n", __FUNCTION__, NFAPI_SFNSF2DEC(hi_dci0_req->sfn_sf), hi_dci0_req->hi_dci0_request_body.number_of_dci, hi_dci0_req->hi_dci0_request_body.number_of_hi); @@ -2050,7 +2065,7 @@ static void remove_ul_config_req_pdu(int index, nfapi_ul_config_request_t *ul_co } int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req) { - nfapi_vnf_p7_config_t *p7_config = vnf.p7_vnfs[0].config; + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); ul_tti_req->header.phy_id = 1; // HACK TODO FIXME - need to pass this around!!!! ul_tti_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST; @@ -2070,7 +2085,7 @@ int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req) { } int oai_nfapi_ul_config_req(nfapi_ul_config_request_t *ul_config_req) { - nfapi_vnf_p7_config_t *p7_config = vnf.p7_vnfs[0].config; + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); ul_config_req->header.phy_id = 1; // HACK TODO FIXME - need to pass this around!!!! ul_config_req->header.message_id = NFAPI_UL_CONFIG_REQUEST; //LOG_D(PHY, "[VNF] %s() header message_id:%02x\n", __FUNCTION__, ul_config_req->header.message_id); @@ -2130,7 +2145,7 @@ int oai_nfapi_ul_config_req(nfapi_ul_config_request_t *ul_config_req) { int oai_nfapi_ue_release_req(nfapi_ue_release_request_t *release_req){ if(release_req->ue_release_request_body.number_of_TLVs <= 0) return 0; - nfapi_vnf_p7_config_t *p7_config = vnf.p7_vnfs[0].config; + nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config(); release_req->header.phy_id = 1; // HACK TODO FIXME - need to pass this around!!!! release_req->header.message_id = NFAPI_UE_RELEASE_REQUEST; diff --git a/nfapi/oai_integration/nfapi_vnf.h b/nfapi/oai_integration/nfapi_vnf.h index 104cff44f4..3e72896d37 100644 --- a/nfapi/oai_integration/nfapi_vnf.h +++ b/nfapi/oai_integration/nfapi_vnf.h @@ -148,5 +148,7 @@ typedef struct { void configure_nfapi_vnf(char *vnf_addr, int vnf_p5_port, char *pnf_ip_addr, int pnf_p7_port, int vnf_p7_port); void configure_nr_nfapi_vnf(eth_params_t params); - +nfapi_vnf_config_t *get_config(); +vnf_p7_t *get_p7_vnf(); +nfapi_vnf_p7_config_t *get_p7_vnf_config(); #endif /* NFAPI_VNF_H_ */ diff --git a/nfapi/oai_integration/socket/socket_pnf.c b/nfapi/oai_integration/socket/socket_pnf.c index fe4999c998..ad6c0dec0c 100644 --- a/nfapi/oai_integration/socket/socket_pnf.c +++ b/nfapi/oai_integration/socket/socket_pnf.c @@ -21,6 +21,9 @@ #include "socket_pnf.h" #include "nfapi.h" +#include "nfapi.h" +static nfapi_pnf_config_t *config; + bool pnf_nr_send_p5_message(pnf_t *pnf, nfapi_nr_p4_p5_message_header_t *msg, uint32_t msg_len) { int packed_len = @@ -148,7 +151,7 @@ bool pnf_nr_send_p7_message(pnf_p7_t *pnf_p7, nfapi_nr_p7_message_header_t *head void *pnf_start_p5_thread(void *ptr) { NFAPI_TRACE(NFAPI_TRACE_INFO, "[PNF] IN PNF NFAPI start thread %s\n", __FUNCTION__); - nfapi_pnf_config_t *config = (nfapi_pnf_config_t *)ptr; + config = (nfapi_pnf_config_t *)ptr; struct sched_param sp; sp.sched_priority = 20; pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp); diff --git a/nfapi/oai_integration/socket/socket_vnf.c b/nfapi/oai_integration/socket/socket_vnf.c index 6440109ed6..518f57c2e3 100644 --- a/nfapi/oai_integration/socket/socket_vnf.c +++ b/nfapi/oai_integration/socket/socket_vnf.c @@ -21,6 +21,8 @@ #include "socket_vnf.h" #include "nfapi.h" +#include "nfapi_vnf.h" +#include #include "nfapi/oai_integration/vendor_ext.h" //TODO: Remove this include when removing the Aerial transport stuff static bool send_p5_msg(vnf_t *vnf, nfapi_vnf_pnf_info_t *pnf, const void *msg, int len, uint8_t stream) @@ -279,7 +281,6 @@ static int nfapi_nr_vnf_p5_start(nfapi_vnf_config_t *config) // Verify that config is not null if (config == 0) return -1; - NFAPI_TRACE(NFAPI_TRACE_INFO, "%s()\n", __FUNCTION__); int p5ListenSock, p5Sock; @@ -293,7 +294,7 @@ static int nfapi_nr_vnf_p5_start(nfapi_vnf_config_t *config) struct sctp_initmsg initMsg = {0}; int noDelay; - vnf_t *vnf = (vnf_t *)(config); + vnf_t *vnf = (vnf_t *)(get_config()); NFAPI_TRACE(NFAPI_TRACE_INFO, "Starting P5 VNF connection on port %u\n", config->vnf_p5_port); @@ -747,8 +748,8 @@ static int nfapi_nr_vnf_p7_start(nfapi_vnf_p7_config_t *config) return -1; NFAPI_TRACE(NFAPI_TRACE_INFO, "%s()\n", __FUNCTION__); - - vnf_p7_t *vnf_p7 = (vnf_p7_t *)config; + vnf_p7_t *vnf_p7 = get_p7_vnf(); + vnf_p7 = (vnf_p7_t *)config; // Create p7 receive udp port // todo : this needs updating for Ipv6 diff --git a/nfapi/oai_integration/wls_integration/include/wls_vnf.h b/nfapi/oai_integration/wls_integration/include/wls_vnf.h index 645b24d056..46a8766b6d 100644 --- a/nfapi/oai_integration/wls_integration/include/wls_vnf.h +++ b/nfapi/oai_integration/wls_integration/include/wls_vnf.h @@ -27,7 +27,6 @@ void *wls_fapi_vnf_nr_start_thread(void *ptr); int wls_fapi_nr_vnf_start(); -void wls_vnf_set_p7_config(void *p7_config); 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); bool wls_vnf_nr_send_p7_message(vnf_p7_t* vnf_p7,nfapi_nr_p7_message_header_t* msg); #endif //OPENAIRINTERFACE_WLS_VNF_H diff --git a/nfapi/oai_integration/wls_integration/wls_vnf.c b/nfapi/oai_integration/wls_integration/wls_vnf.c index ccc68b4905..79934164f4 100644 --- a/nfapi/oai_integration/wls_integration/wls_vnf.c +++ b/nfapi/oai_integration/wls_integration/wls_vnf.c @@ -33,14 +33,7 @@ static nfapi_vnf_config_t *config; static WLS_MAC_CTX wls_mac_iface; vnf_t *_vnf = NULL; -// A copy of the VNF P7 config is needed for the message processing thread, in other transport mechanisms where P7 is handled in a -// different thread, the config is passed to it upon the thread creation -nfapi_vnf_p7_config_t *wls_vnf_p7_config = NULL; -void wls_vnf_set_p7_config(void *p7_config) -{ - wls_vnf_p7_config = (nfapi_vnf_p7_config_t *)p7_config; -} static PWLS_MAC_CTX wls_mac_get_ctx(void) { @@ -258,7 +251,7 @@ for(int i = 0 ; i< pWls->nTotalBlocks; i++){ void* pMsg = wls_mac_alloc_buffer(pWls, 0, i); /* Enqueue all blocks */ nBlocks += WLS_EnqueueBlock(pWls->hWls, WLS_VA2PA(pWls->hWls, pMsg)); -} +} NFAPI_TRACE(NFAPI_TRACE_INFO, "\nAllocated %d Blocks \n", nBlocks); return true; } @@ -294,11 +287,11 @@ static void procPhyMessages(uint32_t msg_size, void *msg_buf, uint16_t msg_id) case NFAPI_NR_PHY_MSG_TYPE_CONFIG_RESPONSE: case NFAPI_NR_PHY_MSG_TYPE_START_RESPONSE: case NFAPI_NR_PHY_MSG_TYPE_STOP_INDICATION: - vnf_nr_handle_p4_p5_message(msg_buf, msg_size, 0, config); + vnf_nr_handle_p4_p5_message(msg_buf, msg_size, 0, get_config()); break; case NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST ... NFAPI_NR_PHY_MSG_TYPE_RACH_INDICATION: { - vnf_nr_handle_p7_message(msg_buf, msg_size + NFAPI_NR_P7_HEADER_LENGTH, (vnf_p7_t *)wls_vnf_p7_config); + vnf_nr_handle_p7_message(msg_buf, msg_size + NFAPI_NR_P7_HEADER_LENGTH, get_p7_vnf()); break; } default: @@ -308,6 +301,7 @@ static void procPhyMessages(uint32_t msg_size, void *msg_buf, uint16_t msg_id) int wls_fapi_nr_vnf_start(nfapi_vnf_config_t *cfg) { + nfapi_vnf_config_t * config = get_config(); config = cfg; if (config == 0) { @@ -403,7 +397,7 @@ bool wls_vnf_nr_send_p7_message(vnf_p7_t *vnf_p7, nfapi_nr_p7_message_header_t * AssertFatal(fapiMsgElem, "WLS_PA2VA failed for fapiMsgElem\n"); //memcpy((uint8_t *)(fapiMsgElem + 1), message, packed_len + NFAPI_HEADER_LENGTH); const uint8_t wls_header[] = {1, 0}; // num_messages , opaque_handle - + fill_fapi_list_elem(headerElem, fapiMsgElem, FAPI_VENDOR_MSG_HEADER_IND, 1, 2); memcpy((uint8_t *)(headerElem + 1), wls_header, 2); int packed_len = ((nfapi_vnf_p7_config_t *)vnf_p7)->pack_func(msg, (fapiMsgElem + 1), NFAPI_MAX_PACKED_MESSAGE_SIZE, NULL); diff --git a/openair1/SCHED/nfapi_lte_dummy.c b/openair1/SCHED/nfapi_lte_dummy.c index d61f0d0548..0028055c61 100644 --- a/openair1/SCHED/nfapi_lte_dummy.c +++ b/openair1/SCHED/nfapi_lte_dummy.c @@ -100,9 +100,6 @@ bool wls_vnf_nr_send_p7_message(vnf_p7_t *vnf_p7, nfapi_nr_p7_message_header_t * return false; } -void wls_vnf_set_p7_config(void *p7_config) -{ -} 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) { From 8ecda65d55356d92ae3b1d9410890eba89a96d10 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 23 Oct 2025 10:00:05 +0200 Subject: [PATCH 037/275] Remove Benetel radio drivers These drivers are superseded by the O-RAN 7.2 FHI. --- cmake_targets/build_oai | 4 +- radio/ETHERNET/CMakeLists.txt | 2 - radio/ETHERNET/benetel/4g/CMakeLists.txt | 20 - radio/ETHERNET/benetel/4g/benetel.c | 392 ---------- radio/ETHERNET/benetel/4g/dpdk_driver.c | 774 ------------------- radio/ETHERNET/benetel/4g/low.c | 94 --- radio/ETHERNET/benetel/4g/low.h | 50 -- radio/ETHERNET/benetel/4g/low_dpdk.c | 119 --- radio/ETHERNET/benetel/4g/shared_buffers.c | 117 --- radio/ETHERNET/benetel/4g/shared_buffers.h | 61 -- radio/ETHERNET/benetel/5g/CMakeLists.txt | 19 - radio/ETHERNET/benetel/5g/benetel.c | 399 ---------- radio/ETHERNET/benetel/5g/dpdk_driver.c | 835 --------------------- radio/ETHERNET/benetel/5g/low.c | 128 ---- radio/ETHERNET/benetel/5g/low.h | 49 -- radio/ETHERNET/benetel/5g/low_dpdk.c | 119 --- radio/ETHERNET/benetel/5g/shared_buffers.c | 123 --- radio/ETHERNET/benetel/5g/shared_buffers.h | 61 -- radio/ETHERNET/benetel/CMakeLists.txt | 9 - 19 files changed, 2 insertions(+), 3373 deletions(-) delete mode 100644 radio/ETHERNET/benetel/4g/CMakeLists.txt delete mode 100644 radio/ETHERNET/benetel/4g/benetel.c delete mode 100644 radio/ETHERNET/benetel/4g/dpdk_driver.c delete mode 100644 radio/ETHERNET/benetel/4g/low.c delete mode 100644 radio/ETHERNET/benetel/4g/low.h delete mode 100644 radio/ETHERNET/benetel/4g/low_dpdk.c delete mode 100644 radio/ETHERNET/benetel/4g/shared_buffers.c delete mode 100644 radio/ETHERNET/benetel/4g/shared_buffers.h delete mode 100644 radio/ETHERNET/benetel/5g/CMakeLists.txt delete mode 100644 radio/ETHERNET/benetel/5g/benetel.c delete mode 100644 radio/ETHERNET/benetel/5g/dpdk_driver.c delete mode 100644 radio/ETHERNET/benetel/5g/low.c delete mode 100644 radio/ETHERNET/benetel/5g/low.h delete mode 100644 radio/ETHERNET/benetel/5g/low_dpdk.c delete mode 100644 radio/ETHERNET/benetel/5g/shared_buffers.c delete mode 100644 radio/ETHERNET/benetel/5g/shared_buffers.h delete mode 100644 radio/ETHERNET/benetel/CMakeLists.txt diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index 658b0b858a..56c5e9b66c 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -92,7 +92,7 @@ Options: USRP, BLADERF, LMSSDR, IRIS, SIMU, AW2SORI, AERIAL, None (Default) Adds this RF board support (in external packages installation and in compilation) -t | --transport - Selects the transport protocol type, options: None, Ethernet, benetel4g, benetel5g, oran_fhlib_5g, oran_fhlib_5g_mplane, WLS + Selects the transport protocol type, options: None, Ethernet, oran_fhlib_5g, oran_fhlib_5g_mplane, WLS -P | --phy_simulators Makes the unitary tests Layer 1 simulators -V | --vcd @@ -278,7 +278,7 @@ function main() { TARGET_LIST="$TARGET_LIST oai_eth_transpro" CMAKE_CMD="$CMAKE_CMD -DOAI_${2^^}=ON" # ^^ makes uppercase ;; - "benetel4g" | "benetel5g" | "oran_fhlib_4g") + "oran_fhlib_4g") TARGET_LIST="$TARGET_LIST $2" CMAKE_CMD="$CMAKE_CMD -DOAI_${2^^}=ON" # ^^ makes uppercase ;; diff --git a/radio/ETHERNET/CMakeLists.txt b/radio/ETHERNET/CMakeLists.txt index 3a9b9dc5cb..dd0f603eda 100644 --- a/radio/ETHERNET/CMakeLists.txt +++ b/radio/ETHERNET/CMakeLists.txt @@ -9,5 +9,3 @@ set_target_properties(oai_eth_transpro PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMA add_custom_command(TARGET oai_eth_transpro POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink liboai_eth_transpro.so liboai_transpro.so WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) - -add_subdirectory(benetel) diff --git a/radio/ETHERNET/benetel/4g/CMakeLists.txt b/radio/ETHERNET/benetel/4g/CMakeLists.txt deleted file mode 100644 index 164e33eb28..0000000000 --- a/radio/ETHERNET/benetel/4g/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -pkg_check_modules(libdpdk REQUIRED libdpdk=20.05.0) - -add_library(benetel_4g MODULE - benetel.c - shared_buffers.c - low.c - low_dpdk.c - dpdk_driver.c -) - -target_compile_definitions(benetel_4g PROPERTIES COMPILE_FLAGS "-fvisibility=hidden -I$ENV{RTE_SDK}/$ENV{RTE_TARGET}/include") - -SET(DPDK_LIBS "-Wl,-rpath,$ENV{RTE_SDK}/$ENV{RTE_TARGET}/lib -Wl,--whole-archive -L$ENV{RTE_SDK}/$ENV{RTE_TARGET}/lib -ldpdk -Wl,--no-whole-archive") -target_include_directories(benetel_4g PRIVATE ${libdpdk_INCLUDE_DIRS}) -target_link_libraries(benetel_4g PRIVATE ${libdpdk_LIBRARIES}) -target_link_libraries(benetel_4g PRIVATE pthread dl rt m numa) -set_target_properties(benetel_4g PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -add_custom_command(TARGET benetel_4g POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink libbenetel_4g.so liboai_transpro.so - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/radio/ETHERNET/benetel/4g/benetel.c b/radio/ETHERNET/benetel/4g/benetel.c deleted file mode 100644 index 8e221fe929..0000000000 --- a/radio/ETHERNET/benetel/4g/benetel.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#include - -#include "common_lib.h" -#include "ethernet_lib.h" -#include "shared_buffers.h" -#include "low.h" -#include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" - -typedef struct { - eth_state_t e; - shared_buffers buffers; - rru_config_msg_type_t last_msg; - int capabilities_sent; - void *benetel_priv; - char *dpdk_main_command_line; -} benetel_eth_state_t; - -int trx_benetel_start(openair0_device *device) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return 0; -} - - -void trx_benetel_end(openair0_device *device) -{ - printf("BENETEL: %s\n", __FUNCTION__); -} - - -int trx_benetel_stop(openair0_device *device) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return(0); -} - - -int trx_benetel_set_freq(openair0_device* device, openair0_config_t *openair0_cfg) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return(0); -} - - -int trx_benetel_set_gains(openair0_device* device, - openair0_config_t *openair0_cfg) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return(0); -} - - -int trx_benetel_get_stats(openair0_device* device) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return(0); -} - - -int trx_benetel_reset_stats(openair0_device* device) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return(0); -} - - -int ethernet_tune(openair0_device *device, - unsigned int option, - int value) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return 0; -} - -int trx_benetel_write_raw(openair0_device *device, - openair0_timestamp timestamp, - void **buff, int nsamps, int cc, int flags) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return nsamps*4; -} - -int trx_benetel_read_raw(openair0_device *device, - openair0_timestamp *timestamp, - void **buff, int nsamps, int cc) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return nsamps*4; -} - -char *msg_type(int t) -{ - static char *s[12] = { - "RAU_tick", - "RRU_capabilities", - "RRU_config", - "RRU_config_ok", - "RRU_start", - "RRU_stop", - "RRU_sync_ok", - "RRU_frame_resynch", - "RRU_MSG_max_num", - "RRU_check_sync", - "RRU_config_update", - "RRU_config_update_ok", - }; - - if (t < 0 || t > 11) return "UNKNOWN"; - return s[t]; -} - -int trx_benetel_ctlsend(openair0_device *device, void *msg, ssize_t msg_len) -{ - RRU_CONFIG_msg_t *rru_config_msg = msg; - benetel_eth_state_t *s = device->priv; - - printf("BENETEL: %s\n", __FUNCTION__); - - printf(" rru_config_msg->type %d [%s]\n", rru_config_msg->type, - msg_type(rru_config_msg->type)); - - s->last_msg = rru_config_msg->type; - - return msg_len; -} - -int trx_benetel_ctlrecv(openair0_device *device, void *msg, ssize_t msg_len) -{ - RRU_CONFIG_msg_t *rru_config_msg = msg; - benetel_eth_state_t *s = device->priv; - - printf("BENETEL: %s\n", __FUNCTION__); - - if (s->last_msg == RAU_tick && s->capabilities_sent == 0) { - RRU_capabilities_t *cap; - rru_config_msg->type = RRU_capabilities; - rru_config_msg->len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_capabilities_t); - cap = (RRU_capabilities_t*)&rru_config_msg->msg[0]; - cap->FH_fmt = OAI_IF4p5_only; - cap->num_bands = 1; - cap->band_list[0] = 7; - cap->nb_rx[0] = device->openair0_cfg->rx_num_channels; - cap->nb_tx[0] = device->openair0_cfg->tx_num_channels; - cap->max_pdschReferenceSignalPower[0] = -27; - cap->max_rxgain[0] = 90; - - s->capabilities_sent = 1; - - return rru_config_msg->len; - } - if (s->last_msg == RRU_config) { - rru_config_msg->type = RRU_config_ok; - return 0; - } - if (s->last_msg == RRU_start) { - s->benetel_priv = benetel_start(s->e.if_name, &s->buffers, s->dpdk_main_command_line); - } - /* correct? */ - while (1) pause(); - - return 0; -} - -void benetel_fh_if4p5_south_in(RU_t *ru, - int *frame, - int *subframe) -{ - benetel_eth_state_t *s = ru->ifdevice.priv; - PHY_VARS_eNB **eNB_list = ru->eNB_list, *eNB; - LTE_DL_FRAME_PARMS *fp; - int symbol; - int32_t *rxdata; - int antenna; - - lock_ul_buffer(&s->buffers, *subframe); -next: - while (!((s->buffers.ul_busy[0][*subframe] == 0x3fff && - s->buffers.ul_busy[1][*subframe] == 0x3fff) || - s->buffers.prach_busy[*subframe] == 1)) - wait_ul_buffer(&s->buffers, *subframe); - if (s->buffers.prach_busy[*subframe] == 1) { - int i; - int antenna = 0; - uint16_t *in; - uint16_t *out; - in = (uint16_t *)s->buffers.prach[*subframe]; - out = (uint16_t *)ru->prach_rxsigF[0][antenna]; - for (i = 0; i < 840*2; i++) - out[i] = ntohs(in[i]); - s->buffers.prach_busy[*subframe] = 0; - ru->wakeup_prach_eNB(ru->eNB_list[0], ru, *frame, *subframe); - goto next; - } - - eNB = eNB_list[0]; - fp = &eNB->frame_parms; - for (antenna = 0; antenna < ru->nb_rx; antenna++) { - for (symbol = 0; symbol < 14; symbol++) { - int i; - uint16_t *p = (uint16_t *)(&s->buffers.ul[antenna][*subframe][symbol*1200*4]); - for (i = 0; i < 1200*2; i++) { - p[i] = htons(p[i]); - } - rxdata = &ru->common.rxdataF[antenna][symbol * fp->ofdm_symbol_size]; -#if 1 - memcpy(rxdata + 2048 - 600, - &s->buffers.ul[antenna][*subframe][symbol*1200*4], - 600 * 4); - memcpy(rxdata, - &s->buffers.ul[antenna][*subframe][symbol*1200*4] + 600*4, - 600 * 4); -#endif - } - } - - s->buffers.ul_busy[0][*subframe] = 0; - s->buffers.ul_busy[1][*subframe] = 0; - signal_ul_buffer(&s->buffers, *subframe); - unlock_ul_buffer(&s->buffers, *subframe); - - //printf("BENETEL: %s (f.sf %d.%d)\n", __FUNCTION__, *frame, *subframe); - - RU_proc_t *proc = &ru->proc; - extern int sf_ahead; - int f = *frame; - int sf = *subframe; - - //calculate timestamp_rx, timestamp_tx based on frame and subframe - proc->tti_rx = sf; - proc->frame_rx = f; - proc->timestamp_rx = ((proc->frame_rx * 10) + proc->tti_rx ) * fp->samples_per_tti ; - - if (get_nprocs()<=4) { - proc->tti_tx = (sf+sf_ahead)%10; - proc->frame_tx = (sf>(9-sf_ahead)) ? (f+1)&1023 : f; - } - - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_IF4P5_SOUTH_IN_RU+ru->idx,f); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_SUBFRAME_NUMBER_IF4P5_SOUTH_IN_RU+ru->idx,sf); - proc->symbol_mask[sf] = 0; - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, proc->timestamp_rx&0xffffffff); - -} - -void benetel_fh_if4p5_south_out(RU_t *ru, - int frame, - int subframe, - uint64_t timestamp) -{ - benetel_eth_state_t *s = ru->ifdevice.priv; - PHY_VARS_eNB **eNB_list = ru->eNB_list, *eNB; - LTE_DL_FRAME_PARMS *fp; - int symbol; - int32_t *txdata; - int aa; - - //printf("BENETEL: %s (f.sf %d.%d ts %ld)\n", __FUNCTION__, frame, subframe, timestamp); - - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx&0xffffffff ); - - lock_dl_buffer(&s->buffers, subframe); - if (s->buffers.dl_busy[0][subframe] || - s->buffers.dl_busy[1][subframe]) { - printf("%s: fatal: DL buffer busy for subframe %d\n", __FUNCTION__, subframe); - unlock_dl_buffer(&s->buffers, subframe); - return; - } - eNB = eNB_list[0]; - fp = &eNB->frame_parms; - if (ru->num_eNB != 1 || fp->ofdm_symbol_size != 2048 || - fp->Ncp != NORMAL || fp->symbols_per_tti != 14) { - printf("%s:%d:%s: unsupported configuration\n", - __FILE__, __LINE__, __FUNCTION__); - exit(1); - } - - for (aa = 0; aa < ru->nb_tx; aa++) { - for (symbol = 0; symbol < 14; symbol++) { - txdata = &ru->common.txdataF_BF[aa][symbol * fp->ofdm_symbol_size]; -#if 1 - memcpy(&s->buffers.dl[aa][subframe][symbol*1200*4], - txdata + 2048 - 600, - 600 * 4); - memcpy(&s->buffers.dl[aa][subframe][symbol*1200*4] + 600*4, - txdata + 1, - 600 * 4); -#endif - int i; - uint16_t *p = (uint16_t *)(&s->buffers.dl[aa][subframe][symbol*1200*4]); - for (i = 0; i < 1200*2; i++) { - p[i] = htons(p[i]); - } - } - } - - s->buffers.dl_busy[0][subframe] = 0x3fff; - s->buffers.dl_busy[1][subframe] = 0x3fff; - unlock_dl_buffer(&s->buffers, subframe); - - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_IF4P5_SOUTH_OUT_RU+ru->idx, ru->proc.frame_tx); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_SUBFRAME_NUMBER_IF4P5_SOUTH_OUT_RU+ru->idx, ru->proc.tti_tx); - -} - -void *get_internal_parameter(char *name) -{ - printf("BENETEL: %s\n", __FUNCTION__); - - if (!strcmp(name, "fh_if4p5_south_in")) - return benetel_fh_if4p5_south_in; - if (!strcmp(name, "fh_if4p5_south_out")) - return benetel_fh_if4p5_south_out; - - return NULL; -} - -__attribute__((__visibility__("default"))) -int transport_init(openair0_device *device, - openair0_config_t *openair0_cfg, - eth_params_t * eth_params ) -{ - benetel_eth_state_t *eth; - - printf("BENETEL: %s\n", __FUNCTION__); - - device->Mod_id = 0; - device->transp_type = ETHERNET_TP; - device->trx_start_func = trx_benetel_start; - device->trx_get_stats_func = trx_benetel_get_stats; - device->trx_reset_stats_func = trx_benetel_reset_stats; - device->trx_end_func = trx_benetel_end; - device->trx_stop_func = trx_benetel_stop; - device->trx_set_freq_func = trx_benetel_set_freq; - device->trx_set_gains_func = trx_benetel_set_gains; - - device->trx_write_func = trx_benetel_write_raw; - device->trx_read_func = trx_benetel_read_raw; - - device->trx_ctlsend_func = trx_benetel_ctlsend; - device->trx_ctlrecv_func = trx_benetel_ctlrecv; - - device->get_internal_parameter = get_internal_parameter; - - eth = calloc(1, sizeof(benetel_eth_state_t)); - if (eth == NULL) { - AssertFatal(0==1, "out of memory\n"); - } - - eth->e.flags = ETH_RAW_IF4p5_MODE; - eth->e.compression = NO_COMPRESS; - eth->e.if_name = eth_params->local_if_name; - device->priv = eth; - device->openair0_cfg=&openair0_cfg[0]; - - if (openair0_cfg->sdr_addrs == NULL) { - printf("benetel: fatal: sdr_addrs not set in configuration file\n"); - exit(1); - } - - eth->dpdk_main_command_line = strdup(openair0_cfg->sdr_addrs); - if (eth->dpdk_main_command_line == NULL) { - AssertFatal(0==1, "out of memory\n"); - } - - eth->last_msg = -1; - - init_buffers(ð->buffers); - - return 0; -} diff --git a/radio/ETHERNET/benetel/4g/dpdk_driver.c b/radio/ETHERNET/benetel/4g/dpdk_driver.c deleted file mode 100644 index 2b6ef37e6f..0000000000 --- a/radio/ETHERNET/benetel/4g/dpdk_driver.c +++ /dev/null @@ -1,774 +0,0 @@ -/* license: to be defined */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "low.h" - -// HEADER DEFINITION -#define ANT_NUM buf[23] -#define PAYLOAD_1 buf[20] -#define PAYLOAD_2 buf[21] -#define ETH_TYPE buf[17] -#define SYMBOL buf[29] -#define SUBFRAME buf[28] -#define FRAME buf[27] - -static volatile bool force_quit; - -unsigned short iq[33600]; -unsigned char iq_swap[67200]; - -unsigned short *iq_ptr[14]; -unsigned int dl_start = 0; - -/* MAC updating enabled by default */ -static int mac_updating = 1; -#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1 -#define MAX_PKT_BURST 32 -#define BURST_TX_DRAIN_US 5 /* TX drain every ~100us */ -#define MEMPOOL_CACHE_SIZE 256 -/* - * Configurable number of RX/TX ring descriptors - */ -#define RTE_TEST_RX_DESC_DEFAULT 1024 -#define RTE_TEST_TX_DESC_DEFAULT 1024 -static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; -static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; -/* ethernet addresses of ports */ -static struct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS]; -/* mask of enabled ports */ -static uint32_t l2fwd_enabled_port_mask = 0; -/* list of enabled ports */ -static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS]; -static unsigned int l2fwd_rx_queue_per_lcore = 1; -#define MAX_RX_QUEUE_PER_LCORE 16 -#define MAX_TX_QUEUE_PER_PORT 16 -struct lcore_queue_conf { - unsigned n_rx_port; - unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE]; -} __rte_cache_aligned; -struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE]; -static struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS]; -static struct rte_eth_conf port_conf = { - .rxmode = { - .split_hdr_size = 0, - .offloads = DEV_RX_OFFLOAD_JUMBO_FRAME, - .split_hdr_size = 0, - .max_rx_pkt_len = 9500, - }, - .txmode = { - .mq_mode = ETH_MQ_TX_NONE, - }, -}; -struct rte_mempool * l2fwd_pktmbuf_pool = NULL; -/* Per-port statistics struct */ -struct l2fwd_port_statistics { - uint64_t tx; - uint64_t rx; - uint64_t dropped; -} __rte_cache_aligned; -struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS]; -#define MAX_TIMER_PERIOD 86400 /* 1 day max */ -/* A tsc-based timer responsible for triggering statistics printout */ -static uint64_t timer_period = 10; /* default period is 10 seconds */ -/* Print out statistics on packets dropped */ -static void -print_stats(void) -{ - uint64_t total_packets_dropped, total_packets_tx, total_packets_rx; - unsigned portid; - total_packets_dropped = 0; - total_packets_tx = 0; - total_packets_rx = 0; - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H','\0' }; - /* Clear screen and move to top left */ - printf("%s%s", clr, topLeft); - printf("\nPort statistics ===================================="); - for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { - /* skip disabled ports */ - if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) - continue; - printf("\nStatistics for port %u ------------------------------" - "\nPackets sent: %24"PRIu64 - "\nPackets received: %20"PRIu64 - "\nPackets dropped: %21"PRIu64, - portid, - port_statistics[portid].tx, - port_statistics[portid].rx, - port_statistics[portid].dropped); - total_packets_dropped += port_statistics[portid].dropped; - total_packets_tx += port_statistics[portid].tx; - total_packets_rx += port_statistics[portid].rx; - } - printf("\nAggregate statistics ===============================" - "\nTotal packets sent: %18"PRIu64 - "\nTotal packets received: %14"PRIu64 - "\nTotal packets dropped: %15"PRIu64, - total_packets_tx, - total_packets_rx, - total_packets_dropped); - printf("\n====================================================\n"); -} -static void -l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid, benetel_t *bs) -{ - unsigned char *buf, *buf_tx; - unsigned int len; - struct ether_hdr *eth; - - unsigned dst_port; - int sent, prach_ctrl = 0; - struct rte_eth_dev_tx_buffer *buffer; - - unsigned char * IQ_ptr; - - ul_packet_t p; - - buf = rte_pktmbuf_mtod(m, unsigned char *); - //rte_memdump(stdout, "Rx Packet",buf ,34); - eth = rte_pktmbuf_mtod(m, struct ether_hdr *); - len = rte_pktmbuf_data_len(m); - - IQ_ptr = &buf[34]; - - //destination mac address (RRU MAC) - eth->d_addr.addr_bytes[0] = 0x02; - eth->d_addr.addr_bytes[1] = 0x00; - eth->d_addr.addr_bytes[2] = 0x5e; - eth->d_addr.addr_bytes[3] = 0x01; - eth->d_addr.addr_bytes[4] = 0x01; - eth->d_addr.addr_bytes[5] = 0x01; - - //source mac address (DU MAC) - eth->s_addr.addr_bytes[0] = 0xdd; - eth->s_addr.addr_bytes[1] = 0xdd; - eth->s_addr.addr_bytes[2] = 0xdd; - eth->s_addr.addr_bytes[3] = 0xdd; - eth->s_addr.addr_bytes[4] = 0xdd; - eth->s_addr.addr_bytes[5] = 0xdd; - - dst_port = l2fwd_dst_ports[portid]; - #if 1 - - if (buf[18] == 0xAA) //msg1 check - { - printf("\nHandshake MSG 1 Received\n"); - printf("\n====================================================\n"); - - buf[18] = 0xBB;//change to msg2 after receive msg1 - - printf("Handshake MSG 2 Sent\n"); - printf("\n====================================================\n"); - } - - #else - // Handshake Condition - if (buf[18] == 0x01 || buf[18] == 0x03) // ORAN handshake msgs. - { - if (buf[18] == 0x01) //msg1 check - { - printf("\nHandshake MSG 1 Received\n"); - printf("\n====================================================\n"); - - buf[18] = 0x02;//change to msg2 after receive msg1 - - printf("Handshake MSG 2 Sent\n"); - printf("\n====================================================\n"); - } - - else if(buf[18] == 0x03) //msg3 check - { - printf("Handshake MSG 3 Sent\n"); - printf("\n====================================================\n"); - - buf[18] = 0x04; - - printf("Handshake MSG 4 Sent\n"); - printf("\n====================================================\n"); - } - } - #endif - // Trigger start send DL packets - if(PAYLOAD_1 == 0x12 && PAYLOAD_2 == 0xce && SYMBOL == 0x09 && ANT_NUM == 0x00 && dl_start == 0){ - - printf("\nU-Plane Started\n"); - printf("\n====================================================\n"); - - dl_start = 1; - } - - - if(PAYLOAD_1 == 0x12 && PAYLOAD_2 == 0xce) { - p.frame = FRAME; - p.subframe = SUBFRAME >> 4; - p.slot = 0; /* unused */ - p.symbol = SYMBOL; - p.antenna = ANT_NUM; - memcpy(p.iq, IQ_ptr, 4800); - store_ul(bs, &p); - } - - // U-PLANE UL PROCESSING - if(PAYLOAD_1 == 0x12 && PAYLOAD_2 == 0xce && dl_start == 1) - { - int a = ANT_NUM; - int tx_frame = FRAME; - int tx_subframe = SUBFRAME >> 4; - int tx_symbol = SYMBOL + 5; - if (tx_symbol > 13) { - tx_symbol -= 14; - tx_subframe++; - if (tx_subframe == 10) { - tx_subframe = 0; - tx_frame = (tx_frame + 1) & 255; - } - } - -// ANT_NUM = 0x00; -// SYMBOL = (SYMBOL + 5) % 14; - PAYLOAD_2 = 0xc8; - - FRAME = tx_frame; - SUBFRAME = tx_subframe << 4; - SYMBOL = tx_symbol; - - /* send actual DL data (if available) */ - lock_dl_buffer(bs->buffers, tx_subframe); - if (!(bs->buffers->dl_busy[a][tx_subframe] & (1 << tx_symbol))) { - printf("%s: warning, DL underflow (antenna %d sf.symbol %d.%d)\n", - __FUNCTION__, - a, - tx_subframe, tx_symbol); - memset(IQ_ptr, 0, 1200 * 4); - } else { - memcpy(IQ_ptr, bs->buffers->dl[a][tx_subframe] + tx_symbol * 1200*4, - 1200*4); - } - bs->buffers->dl_busy[a][tx_subframe] &= ~(1 << tx_symbol); - unlock_dl_buffer(bs->buffers, tx_subframe); - } - - // U-PLANE PRACH PROCESSING - else if(PAYLOAD_1 == 0x0d && PAYLOAD_2 == 0x28){ - if (ANT_NUM == 0) { - store_prach(bs, FRAME, SUBFRAME>>4, IQ_ptr); - } - - rte_pktmbuf_free(m); - return; - } - - // Send Packets - buffer = tx_buffer[dst_port]; - - sent = rte_eth_tx_buffer(dst_port, 0, buffer, m); - - if (sent) - port_statistics[dst_port].tx += sent; - -} -/* main processing loop */ -static void -l2fwd_main_loop(benetel_t *bs) -{ - struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; - struct rte_mbuf *m; - int sent; - unsigned lcore_id; - uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc; - unsigned i, j, portid, nb_rx; - struct lcore_queue_conf *qconf; - const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S * - BURST_TX_DRAIN_US; - struct rte_eth_dev_tx_buffer *buffer; - prev_tsc = 0; - timer_tsc = 0; - lcore_id = rte_lcore_id(); - qconf = &lcore_queue_conf[lcore_id]; - if (qconf->n_rx_port == 0) { - RTE_LOG(INFO, L2FWD, "lcore %u has nothing to do\n", lcore_id); - return; - } - RTE_LOG(INFO, L2FWD, "entering main loop on lcore %u\n", lcore_id); - for (i = 0; i < qconf->n_rx_port; i++) { - portid = qconf->rx_port_list[i]; - RTE_LOG(INFO, L2FWD, " -- lcoreid=%u portid=%u\n", lcore_id, - portid); - } - while (!force_quit) { - cur_tsc = rte_rdtsc(); - /* - * TX burst queue drain - */ - diff_tsc = cur_tsc - prev_tsc; - if (unlikely(diff_tsc > drain_tsc)) { - for (i = 0; i < qconf->n_rx_port; i++) { - portid = l2fwd_dst_ports[qconf->rx_port_list[i]]; - buffer = tx_buffer[portid]; - sent = rte_eth_tx_buffer_flush(portid, 0, buffer); - if (sent) - port_statistics[portid].tx += sent; - } - /* if timer is enabled */ - if (timer_period > 0) { - /* advance the timer */ - timer_tsc += diff_tsc; - /* if timer has reached its timeout */ - if (unlikely(timer_tsc >= timer_period)) { - /* do this only on master core */ - if (lcore_id == rte_get_master_lcore()) { - //print_stats(); - /* reset the timer */ - timer_tsc = 0; - } - } - } - prev_tsc = cur_tsc; - } - /* - * Read packet from RX queues - */ - for (i = 0; i < qconf->n_rx_port; i++) { - portid = qconf->rx_port_list[i]; - nb_rx = rte_eth_rx_burst(portid, 0, - pkts_burst, MAX_PKT_BURST); - port_statistics[portid].rx += nb_rx; - for (j = 0; j < nb_rx; j++) { - m = pkts_burst[j]; - rte_prefetch0(rte_pktmbuf_mtod(m, void *)); - l2fwd_simple_forward(m, portid, bs); - } - } - } -} -static int -l2fwd_launch_one_lcore(void *bs) -{ - l2fwd_main_loop(bs); - return 0; -} -/* display usage */ -static void -l2fwd_usage(const char *prgname) -{ - printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n" - " -p PORTMASK: hexadecimal bitmask of ports to configure\n" - " -q NQ: number of queue (=ports) per lcore (default is 1)\n" - " -T PERIOD: statistics will be refreshed each PERIOD seconds (0 to disable, 10 default, 86400 maximum)\n" - " --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n" - " When enabled:\n" - " - The source MAC address is replaced by the TX port MAC address\n" - " - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n", - prgname); -} -static int -l2fwd_parse_portmask(const char *portmask) -{ - char *end = NULL; - unsigned long pm; - /* parse hexadecimal string */ - pm = strtoul(portmask, &end, 16); - if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) - return -1; - if (pm == 0) - return -1; - return pm; -} -static unsigned int -l2fwd_parse_nqueue(const char *q_arg) -{ - char *end = NULL; - unsigned long n; - /* parse hexadecimal string */ - n = strtoul(q_arg, &end, 10); - if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) - return 0; - if (n == 0) - return 0; - if (n >= MAX_RX_QUEUE_PER_LCORE) - return 0; - return n; -} -static int -l2fwd_parse_timer_period(const char *q_arg) -{ - char *end = NULL; - int n; - /* parse number string */ - n = strtol(q_arg, &end, 10); - if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) - return -1; - if (n >= MAX_TIMER_PERIOD) - return -1; - return n; -} -static const char short_options[] = - "p:" /* portmask */ - "q:" /* number of queues */ - "T:" /* timer period */ - ; -#define CMD_LINE_OPT_MAC_UPDATING "mac-updating" -#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" -enum { - /* long options mapped to a short option */ - /* first long only option value must be >= 256, so that we won't - * conflict with short options */ - CMD_LINE_OPT_MIN_NUM = 256, -}; -static const struct option lgopts[] = { - { CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1}, - { CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0}, - {NULL, 0, 0, 0} -}; -/* Parse the argument given in the command line of the application */ -static int -l2fwd_parse_args(int argc, char **argv) -{ - int opt, ret, timer_secs; - char **argvopt; - int option_index; - char *prgname = argv[0]; - argvopt = argv; - while ((opt = getopt_long(argc, argvopt, short_options, - lgopts, &option_index)) != EOF) { - switch (opt) { - /* portmask */ - case 'p': - l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg); - if (l2fwd_enabled_port_mask == 0) { - printf("invalid portmask\n"); - l2fwd_usage(prgname); - return -1; - } - break; - /* nqueue */ - case 'q': - l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg); - if (l2fwd_rx_queue_per_lcore == 0) { - printf("invalid queue number\n"); - l2fwd_usage(prgname); - return -1; - } - break; - /* timer period */ - case 'T': - timer_secs = l2fwd_parse_timer_period(optarg); - if (timer_secs < 0) { - printf("invalid timer period\n"); - l2fwd_usage(prgname); - return -1; - } - timer_period = timer_secs; - break; - /* long options */ - case 0: - break; - default: - l2fwd_usage(prgname); - return -1; - } - } - if (optind >= 0) - argv[optind-1] = prgname; - ret = optind-1; - optind = 1; /* reset getopt lib */ - return ret; -} -/* Check the link status of all ports in up to 9s, and print them finally */ -static void -check_all_ports_link_status(uint32_t port_mask) -{ -#define CHECK_INTERVAL 100 /* 100ms */ -#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ - uint16_t portid; - uint8_t count, all_ports_up, print_flag = 0; - struct rte_eth_link link; - printf("\nChecking link status"); - fflush(stdout); - for (count = 0; count <= MAX_CHECK_TIME; count++) { - if (force_quit) - return; - all_ports_up = 1; - RTE_ETH_FOREACH_DEV(portid) { - if (force_quit) - return; - if ((port_mask & (1 << portid)) == 0) - continue; - memset(&link, 0, sizeof(link)); - rte_eth_link_get_nowait(portid, &link); - /* print link status if flag set */ - if (print_flag == 1) { - if (link.link_status) - printf( - "Port%d Link Up. Speed %u Mbps - %s\n", - portid, link.link_speed, - (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? - ("full-duplex") : ("half-duplex\n")); - else - printf("Port %d Link Down\n", portid); - continue; - } - /* clear all_ports_up flag if any link down */ - if (link.link_status == ETH_LINK_DOWN) { - all_ports_up = 0; - break; - } - } - /* after finally printing all link status, get out */ - if (print_flag == 1) - break; - if (all_ports_up == 0) { - printf("."); - fflush(stdout); - rte_delay_ms(CHECK_INTERVAL); - } - /* set the print_flag if all ports up or timeout */ - if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) { - print_flag = 1; - printf("done\n"); - } - } -} -static void -signal_handler(int signum) -{ - if (signum == SIGINT || signum == SIGTERM) { - printf("\n\nSignal %d received, preparing to exit...\n", - signum); - force_quit = true; - } -} -int -dpdk_main(int argc, char **argv, benetel_t *bs) -{ - struct lcore_queue_conf *qconf; - int ret; - uint16_t nb_ports; - uint16_t nb_ports_available = 0; - uint16_t portid, last_port; - unsigned lcore_id, rx_lcore_id; - unsigned nb_ports_in_mask = 0; - unsigned int nb_lcores = 0; - unsigned int nb_mbufs; - /* init EAL */ - ret = rte_eal_init(argc, argv); - if (ret < 0) - rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); - argc -= ret; - argv += ret; - force_quit = false; - signal(SIGINT, signal_handler); - signal(SIGTERM, signal_handler); - /* parse application arguments (after the EAL ones) */ - ret = l2fwd_parse_args(argc, argv); - if (ret < 0) - rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n"); - printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled"); - /* convert to number of cycles */ - timer_period *= rte_get_timer_hz(); - nb_ports = rte_eth_dev_count_avail(); - if (nb_ports == 0) - rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); - /* check port mask to possible port mask */ - if (l2fwd_enabled_port_mask & ~((1 << nb_ports) - 1)) - rte_exit(EXIT_FAILURE, "Invalid portmask; possible (0x%x)\n", - (1 << nb_ports) - 1); - /* reset l2fwd_dst_ports */ - for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) - l2fwd_dst_ports[portid] = 0; - last_port = 0; - /* - * Each logical core is assigned a dedicated TX queue on each port. - */ - RTE_ETH_FOREACH_DEV(portid) { - /* skip ports that are not enabled */ - if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) - continue; - if (nb_ports_in_mask % 2) { - l2fwd_dst_ports[portid] = last_port; - l2fwd_dst_ports[last_port] = portid; - } - else - last_port = portid; - nb_ports_in_mask++; - } - if (nb_ports_in_mask % 2) { - printf("Notice: odd number of ports in portmask.\n"); - l2fwd_dst_ports[last_port] = last_port; - } - rx_lcore_id = 0; - qconf = NULL; - /* Initialize the port/queue configuration of each logical core */ - RTE_ETH_FOREACH_DEV(portid) { - /* skip ports that are not enabled */ - if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) - continue; - /* get the lcore_id for this port */ - while (rte_lcore_is_enabled(rx_lcore_id) == 0 || - lcore_queue_conf[rx_lcore_id].n_rx_port == - l2fwd_rx_queue_per_lcore) { - rx_lcore_id++; - if (rx_lcore_id >= RTE_MAX_LCORE) - rte_exit(EXIT_FAILURE, "Not enough cores\n"); - } - if (qconf != &lcore_queue_conf[rx_lcore_id]) { - /* Assigned a new logical core in the loop above. */ - qconf = &lcore_queue_conf[rx_lcore_id]; - nb_lcores++; - } - qconf->rx_port_list[qconf->n_rx_port] = portid; - qconf->n_rx_port++; - printf("Lcore %u: RX port %u\n", rx_lcore_id, portid); - } - nb_mbufs = RTE_MAX(nb_ports * (nb_rxd + nb_txd + MAX_PKT_BURST + - nb_lcores * MEMPOOL_CACHE_SIZE), 8192U); - /* create the mbuf pool */ - l2fwd_pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", nb_mbufs, - MEMPOOL_CACHE_SIZE, 0, 9680, - rte_socket_id()); - if (l2fwd_pktmbuf_pool == NULL) - rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n"); - /* Initialise each port */ - RTE_ETH_FOREACH_DEV(portid) { - struct rte_eth_rxconf rxq_conf; - struct rte_eth_txconf txq_conf; - struct rte_eth_conf local_port_conf = port_conf; - struct rte_eth_dev_info dev_info; - /* skip ports that are not enabled */ - if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) { - printf("Skipping disabled port %u\n", portid); - continue; - } - nb_ports_available++; - /* init port */ - printf("Initializing port %u... ", portid); - fflush(stdout); - rte_eth_dev_info_get(portid, &dev_info); - if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) - local_port_conf.txmode.offloads |= - DEV_TX_OFFLOAD_MBUF_FAST_FREE; - ret = rte_eth_dev_configure(portid, 1, 1, &local_port_conf); - if (ret < 0) - rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n", - ret, portid); - ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd, - &nb_txd); - if (ret < 0) - rte_exit(EXIT_FAILURE, - "Cannot adjust number of descriptors: err=%d, port=%u\n", - ret, portid); - rte_eth_macaddr_get(portid,&l2fwd_ports_eth_addr[portid]); - /* init one RX queue */ - fflush(stdout); - rxq_conf = dev_info.default_rxconf; - rxq_conf.offloads = local_port_conf.rxmode.offloads; - ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd, - rte_eth_dev_socket_id(portid), - &rxq_conf, - l2fwd_pktmbuf_pool); - if (ret < 0) - rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup:err=%d, port=%u\n", - ret, portid); - /* init one TX queue on each port */ - fflush(stdout); - txq_conf = dev_info.default_txconf; - txq_conf.offloads = local_port_conf.txmode.offloads; - ret = rte_eth_tx_queue_setup(portid, 0, nb_txd, - rte_eth_dev_socket_id(portid), - &txq_conf); - if (ret < 0) - rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup:err=%d, port=%u\n", - ret, portid); - /* Initialize TX buffers */ - tx_buffer[portid] = rte_zmalloc_socket("tx_buffer", - RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0, - rte_eth_dev_socket_id(portid)); - if (tx_buffer[portid] == NULL) - rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx on port %u\n", - portid); - rte_eth_tx_buffer_init(tx_buffer[portid], MAX_PKT_BURST); - ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[portid], - rte_eth_tx_buffer_count_callback, - &port_statistics[portid].dropped); - if (ret < 0) - rte_exit(EXIT_FAILURE, - "Cannot set error callback for tx buffer on port %u\n", - portid); - /* Start device */ - ret = rte_eth_dev_start(portid); - if (ret < 0) - rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err=%d, port=%u\n", - ret, portid); - printf("done: \n"); - rte_eth_promiscuous_enable(portid); - printf("Port %u, MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n\n", - portid, - l2fwd_ports_eth_addr[portid].addr_bytes[0], - l2fwd_ports_eth_addr[portid].addr_bytes[1], - l2fwd_ports_eth_addr[portid].addr_bytes[2], - l2fwd_ports_eth_addr[portid].addr_bytes[3], - l2fwd_ports_eth_addr[portid].addr_bytes[4], - l2fwd_ports_eth_addr[portid].addr_bytes[5]); - /* initialize port stats */ - memset(&port_statistics, 0, sizeof(port_statistics)); - } - if (!nb_ports_available) { - rte_exit(EXIT_FAILURE, - "All available ports are disabled. Please set portmask.\n"); - } - check_all_ports_link_status(l2fwd_enabled_port_mask); - ret = 0; - /* launch per-lcore init on every lcore */ - rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, bs, CALL_MASTER); - RTE_LCORE_FOREACH_SLAVE(lcore_id) { - if (rte_eal_wait_lcore(lcore_id) < 0) { - ret = -1; - break; - } - } - RTE_ETH_FOREACH_DEV(portid) { - if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) - continue; - printf("Closing port %d...", portid); - rte_eth_dev_stop(portid); - rte_eth_dev_close(portid); - printf(" Done\n"); - } - printf("Bye...\n"); - return ret; -} diff --git a/radio/ETHERNET/benetel/4g/low.c b/radio/ETHERNET/benetel/4g/low.c deleted file mode 100644 index 14783a700b..0000000000 --- a/radio/ETHERNET/benetel/4g/low.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#include "low.h" - -#include -#include -#include - -void store_ul(benetel_t *bs, ul_packet_t *ul) -{ - int a = ul->antenna; - - if (ul->subframe != bs->next_subframe[a] || - ul->symbol != bs->next_symbol[a]) { - printf("%s: fatal, antenna %d expected frame.sf.symbol %d.%d.%d, got %d.%d.%d\n", - __FUNCTION__, - a, - bs->expected_benetel_frame[a], bs->next_subframe[a], bs->next_symbol[a], - ul->frame, ul->subframe, ul->symbol); - exit(1); - } - - lock_ul_buffer(bs->buffers, bs->next_subframe[a]); - if (bs->buffers->ul_busy[a][bs->next_subframe[a]] & (1 << bs->next_symbol[a])) { - printf("%s: warning, UL overflow (sf.symbol %d.%d)\n", __FUNCTION__, - bs->next_subframe[a], bs->next_symbol[a]); - } - memcpy(bs->buffers->ul[a][bs->next_subframe[a]] + bs->next_symbol[a] * 1200*4, - ul->iq, 1200*4); - bs->buffers->ul_busy[a][bs->next_subframe[a]] |= (1 << bs->next_symbol[a]); - signal_ul_buffer(bs->buffers, bs->next_subframe[a]); - unlock_ul_buffer(bs->buffers, bs->next_subframe[a]); - - bs->next_symbol[a]++; - if (bs->next_symbol[a] == 14) { - bs->next_symbol[a] = 0; - bs->next_subframe[a] = (bs->next_subframe[a] + 1) % 10; - if (bs->next_subframe[a] == 0) { - bs->expected_benetel_frame[a]++; - bs->expected_benetel_frame[a] &= 255; - } - } -} - -void store_prach(benetel_t *bs, int frame, int subframe, void *data) -{ - static int last_frame = -1; - static int last_subframe = -1; - /* hack: antenna number is always 0, discard second packet with same f/sf */ - if (frame == last_frame && subframe == last_subframe) return; - last_frame = frame; - last_subframe = subframe; - - lock_ul_buffer(bs->buffers, subframe); - if (bs->buffers->prach_busy[subframe]) { - printf("store_prach: fatal: previous prach buffer not processed\n"); - unlock_ul_buffer(bs->buffers, subframe); - return; - } - bs->buffers->prach_busy[subframe] = 1; - memcpy(bs->buffers->prach[subframe], data, 849*4); - signal_ul_buffer(bs->buffers, subframe); - unlock_ul_buffer(bs->buffers, subframe); - -} - -void *benetel_start_dpdk(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line); - -void *benetel_start(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line) -{ - if (!strcmp(ifname, "dpdk")) - return benetel_start_dpdk(ifname, buffers, dpdk_main_command_line); - printf("benetel: fatal: interface %s not supported, only dpdpk is supported\n", ifname); - exit(1); -} diff --git a/radio/ETHERNET/benetel/4g/low.h b/radio/ETHERNET/benetel/4g/low.h deleted file mode 100644 index e74bf89163..0000000000 --- a/radio/ETHERNET/benetel/4g/low.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#ifndef _BENETEL_4G_LOW_H_ -#define _BENETEL_4G_LOW_H_ - -#include "shared_buffers.h" - -typedef struct { - shared_buffers *buffers; - /* [2] is for two antennas */ - int next_subframe[2]; - int next_symbol[2]; - int expected_benetel_frame[2]; - char *dpdk_main_command_line; -} benetel_t; - -typedef struct { - int frame; - int subframe; - int slot; - int symbol; - int antenna; - unsigned char iq[4800]; -} ul_packet_t; - -void *benetel_start(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line); - -void store_ul(benetel_t *bs, ul_packet_t *ul); -void store_prach(benetel_t *bs, int frame, int subframe, void *data); - -#endif /* _BENETEL_4G_LOW_H_ */ diff --git a/radio/ETHERNET/benetel/4g/low_dpdk.c b/radio/ETHERNET/benetel/4g/low_dpdk.c deleted file mode 100644 index edf62a67e0..0000000000 --- a/radio/ETHERNET/benetel/4g/low_dpdk.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -/* those 2 lines for CPU_SET */ -#define _GNU_SOURCE -#include - -#include -#include -#include -#include - -#include "low.h" - -int dpdk_main(int argc, char **argv, benetel_t *); - -void *dpdk_thread(void *_bs) -{ - benetel_t *bs = _bs; - int n = 0; - char **v = NULL; // { "softmodem", "-n", "2", "-l", "8", "--", "-p", "0x2" }; - char *s = bs->dpdk_main_command_line; - char *tok; - - while ((tok = strtok(s, " ")) != NULL) { - n++; - v = realloc(v, n * sizeof(char *)); - if (v == NULL) { - printf("%s: out of memory\n", __FUNCTION__); - exit(1); - } - v[n-1] = tok; - s = NULL; - } - - dpdk_main(n, v, bs); - exit(1); - return 0; -} - -void *benetel_start_dpdk(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line) -{ - benetel_t *bs; - - bs = calloc(1, sizeof(benetel_t)); - if (bs == NULL) { - printf("%s: out of memory\n", __FUNCTION__); - exit(1); - } - - bs->buffers = buffers; - - bs->expected_benetel_frame[0] = 255; - bs->expected_benetel_frame[1] = 255; - - bs->dpdk_main_command_line = dpdk_main_command_line; - - pthread_attr_t attr; - - if (pthread_attr_init(&attr) != 0) { - printf("pthread_attr_init failed\n"); - exit(1); - } - - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(12,&cpuset); - if (pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset) != 0) { - printf("pthread_attr_setaffinity_np failed\n"); - exit(1); - } - - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) != 0) { - printf("pthread_attr_setschedpolicy failed\n"); - exit(1); - } - - struct sched_param params; - params.sched_priority = sched_get_priority_max(SCHED_FIFO); - if (sched_get_priority_max(SCHED_FIFO) == -1) { - printf("sched_get_priority_max failed\n"); - exit(1); - } - if (pthread_attr_setschedparam(&attr, ¶ms) != 0) { - printf("pthread_setschedparam failed\n"); - exit(1); - } - - pthread_t t; - if (pthread_create(&t, &attr, dpdk_thread, bs) != 0) { - printf("%s: thread creation failed\n", __FUNCTION__); - exit(1); - } - - if (pthread_attr_destroy(&attr) != 0) { - printf("pthread_attr_init failed\n"); - exit(1); - } - - return bs; -} diff --git a/radio/ETHERNET/benetel/4g/shared_buffers.c b/radio/ETHERNET/benetel/4g/shared_buffers.c deleted file mode 100644 index 380cbabc73..0000000000 --- a/radio/ETHERNET/benetel/4g/shared_buffers.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#include "shared_buffers.h" - -#include -#include -#include - -void init_buffers(shared_buffers *s) -{ - int subframe; - - memset(s, 0, sizeof(*s)); - - for (subframe = 0; subframe < 10; subframe++) { - if (pthread_mutex_init(&s->m_dl[subframe], NULL) != 0 || - pthread_cond_init(&s->c_dl[subframe], NULL) != 0 || - pthread_mutex_init(&s->m_ul[subframe], NULL) != 0 || - pthread_cond_init(&s->c_ul[subframe], NULL) != 0) { - printf("%s: error initializing mutex/cond\n", __FUNCTION__); - exit(1); - } - } - - /* in FDD the eNB's first transmitted DL subframe is 4 but the device - * needs to have subframes 1, 2 and 3 ready. Let's pretend there are ready. - */ - s->dl_busy[0][1] = 0x3fff; - s->dl_busy[0][2] = 0x3fff; - s->dl_busy[0][3] = 0x3fff; - s->dl_busy[1][1] = 0x3fff; - s->dl_busy[1][2] = 0x3fff; - s->dl_busy[1][3] = 0x3fff; -} - -void lock_dl_buffer(shared_buffers *s, int subframe) -{ - if (pthread_mutex_lock(&s->m_dl[subframe]) != 0) { - printf("%s: fatal: lock fails\n", __FUNCTION__); - exit(1); - } -} - -void unlock_dl_buffer(shared_buffers *s, int subframe) -{ - if (pthread_mutex_unlock(&s->m_dl[subframe]) != 0) { - printf("%s: fatal: unlock fails\n", __FUNCTION__); - exit(1); - } -} - -void wait_dl_buffer(shared_buffers *s, int subframe) -{ - if (pthread_cond_wait(&s->c_dl[subframe], &s->m_dl[subframe]) != 0) { - printf("%s: fatal: cond_wait fails\n", __FUNCTION__); - exit(1); - } -} - -void signal_dl_buffer(shared_buffers *s, int subframe) -{ - if (pthread_cond_broadcast(&s->c_dl[subframe]) != 0) { - printf("%s: fatal: cond_broadcast fails\n", __FUNCTION__); - exit(1); - } -} - -void lock_ul_buffer(shared_buffers *s, int subframe) -{ - if (pthread_mutex_lock(&s->m_ul[subframe]) != 0) { - printf("%s: fatal: lock fails\n", __FUNCTION__); - exit(1); - } -} - -void unlock_ul_buffer(shared_buffers *s, int subframe) -{ - if (pthread_mutex_unlock(&s->m_ul[subframe]) != 0) { - printf("%s: fatal: unlock fails\n", __FUNCTION__); - exit(1); - } -} - -void wait_ul_buffer(shared_buffers *s, int subframe) -{ - if (pthread_cond_wait(&s->c_ul[subframe], &s->m_ul[subframe]) != 0) { - printf("%s: fatal: cond_wait fails\n", __FUNCTION__); - exit(1); - } -} - -void signal_ul_buffer(shared_buffers *s, int subframe) -{ - if (pthread_cond_broadcast(&s->c_ul[subframe]) != 0) { - printf("%s: fatal: cond_broadcast fails\n", __FUNCTION__); - exit(1); - } -} diff --git a/radio/ETHERNET/benetel/4g/shared_buffers.h b/radio/ETHERNET/benetel/4g/shared_buffers.h deleted file mode 100644 index ed174265c5..0000000000 --- a/radio/ETHERNET/benetel/4g/shared_buffers.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#ifndef _BENETEL_4G_SHARED_BUFFERS_H_ -#define _BENETEL_4G_SHARED_BUFFERS_H_ - -#include -#include - -typedef struct { - /* [2] is for two antennas */ - unsigned char dl[2][10][14*1200*4]; - unsigned char ul[2][10][14*1200*4]; - uint16_t dl_busy[2][10]; - uint16_t ul_busy[2][10]; - - pthread_mutex_t m_ul[10]; - pthread_cond_t c_ul[10]; - - pthread_mutex_t m_dl[10]; - pthread_cond_t c_dl[10]; - - unsigned char prach[10][849*4]; - unsigned char prach_busy[10]; - - /* statistics/error counting */ - int ul_overflow; - int dl_underflow; -} shared_buffers; - -void init_buffers(shared_buffers *s); - -void lock_dl_buffer(shared_buffers *s, int subframe); -void unlock_dl_buffer(shared_buffers *s, int subframe); -void wait_dl_buffer(shared_buffers *s, int subframe); -void signal_dl_buffer(shared_buffers *s, int subframe); - -void lock_ul_buffer(shared_buffers *s, int subframe); -void unlock_ul_buffer(shared_buffers *s, int subframe); -void wait_ul_buffer(shared_buffers *s, int subframe); -void signal_ul_buffer(shared_buffers *s, int subframe); - -#endif /* _BENETEL_4G_SHARED_BUFFERS_H_ */ diff --git a/radio/ETHERNET/benetel/5g/CMakeLists.txt b/radio/ETHERNET/benetel/5g/CMakeLists.txt deleted file mode 100644 index cdad594ce3..0000000000 --- a/radio/ETHERNET/benetel/5g/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -pkg_check_modules(libdpdk REQUIRED libdpdk=20.05.0) - -add_library(benetel_5g MODULE - benetel.c - shared_buffers.c - low.c - low_dpdk.c - dpdk_driver.c -) - -SET(DPDK_LIBS "-Wl,-rpath,$ENV{RTE_SDK}/$ENV{RTE_TARGET}/lib -Wl,--whole-archive -L$ENV{RTE_SDK}/$ENV{RTE_TARGET}/lib -ldpdk -Wl,--no-whole-archive") -target_include_directories(benetel_5g PRIVATE ${libdpdk_INCLUDE_DIRS}) -target_link_libraries(benetel_5g PRIVATE ${libdpdk_LIBRARIES}) -target_link_libraries(benetel_5g PRIVATE pthread dl rt m numa) -set_target_properties(benetel_5g PROPERTIES COMPILE_FLAGS "-fvisibility=hidden -I$ENV{RTE_SDK}/$ENV{RTE_TARGET}/include") -set_target_properties(benetel_5g PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -add_custom_command(TARGET benetel_5g POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink libbenetel_5g.so liboai_transpro.so - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/radio/ETHERNET/benetel/5g/benetel.c b/radio/ETHERNET/benetel/5g/benetel.c deleted file mode 100644 index 996d568767..0000000000 --- a/radio/ETHERNET/benetel/5g/benetel.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#include - -#include "common_lib.h" -#include "ethernet_lib.h" -#include "shared_buffers.h" -#include "low.h" -#include "openair1/PHY/defs_gNB.h" - -typedef struct { - eth_state_t e; - shared_buffers buffers; - rru_config_msg_type_t last_msg; - int capabilities_sent; - void *benetel_priv; - char *dpdk_main_command_line; -} benetel_eth_state_t; - -int trx_benetel_start(openair0_device *device) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return 0; -} - - -void trx_benetel_end(openair0_device *device) -{ - printf("BENETEL: %s\n", __FUNCTION__); -} - - -int trx_benetel_stop(openair0_device *device) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return(0); -} - - -int trx_benetel_set_freq(openair0_device* device, openair0_config_t *openair0_cfg) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return(0); -} - - -int trx_benetel_set_gains(openair0_device* device, - openair0_config_t *openair0_cfg) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return(0); -} - - -int trx_benetel_get_stats(openair0_device* device) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return(0); -} - - -int trx_benetel_reset_stats(openair0_device* device) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return(0); -} - - -int ethernet_tune(openair0_device *device, - unsigned int option, - int value) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return 0; -} - -int trx_benetel_write_raw(openair0_device *device, - openair0_timestamp timestamp, - void **buff, int nsamps, int cc, int flags) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return nsamps*4; -} - -int trx_benetel_read_raw(openair0_device *device, - openair0_timestamp *timestamp, - void **buff, int nsamps, int cc) -{ - printf("BENETEL: %s\n", __FUNCTION__); - return nsamps*4; -} - -char *msg_type(int t) -{ - static char *s[12] = { - "RAU_tick", - "RRU_capabilities", - "RRU_config", - "RRU_config_ok", - "RRU_start", - "RRU_stop", - "RRU_sync_ok", - "RRU_frame_resynch", - "RRU_MSG_max_num", - "RRU_check_sync", - "RRU_config_update", - "RRU_config_update_ok", - }; - - if (t < 0 || t > 11) return "UNKNOWN"; - return s[t]; -} - -int trx_benetel_ctlsend(openair0_device *device, void *msg, ssize_t msg_len) -{ - RRU_CONFIG_msg_t *rru_config_msg = msg; - benetel_eth_state_t *s = device->priv; - - printf("BENETEL: %s\n", __FUNCTION__); - - printf(" rru_config_msg->type %d [%s]\n", rru_config_msg->type, - msg_type(rru_config_msg->type)); - - s->last_msg = rru_config_msg->type; - - return msg_len; -} - -int trx_benetel_ctlrecv(openair0_device *device, void *msg, ssize_t msg_len) -{ - RRU_CONFIG_msg_t *rru_config_msg = msg; - benetel_eth_state_t *s = device->priv; - - printf("BENETEL: %s\n", __FUNCTION__); - - if (s->last_msg == RAU_tick && s->capabilities_sent == 0) { - RRU_capabilities_t *cap; - rru_config_msg->type = RRU_capabilities; - rru_config_msg->len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_capabilities_t); - cap = (RRU_capabilities_t*)&rru_config_msg->msg[0]; - cap->FH_fmt = OAI_IF4p5_only; - cap->num_bands = 1; - cap->band_list[0] = 78; - /* TODO: hardcoded to 1 for the moment, get the real value somehow... */ - cap->nb_rx[0] = 1; //device->openair0_cfg->rx_num_channels; - cap->nb_tx[0] = 1; //device->openair0_cfg->tx_num_channels; - cap->max_pdschReferenceSignalPower[0] = -27; - cap->max_rxgain[0] = 90; - - s->capabilities_sent = 1; - - return rru_config_msg->len; - } -#if 0 - if (s->last_msg == RRU_config) { - rru_config_msg->type = RRU_config_ok; - return 0; - } - if (s->last_msg == RRU_start) { -printf("***************** benetel start\n"); - s->benetel_priv = benetel_start(s->e.if_name, &s->buffers, s->dpdk_main_command_line); - } -#endif - if (s->last_msg == RRU_config) { - rru_config_msg->type = RRU_config_ok; - s->benetel_priv = benetel_start(s->e.if_name, &s->buffers, s->dpdk_main_command_line); - } - /* correct? */ -//printf("***************** benetel pause\n"); -// while (1) pause(); - - return 0; -} - -void benetel_fh_if4p5_south_in(RU_t *ru, - int *frame, - int *slot) -{ -//printf("XXX benetel_fh_if4p5_south_in %d %d\n", *frame, *slot); - benetel_eth_state_t *s = ru->ifdevice.priv; - NR_DL_FRAME_PARMS *fp; - int symbol; - int32_t *rxdata; - int antenna; - - lock_ul_buffer(&s->buffers, *slot); -#if 1 -next: - while (!((s->buffers.ul_busy[0][*slot] == 0x3fff && - s->buffers.ul_busy[1][*slot] == 0x3fff) || - s->buffers.prach_busy[*slot] == 1)) - wait_ul_buffer(&s->buffers, *slot); - if (s->buffers.prach_busy[*slot] == 1) { - int i; - int antenna = 0; - uint16_t *in; - uint16_t *out; - in = (uint16_t *)s->buffers.prach[*slot]; - out = (uint16_t *)ru->prach_rxsigF[0][antenna]; - for (i = 0; i < 839*2; i++) - out[i] = ntohs(in[i]); - s->buffers.prach_busy[*slot] = 0; - //printf("prach for f.sl %d.%d\n", *frame, *slot); - //ru->wakeup_prach_gNB(ru->gNB_list[0], ru, *frame, *slot); - goto next; - } -#endif - - fp = ru->nr_frame_parms; - for (antenna = 0; antenna < ru->nb_rx; antenna++) { - for (symbol = 0; symbol < 14; symbol++) { - int i; - int16_t *p = (int16_t *)(&s->buffers.ul[antenna][*slot][symbol*1272*4]); - for (i = 0; i < 1272*2; i++) { - p[i] = (int16_t)(ntohs(p[i])) / 16; - } - rxdata = &ru->common.rxdataF[antenna][symbol * fp->ofdm_symbol_size]; -#if 0 -if (*slot == 0 && symbol == 0) -printf("rxdata in benetel_fh_if4p5_south_in %p\n", &ru->common.rxdataF[antenna][0]); -#endif -#if 1 - memcpy(rxdata + 2048 - 1272/2, - &s->buffers.ul[antenna][*slot][symbol*1272*4], - (1272/2) * 4); - memcpy(rxdata, - &s->buffers.ul[antenna][*slot][symbol*1272*4] + (1272/2)*4, - (1272/2) * 4); -#endif - } - } - - s->buffers.ul_busy[0][*slot] = 0; - s->buffers.ul_busy[1][*slot] = 0; - signal_ul_buffer(&s->buffers, *slot); - unlock_ul_buffer(&s->buffers, *slot); - - //printf("BENETEL: %s (f.sf %d.%d)\n", __FUNCTION__, *frame, *slot); - - RU_proc_t *proc = &ru->proc; - int f = *frame; - int sl = *slot; - - //calculate timestamp_rx, timestamp_tx based on frame and slot - proc->tti_rx = sl; - proc->frame_rx = f; - /* TODO: be sure of samples_per_slot0 - FK: should use get_samples_per_slot(slot) - but for mu=1 its ok - */ - proc->timestamp_rx = ((proc->frame_rx * 20) + proc->tti_rx ) * fp->samples_per_slot0; - - if (get_nprocs()<=4) { - // why? what if there are more? - proc->tti_tx = (sl+ru->sl_ahead)%20; - proc->frame_tx = (sl>(19-ru->sl_ahead)) ? (f+1)&1023 : f; - } -} - -void benetel_fh_if4p5_south_out(RU_t *ru, - int frame, - int slot, - uint64_t timestamp) -{ - -//printf("XXX benetel_fh_if4p5_south_out %d %d %ld\n", frame, slot, timestamp); - benetel_eth_state_t *s = ru->ifdevice.priv; - NR_DL_FRAME_PARMS *fp; - int symbol; - int32_t *txdata; - int aa; - - //printf("BENETEL: %s (f.sf %d.%d ts %ld)\n", __FUNCTION__, frame, slot, timestamp); - - lock_dl_buffer(&s->buffers, slot); - if (s->buffers.dl_busy[0][slot] || - s->buffers.dl_busy[1][slot]) { - printf("%s: fatal: DL buffer busy for subframe %d\n", __FUNCTION__, slot); - unlock_dl_buffer(&s->buffers, slot); - return; - } - - fp = ru->nr_frame_parms; - if (ru->num_gNB != 1 || fp->ofdm_symbol_size != 2048 || - fp->Ncp != NORMAL || fp->symbols_per_slot != 14) { - printf("%s:%d:%s: unsupported configuration\n", - __FILE__, __LINE__, __FUNCTION__); - exit(1); - } - - for (aa = 0; aa < ru->nb_tx; aa++) { - for (symbol = 0; symbol < 14; symbol++) { - txdata = &ru->common.txdataF_BF[aa][symbol * fp->ofdm_symbol_size]; -#if 1 - memcpy(&s->buffers.dl[aa][slot][symbol*1272*4], - txdata + 2048 - (1272/2), - (1272/2) * 4); - memcpy(&s->buffers.dl[aa][slot][symbol*1272*4] + (1272/2)*4, - txdata, - (1272/2) * 4); -#endif - int i; - uint16_t *p = (uint16_t *)(&s->buffers.dl[aa][slot][symbol*1272*4]); - for (i = 0; i < 1272*2; i++) { - p[i] = htons(p[i]); - } - } - } - - s->buffers.dl_busy[0][slot] = 0x3fff; - s->buffers.dl_busy[1][slot] = 0x3fff; - unlock_dl_buffer(&s->buffers, slot); -} - -void *get_internal_parameter(char *name) -{ - printf("BENETEL: %s\n", __FUNCTION__); - - if (!strcmp(name, "fh_if4p5_south_in")) - return benetel_fh_if4p5_south_in; - if (!strcmp(name, "fh_if4p5_south_out")) - return benetel_fh_if4p5_south_out; - - return NULL; -} - -__attribute__((__visibility__("default"))) -int transport_init(openair0_device *device, - openair0_config_t *openair0_cfg, - eth_params_t * eth_params ) -{ - benetel_eth_state_t *eth; - - printf("BENETEL: %s\n", __FUNCTION__); - - device->Mod_id = 0; - device->transp_type = ETHERNET_TP; - device->trx_start_func = trx_benetel_start; - device->trx_get_stats_func = trx_benetel_get_stats; - device->trx_reset_stats_func = trx_benetel_reset_stats; - device->trx_end_func = trx_benetel_end; - device->trx_stop_func = trx_benetel_stop; - device->trx_set_freq_func = trx_benetel_set_freq; - device->trx_set_gains_func = trx_benetel_set_gains; - - device->trx_write_func = trx_benetel_write_raw; - device->trx_read_func = trx_benetel_read_raw; - - device->trx_ctlsend_func = trx_benetel_ctlsend; - device->trx_ctlrecv_func = trx_benetel_ctlrecv; - - device->get_internal_parameter = get_internal_parameter; - - eth = calloc(1, sizeof(benetel_eth_state_t)); - if (eth == NULL) { - AssertFatal(0==1, "out of memory\n"); - } - - eth->e.flags = ETH_RAW_IF4p5_MODE; - eth->e.compression = NO_COMPRESS; - eth->e.if_name = eth_params->local_if_name; - device->priv = eth; - device->openair0_cfg=&openair0_cfg[0]; - - if (openair0_cfg->sdr_addrs == NULL) { - printf("benetel: fatal: sdr_addrs not set in configuration file\n"); - exit(1); - } - - eth->dpdk_main_command_line = strdup(openair0_cfg->sdr_addrs); - if (eth->dpdk_main_command_line == NULL) { - AssertFatal(0==1, "out of memory\n"); - } - - eth->last_msg = -1; - - init_buffers(ð->buffers); - - return 0; -} diff --git a/radio/ETHERNET/benetel/5g/dpdk_driver.c b/radio/ETHERNET/benetel/5g/dpdk_driver.c deleted file mode 100644 index 9b398e9bd9..0000000000 --- a/radio/ETHERNET/benetel/5g/dpdk_driver.c +++ /dev/null @@ -1,835 +0,0 @@ -/* license: to be defined */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "low.h" - -// HEADER DEFINITION -#define ANT_NUM buf[23] -#define PAYLOAD_1 buf[20] -#define PAYLOAD_2 buf[21] -#define ETH_TYPE buf[17] -#define SYMBOL buf[29] -#define SUBFRAME buf[28] -#define FRAME buf[27] - -static volatile bool force_quit; - -unsigned short iq[712320]; -unsigned char iq_swap[1424640]; - -unsigned short *iq_ptr[14]; -unsigned int dl_start = 0, slot_id_ctrl = 0, count_symbol = 0, sf = 0x10; - -/* MAC updating enabled by default */ -static int mac_updating = 1; -#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1 -#define MAX_PKT_BURST 32 -#define BURST_TX_DRAIN_US 5 /* TX drain every ~100us */ -#define MEMPOOL_CACHE_SIZE 256 -/* - * Configurable number of RX/TX ring descriptors - */ -#define RTE_TEST_RX_DESC_DEFAULT 1024 -#define RTE_TEST_TX_DESC_DEFAULT 1024 -static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; -static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; -/* ethernet addresses of ports */ -static struct ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS]; -/* mask of enabled ports */ -static uint32_t l2fwd_enabled_port_mask = 0; -/* list of enabled ports */ -static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS]; -static unsigned int l2fwd_rx_queue_per_lcore = 1; -#define MAX_RX_QUEUE_PER_LCORE 16 -#define MAX_TX_QUEUE_PER_PORT 16 -struct lcore_queue_conf { - unsigned n_rx_port; - unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE]; -} __rte_cache_aligned; -struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE]; -static struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS]; -static struct rte_eth_conf port_conf = { - .rxmode = { - .split_hdr_size = 0, - .offloads = DEV_RX_OFFLOAD_JUMBO_FRAME, - .split_hdr_size = 0, - .max_rx_pkt_len = 9500, - }, - .txmode = { - .mq_mode = ETH_MQ_TX_NONE, - }, -}; -struct rte_mempool * l2fwd_pktmbuf_pool = NULL; -/* Per-port statistics struct */ -struct l2fwd_port_statistics { - uint64_t tx; - uint64_t rx; - uint64_t dropped; -} __rte_cache_aligned; -struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS]; -#define MAX_TIMER_PERIOD 86400 /* 1 day max */ -/* A tsc-based timer responsible for triggering statistics printout */ -static uint64_t timer_period = 10; /* default period is 10 seconds */ -/* Print out statistics on packets dropped */ -static void -print_stats(void) -{ - uint64_t total_packets_dropped, total_packets_tx, total_packets_rx; - unsigned portid; - total_packets_dropped = 0; - total_packets_tx = 0; - total_packets_rx = 0; - const char clr[] = { 27, '[', '2', 'J', '\0' }; - const char topLeft[] = { 27, '[', '1', ';', '1', 'H','\0' }; - /* Clear screen and move to top left */ - printf("%s%s", clr, topLeft); - printf("\nPort statistics ===================================="); - for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { - /* skip disabled ports */ - if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) - continue; - printf("\nStatistics for port %u ------------------------------" - "\nPackets sent: %24"PRIu64 - "\nPackets received: %20"PRIu64 - "\nPackets dropped: %21"PRIu64, - portid, - port_statistics[portid].tx, - port_statistics[portid].rx, - port_statistics[portid].dropped); - total_packets_dropped += port_statistics[portid].dropped; - total_packets_tx += port_statistics[portid].tx; - total_packets_rx += port_statistics[portid].rx; - } - printf("\nAggregate statistics ===============================" - "\nTotal packets sent: %18"PRIu64 - "\nTotal packets received: %14"PRIu64 - "\nTotal packets dropped: %15"PRIu64, - total_packets_tx, - total_packets_rx, - total_packets_dropped); - printf("\n====================================================\n"); -} -static void -l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid, benetel_t *bs) -{ - unsigned char *buf, *buf_tx; - unsigned int len; - struct ether_hdr *eth; - - unsigned dst_port; - int sent, prach_ctrl = 0; - struct rte_eth_dev_tx_buffer *buffer; - - unsigned char * IQ_ptr; - - ul_packet_t p; - - buf = rte_pktmbuf_mtod(m, unsigned char *); - //DD Debug - //rte_memdump(stdout, "Rx Packet",buf ,34); - eth = rte_pktmbuf_mtod(m, struct ether_hdr *); - len = rte_pktmbuf_data_len(m); - - IQ_ptr = &buf[34]; - if (portid == 0) - { - //destination mac address (RRU MAC) - /*eth->d_addr.addr_bytes[0] = 0x02; - eth->d_addr.addr_bytes[1] = 0x00; - eth->d_addr.addr_bytes[2] = 0x5e; - eth->d_addr.addr_bytes[3] = 0x01; - eth->d_addr.addr_bytes[4] = 0x01; - eth->d_addr.addr_bytes[5] = 0x01;*/ - - eth->d_addr.addr_bytes[0] = buf[6]; - eth->d_addr.addr_bytes[1] = buf[7]; - eth->d_addr.addr_bytes[2] = buf[8]; - eth->d_addr.addr_bytes[3] = buf[9]; - eth->d_addr.addr_bytes[4] = buf[10]; - eth->d_addr.addr_bytes[5] = buf[11]; - - //source mac address (DU MAC) - eth->s_addr.addr_bytes[0] = 0xdd; - eth->s_addr.addr_bytes[1] = 0xdd; - eth->s_addr.addr_bytes[2] = 0xdd; - eth->s_addr.addr_bytes[3] = 0xdd; - eth->s_addr.addr_bytes[4] = 0xdd; - eth->s_addr.addr_bytes[5] = 0xdd; - } - else - { - //destination mac address (RRU MAC) - /*eth->d_addr.addr_bytes[0] = 0x02; - eth->d_addr.addr_bytes[1] = 0x00; - eth->d_addr.addr_bytes[2] = 0x5e; - eth->d_addr.addr_bytes[3] = 0x01; - eth->d_addr.addr_bytes[4] = 0x01; - eth->d_addr.addr_bytes[5] = 0x01;*/ - - eth->d_addr.addr_bytes[0] = buf[6]; - eth->d_addr.addr_bytes[1] = buf[7]; - eth->d_addr.addr_bytes[2] = buf[8]; - eth->d_addr.addr_bytes[3] = buf[9]; - eth->d_addr.addr_bytes[4] = buf[10]; - eth->d_addr.addr_bytes[5] = buf[11]; - //source mac address (DU MAC) - eth->s_addr.addr_bytes[0] = 0xdd; - eth->s_addr.addr_bytes[1] = 0xdd; - eth->s_addr.addr_bytes[2] = 0xdd; - eth->s_addr.addr_bytes[3] = 0xdd; - eth->s_addr.addr_bytes[4] = 0xdd; - eth->s_addr.addr_bytes[5] = 0xde; - - } - dst_port = l2fwd_dst_ports[portid]; - //DD Debug - //printf("dstPort =%d\n", dst_port); - // Handshake Condition - if (buf[18] == 0xAA ) // ORAN handshake msgs. - { - printf("\n Start UP Request Received\n"); - printf("\n====================================================\n"); - - buf[18] = 0xBB;//change to msg2 after receive msg1 - - printf(" Start Up Response Sent\n"); - printf("\n====================================================\n"); - - } - - // Trigger start send DL packets - if(PAYLOAD_1 == 0x13 && PAYLOAD_2 == 0xe4 && SYMBOL == 0x44 && ANT_NUM == 0x00 && SUBFRAME == 0x00 && dl_start == 0){ - - printf("\nU-Plane Started\n"); - printf("\n====================================================\n"); - - dl_start = 1; - count_symbol = 28; - } - - - if(PAYLOAD_1 == 0x13 && PAYLOAD_2 == 0xe4) { - int subframe = SUBFRAME >> 4; - int slot = ((SUBFRAME & 0x0f) << 2) | ((SYMBOL >> 6) & 0x03); - p.frame = FRAME; - p.slot = subframe * 2 + slot; - p.symbol = SYMBOL & 0x3f; - p.antenna = ANT_NUM; - memcpy(p.iq, IQ_ptr, 5088); - store_ul(bs, &p); - // if (p.symbol==0) printf("store ul f.sl.sy %d.%d.%d\n", p.frame, p.slot, p.symbol); - } - - // U-PLANE UL PROCESSING - if(PAYLOAD_1 == 0x13 && PAYLOAD_2 == 0xe4 && dl_start == 1) - { - int a = ANT_NUM & 0x01; - int frame = FRAME; - int subframe = SUBFRAME >> 4; - int slot = ((SUBFRAME & 0x0f) << 2) | ((SYMBOL >> 6) & 0x03); - int symbol = SYMBOL & 0x3f; - int oai_slot; - - int tx_frame = frame; - int tx_subframe = subframe; - int tx_slot = slot; - int tx_symbol = symbol + 10; - - if (tx_symbol > 13) { - tx_symbol -= 14; - tx_slot++; - if (tx_slot == 2) { - tx_slot = 0; - tx_subframe++; - if (tx_subframe == 10) { - tx_subframe = 0; - tx_frame = (tx_frame + 1) & 255; - } - } - } - - // Mask the symbol bits from UL packet received and apply the shift. - SYMBOL = ((SYMBOL & 0b00111111) + 10) % 14; - - ANT_NUM = a; - SUBFRAME = sf; - - if (a == 1) - slot_id_ctrl++; - - // Slot id control for DL - if (slot_id_ctrl > 13) { - SYMBOL = SYMBOL | 0b01000000; - - if (a == 1) { - if (slot_id_ctrl > 27) { - slot_id_ctrl = 0; - sf = sf + 0x10; - - if (sf > 0x90){ - sf = 0; - } - } - } - } - - /* send actual DL data (if available) */ - oai_slot = tx_subframe * 2 + tx_slot; - lock_dl_buffer(bs->buffers, oai_slot); - if (!(bs->buffers->dl_busy[a][oai_slot] & (1 << tx_symbol))) { - //printf("%s: warning, DL underflow (antenna %d sl.symbol %d.%d)\n", __FUNCTION__,a, oai_slot, tx_symbol); - memset(IQ_ptr, 0, 1272 * 4); - } else { - memcpy(IQ_ptr, bs->buffers->dl[a][oai_slot] + tx_symbol * 1272*4, - 1272*4); - } - //printf("DL buffer f sf slot symbol %d %d %d %d (sf %d)\n", tx_frame, tx_subframe, tx_slot, tx_symbol, (int)sf); - bs->buffers->dl_busy[a][oai_slot] &= ~(1 << tx_symbol); - unlock_dl_buffer(bs->buffers, oai_slot); - } - - // U-PLANE PRACH PROCESSING - else if(PAYLOAD_1 == 0x0d && PAYLOAD_2 == 0x28){ - if (ANT_NUM == 0) { - int subframe = SUBFRAME >> 4; - //int slot = ((SUBFRAME & 0x0f) << 2) | ((SYMBOL >> 6) & 0x03); - if (subframe==9) { - //printf("store prach f.sf.sl %d.%d.%d %d\n", FRAME, subframe, slot, subframe * 2 + slot); - store_prach(bs, FRAME, subframe * 2 + 1, IQ_ptr); - } - } - rte_pktmbuf_free(m); - return; - } - - // Send Packets - buffer = tx_buffer[dst_port]; - - sent = rte_eth_tx_buffer(dst_port, 0, buffer, m); - - if (sent) - port_statistics[dst_port].tx += sent; - -} -/* main processing loop */ -static void -l2fwd_main_loop(benetel_t *bs) -{ - struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; - struct rte_mbuf *m; - int sent; - unsigned lcore_id; - uint64_t prev_tsc, diff_tsc, cur_tsc, timer_tsc; - unsigned i, j, portid, nb_rx; - struct lcore_queue_conf *qconf; - const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S * - BURST_TX_DRAIN_US; - struct rte_eth_dev_tx_buffer *buffer; - prev_tsc = 0; - timer_tsc = 0; - lcore_id = rte_lcore_id(); - qconf = &lcore_queue_conf[lcore_id]; - if (qconf->n_rx_port == 0) { - RTE_LOG(INFO, L2FWD, "lcore %u has nothing to do\n", lcore_id); - return; - } - RTE_LOG(INFO, L2FWD, "entering main loop on lcore %u\n", lcore_id); - for (i = 0; i < qconf->n_rx_port; i++) { - portid = qconf->rx_port_list[i]; - RTE_LOG(INFO, L2FWD, " -- lcoreid=%u portid=%u\n", lcore_id, - portid); - } - while (!force_quit) { - cur_tsc = rte_rdtsc(); - /* - * TX burst queue drain - */ - diff_tsc = cur_tsc - prev_tsc; - if (unlikely(diff_tsc > drain_tsc)) { - for (i = 0; i < qconf->n_rx_port; i++) { - portid = l2fwd_dst_ports[qconf->rx_port_list[i]]; - buffer = tx_buffer[portid]; - sent = rte_eth_tx_buffer_flush(portid, 0, buffer); - if (sent) - port_statistics[portid].tx += sent; - } - /* if timer is enabled */ - if (timer_period > 0) { - /* advance the timer */ - timer_tsc += diff_tsc; - /* if timer has reached its timeout */ - if (unlikely(timer_tsc >= timer_period)) { - /* do this only on master core */ - if (lcore_id == rte_get_master_lcore()) { - print_stats(); - /* reset the timer */ - timer_tsc = 0; - } - } - } - prev_tsc = cur_tsc; - } - /* - * Read packet from RX queues - */ - for (i = 0; i < qconf->n_rx_port; i++) { - portid = qconf->rx_port_list[i]; - nb_rx = rte_eth_rx_burst(portid, 0, - pkts_burst, MAX_PKT_BURST); - port_statistics[portid].rx += nb_rx; - for (j = 0; j < nb_rx; j++) { - m = pkts_burst[j]; - rte_prefetch0(rte_pktmbuf_mtod(m, void *)); - l2fwd_simple_forward(m, portid, bs); - } - } - } -} -static int -l2fwd_launch_one_lcore(void *bs) -{ - l2fwd_main_loop(bs); - return 0; -} -/* display usage */ -static void -l2fwd_usage(const char *prgname) -{ - printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n" - " -p PORTMASK: hexadecimal bitmask of ports to configure\n" - " -q NQ: number of queue (=ports) per lcore (default is 1)\n" - " -T PERIOD: statistics will be refreshed each PERIOD seconds (0 to disable, 10 default, 86400 maximum)\n" - " --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n" - " When enabled:\n" - " - The source MAC address is replaced by the TX port MAC address\n" - " - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n", - prgname); -} -static int -l2fwd_parse_portmask(const char *portmask) -{ - char *end = NULL; - unsigned long pm; - /* parse hexadecimal string */ - pm = strtoul(portmask, &end, 16); - if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) - return -1; - if (pm == 0) - return -1; - return pm; -} -static unsigned int -l2fwd_parse_nqueue(const char *q_arg) -{ - char *end = NULL; - unsigned long n; - /* parse hexadecimal string */ - n = strtoul(q_arg, &end, 10); - if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) - return 0; - if (n == 0) - return 0; - if (n >= MAX_RX_QUEUE_PER_LCORE) - return 0; - return n; -} -static int -l2fwd_parse_timer_period(const char *q_arg) -{ - char *end = NULL; - int n; - /* parse number string */ - n = strtol(q_arg, &end, 10); - if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) - return -1; - if (n >= MAX_TIMER_PERIOD) - return -1; - return n; -} -static const char short_options[] = - "p:" /* portmask */ - "q:" /* number of queues */ - "T:" /* timer period */ - ; -#define CMD_LINE_OPT_MAC_UPDATING "mac-updating" -#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" -enum { - /* long options mapped to a short option */ - /* first long only option value must be >= 256, so that we won't - * conflict with short options */ - CMD_LINE_OPT_MIN_NUM = 256, -}; -static const struct option lgopts[] = { - { CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1}, - { CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0}, - {NULL, 0, 0, 0} -}; -/* Parse the argument given in the command line of the application */ -static int -l2fwd_parse_args(int argc, char **argv) -{ - int opt, ret, timer_secs; - char **argvopt; - int option_index; - char *prgname = argv[0]; - argvopt = argv; - while ((opt = getopt_long(argc, argvopt, short_options, - lgopts, &option_index)) != EOF) { - switch (opt) { - /* portmask */ - case 'p': - l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg); - if (l2fwd_enabled_port_mask == 0) { - printf("invalid portmask\n"); - l2fwd_usage(prgname); - return -1; - } - break; - /* nqueue */ - case 'q': - l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg); - if (l2fwd_rx_queue_per_lcore == 0) { - printf("invalid queue number\n"); - l2fwd_usage(prgname); - return -1; - } - break; - /* timer period */ - case 'T': - timer_secs = l2fwd_parse_timer_period(optarg); - if (timer_secs < 0) { - printf("invalid timer period\n"); - l2fwd_usage(prgname); - return -1; - } - timer_period = timer_secs; - break; - /* long options */ - case 0: - break; - case 'proc-type': - break; - case 'socket-mem': - break; - case 'file-prefix': - l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg); - if (l2fwd_rx_queue_per_lcore == 0) { - printf("invalid queue number\n"); - l2fwd_usage(prgname); - return -1; - } - break; - /* timer period */ - /* timer period */ - /* timer period */ - default: - l2fwd_usage(prgname); - return -1; - } - } - if (optind >= 0) - argv[optind-1] = prgname; - ret = optind-1; - optind = 1; /* reset getopt lib */ - return ret; -} -/* Check the link status of all ports in up to 9s, and print them finally */ -static void -check_all_ports_link_status(uint32_t port_mask) -{ -#define CHECK_INTERVAL 100 /* 100ms */ -#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ - uint16_t portid; - uint8_t count, all_ports_up, print_flag = 0; - struct rte_eth_link link; - printf("\nChecking link status"); - fflush(stdout); - for (count = 0; count <= MAX_CHECK_TIME; count++) { - if (force_quit) - return; - all_ports_up = 1; - RTE_ETH_FOREACH_DEV(portid) { - if (force_quit) - return; - if ((port_mask & (1 << portid)) == 0) - continue; - memset(&link, 0, sizeof(link)); - rte_eth_link_get_nowait(portid, &link); - /* print link status if flag set */ - if (print_flag == 1) { - if (link.link_status) - printf( - "Port%d Link Up. Speed %u Mbps - %s\n", - portid, link.link_speed, - (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? - ("full-duplex") : ("half-duplex\n")); - else - printf("Port %d Link Down\n", portid); - continue; - } - /* clear all_ports_up flag if any link down */ - if (link.link_status == ETH_LINK_DOWN) { - all_ports_up = 0; - break; - } - } - /* after finally printing all link status, get out */ - if (print_flag == 1) - break; - if (all_ports_up == 0) { - printf("."); - fflush(stdout); - rte_delay_ms(CHECK_INTERVAL); - } - /* set the print_flag if all ports up or timeout */ - if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) { - print_flag = 1; - printf("done\n"); - } - } -} -static void -signal_handler(int signum) -{ - if (signum == SIGINT || signum == SIGTERM) { - printf("\n\nSignal %d received, preparing to exit...\n", - signum); - force_quit = true; - } -} -int -dpdk_main(int argc, char **argv, benetel_t *bs) -{ - struct lcore_queue_conf *qconf; - int ret; - uint16_t nb_ports; - uint16_t nb_ports_available = 0; - uint16_t portid, last_port; - unsigned lcore_id, rx_lcore_id; - unsigned nb_ports_in_mask = 0; - unsigned int nb_lcores = 0; - unsigned int nb_mbufs; - /* init EAL */ - ret = rte_eal_init(argc, argv); - if (ret < 0) - rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); - argc -= ret; - argv += ret; - force_quit = false; - signal(SIGINT, signal_handler); - signal(SIGTERM, signal_handler); - /* parse application arguments (after the EAL ones) */ - ret = l2fwd_parse_args(argc, argv); - if (ret < 0) - rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n"); - printf("MAC updating %s\n", mac_updating ? "enabled" : "disabled"); - /* convert to number of cycles */ - timer_period *= rte_get_timer_hz(); - nb_ports = rte_eth_dev_count_avail(); - if (nb_ports == 0) - rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); - /* check port mask to possible port mask */ - if (l2fwd_enabled_port_mask & ~((1 << nb_ports) - 1)) - rte_exit(EXIT_FAILURE, "Invalid portmask; possible (0x%x)\n", - (1 << nb_ports) - 1); - /* reset l2fwd_dst_ports */ - for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) - l2fwd_dst_ports[portid] = 0; - last_port = 0; - /* - * Each logical core is assigned a dedicated TX queue on each port. - */ - RTE_ETH_FOREACH_DEV(portid) { - /* skip ports that are not enabled */ - if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) - continue; - if (nb_ports_in_mask % 2) { - l2fwd_dst_ports[portid] = last_port; - l2fwd_dst_ports[last_port] = portid; - } - else - last_port = portid; - nb_ports_in_mask++; - } - if (nb_ports_in_mask % 2) { - printf("Notice: odd number of ports in portmask.\n"); - l2fwd_dst_ports[last_port] = last_port; - } - rx_lcore_id = 0; - qconf = NULL; - /* Initialize the port/queue configuration of each logical core */ - RTE_ETH_FOREACH_DEV(portid) { - /* skip ports that are not enabled */ - if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) - continue; - /* get the lcore_id for this port */ - while (rte_lcore_is_enabled(rx_lcore_id) == 0 || - lcore_queue_conf[rx_lcore_id].n_rx_port == - l2fwd_rx_queue_per_lcore) { - rx_lcore_id++; - if (rx_lcore_id >= RTE_MAX_LCORE) - rte_exit(EXIT_FAILURE, "Not enough cores\n"); - } - if (qconf != &lcore_queue_conf[rx_lcore_id]) { - /* Assigned a new logical core in the loop above. */ - qconf = &lcore_queue_conf[rx_lcore_id]; - nb_lcores++; - } - qconf->rx_port_list[qconf->n_rx_port] = portid; - qconf->n_rx_port++; - printf("Lcore %u: RX port %u\n", rx_lcore_id, portid); - } - nb_mbufs = RTE_MAX(nb_ports * (nb_rxd + nb_txd + MAX_PKT_BURST + - nb_lcores * MEMPOOL_CACHE_SIZE), 8192U); - /* create the mbuf pool */ - //DD Debug - //printf ("Proc Type %d \n", rte_eal_process_type()); - l2fwd_pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", nb_mbufs, - MEMPOOL_CACHE_SIZE, 0, 9680, - rte_socket_id()); - if (l2fwd_pktmbuf_pool == NULL) - rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n"); - /* Initialise each port */ - RTE_ETH_FOREACH_DEV(portid) { - struct rte_eth_rxconf rxq_conf; - struct rte_eth_txconf txq_conf; - struct rte_eth_conf local_port_conf = port_conf; - struct rte_eth_dev_info dev_info; - /* skip ports that are not enabled */ - if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) { - printf("Skipping disabled port %u\n", portid); - continue; - } - nb_ports_available++; - /* init port */ - printf("Initializing port %u... ", portid); - fflush(stdout); - rte_eth_dev_info_get(portid, &dev_info); - if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) - local_port_conf.txmode.offloads |= - DEV_TX_OFFLOAD_MBUF_FAST_FREE; - ret = rte_eth_dev_configure(portid, 1, 1, &local_port_conf); - if (ret < 0) - rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n", - ret, portid); - ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd, - &nb_txd); - if (ret < 0) - rte_exit(EXIT_FAILURE, - "Cannot adjust number of descriptors: err=%d, port=%u\n", - ret, portid); - rte_eth_macaddr_get(portid,&l2fwd_ports_eth_addr[portid]); - /* init one RX queue */ - fflush(stdout); - rxq_conf = dev_info.default_rxconf; - rxq_conf.offloads = local_port_conf.rxmode.offloads; - ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd, - rte_eth_dev_socket_id(portid), - &rxq_conf, - l2fwd_pktmbuf_pool); - if (ret < 0) - rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup:err=%d, port=%u\n", - ret, portid); - /* init one TX queue on each port */ - fflush(stdout); - txq_conf = dev_info.default_txconf; - txq_conf.offloads = local_port_conf.txmode.offloads; - ret = rte_eth_tx_queue_setup(portid, 0, nb_txd, - rte_eth_dev_socket_id(portid), - &txq_conf); - if (ret < 0) - rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup:err=%d, port=%u\n", - ret, portid); - /* Initialize TX buffers */ - tx_buffer[portid] = rte_zmalloc_socket("tx_buffer", - RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0, - rte_eth_dev_socket_id(portid)); - if (tx_buffer[portid] == NULL) - rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx on port %u\n", - portid); - rte_eth_tx_buffer_init(tx_buffer[portid], MAX_PKT_BURST); - ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[portid], - rte_eth_tx_buffer_count_callback, - &port_statistics[portid].dropped); - if (ret < 0) - rte_exit(EXIT_FAILURE, - "Cannot set error callback for tx buffer on port %u\n", - portid); - /* Start device */ - ret = rte_eth_dev_start(portid); - if (ret < 0) - rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err=%d, port=%u\n", - ret, portid); - printf("done: \n"); - rte_eth_promiscuous_enable(portid); - printf("Port %u, MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n\n", - portid, - l2fwd_ports_eth_addr[portid].addr_bytes[0], - l2fwd_ports_eth_addr[portid].addr_bytes[1], - l2fwd_ports_eth_addr[portid].addr_bytes[2], - l2fwd_ports_eth_addr[portid].addr_bytes[3], - l2fwd_ports_eth_addr[portid].addr_bytes[4], - l2fwd_ports_eth_addr[portid].addr_bytes[5]); - /* initialize port stats */ - memset(&port_statistics, 0, sizeof(port_statistics)); - } - if (!nb_ports_available) { - rte_exit(EXIT_FAILURE, - "All available ports are disabled. Please set portmask.\n"); - } - check_all_ports_link_status(l2fwd_enabled_port_mask); - ret = 0; - /* launch per-lcore init on every lcore */ - rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, bs, CALL_MASTER); - RTE_LCORE_FOREACH_SLAVE(lcore_id) { - if (rte_eal_wait_lcore(lcore_id) < 0) { - ret = -1; - break; - } - } - RTE_ETH_FOREACH_DEV(portid) { - if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) - continue; - printf("Closing port %d...", portid); - rte_eth_dev_stop(portid); - rte_eth_dev_close(portid); - printf(" Done\n"); - } - printf("Bye...\n"); - return ret; -} diff --git a/radio/ETHERNET/benetel/5g/low.c b/radio/ETHERNET/benetel/5g/low.c deleted file mode 100644 index b34d69784f..0000000000 --- a/radio/ETHERNET/benetel/5g/low.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#include "low.h" - -#include -#include -#include - -void store_ul(benetel_t *bs, ul_packet_t *ul) -{ -#if 0 -struct timespec t; -static struct timespec old; -clock_gettime(CLOCK_REALTIME, &t); -printf("store_ul %d.%ld (%ld)\n", (int)(t.tv_sec % 60), t.tv_nsec, t.tv_nsec - old.tv_nsec); -old = t; -#endif - - int a = ul->antenna; - - if (ul->slot != bs->next_slot[a] || - ul->symbol != bs->next_symbol[a]) { - printf("%s: error, antenna %d expected frame.sl.symbol %d.%d.%d, got %d.%d.%d\n", - __FUNCTION__, - a, bs->expected_benetel_frame[a], bs->next_slot[a], bs->next_symbol[a], - ul->frame, ul->slot, ul->symbol); - } - - /* fill missing data with 0s */ - while (ul->slot != bs->next_slot[a] || - ul->symbol != bs->next_symbol[a]) { - lock_ul_buffer(bs->buffers, bs->next_slot[a]); - if (bs->buffers->ul_busy[a][bs->next_slot[a]] & (1 << bs->next_symbol[a])) { - printf("%s: warning, antenna %d UL overflow (sl.symbol %d.%d)\n", __FUNCTION__, - a, bs->next_slot[a], bs->next_symbol[a]); - } - - memset(bs->buffers->ul[a][bs->next_slot[a]] + bs->next_symbol[a] * 1272*4, - 0, 1272*4); - bs->buffers->ul_busy[a][bs->next_slot[a]] |= (1 << bs->next_symbol[a]); - signal_ul_buffer(bs->buffers, bs->next_slot[a]); - unlock_ul_buffer(bs->buffers, bs->next_slot[a]); - - bs->next_symbol[a]++; - if (bs->next_symbol[a] == 14) { - bs->next_symbol[a] = 0; - bs->next_slot[a] = (bs->next_slot[a] + 1) % 20; - if (bs->next_slot[a] == 0) { - bs->expected_benetel_frame[a]++; - bs->expected_benetel_frame[a] &= 255; - } - } - } - - lock_ul_buffer(bs->buffers, bs->next_slot[a]); - if (bs->buffers->ul_busy[a][bs->next_slot[a]] & (1 << bs->next_symbol[a])) { - printf("%s: warning, antenna %d UL overflow (sl.symbol %d.%d)\n", __FUNCTION__, - a, bs->next_slot[a], bs->next_symbol[a]); - } - - memcpy(bs->buffers->ul[a][bs->next_slot[a]] + bs->next_symbol[a] * 1272*4, - ul->iq, 1272*4); - bs->buffers->ul_busy[a][bs->next_slot[a]] |= (1 << bs->next_symbol[a]); - signal_ul_buffer(bs->buffers, bs->next_slot[a]); - unlock_ul_buffer(bs->buffers, bs->next_slot[a]); - - bs->next_symbol[a]++; - if (bs->next_symbol[a] == 14) { - bs->next_symbol[a] = 0; - bs->next_slot[a] = (bs->next_slot[a] + 1) % 20; - if (bs->next_slot[a] == 0) { - bs->expected_benetel_frame[a]++; - bs->expected_benetel_frame[a] &= 255; - } - } -} - -void store_prach(benetel_t *bs, int frame, int slot, void *data) -{ - static int last_frame = -1; - static int last_slot = -1; - /* hack: antenna number is always 0, discard second packet with same f/sf */ - //if (frame == last_frame && slot == last_slot) return; - if (frame == last_frame && slot == last_slot) { printf("got f.sl %d.%d twice\n", frame, slot); return; } - last_frame = frame; - last_slot = slot; - - lock_ul_buffer(bs->buffers, slot); - if (bs->buffers->prach_busy[slot]) { - printf("store_prach: fatal: previous prach buffer not processed\n"); - unlock_ul_buffer(bs->buffers, slot); - return; - } - bs->buffers->prach_busy[slot] = 1; - memcpy(bs->buffers->prach[slot], data, 839*4); - signal_ul_buffer(bs->buffers, slot); - unlock_ul_buffer(bs->buffers, slot); - -} - -void *benetel_start_dpdk(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line); - -void *benetel_start(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line) -{ - if (!strcmp(ifname, "dpdk")) - return benetel_start_dpdk(ifname, buffers, dpdk_main_command_line); - printf("benetel: fatal: interface %s not supported, only dpdpk is supported\n", ifname); - exit(1); -} diff --git a/radio/ETHERNET/benetel/5g/low.h b/radio/ETHERNET/benetel/5g/low.h deleted file mode 100644 index 659819296a..0000000000 --- a/radio/ETHERNET/benetel/5g/low.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#ifndef _BENETEL_5G_LOW_H_ -#define _BENETEL_5G_LOW_H_ - -#include "shared_buffers.h" - -typedef struct { - shared_buffers *buffers; - /* [2] is for two antennas */ - int next_slot[2]; - int next_symbol[2]; - int expected_benetel_frame[2]; - char *dpdk_main_command_line; -} benetel_t; - -typedef struct { - int frame; - int slot; - int symbol; - int antenna; - unsigned char iq[5088]; -} ul_packet_t; - -void *benetel_start(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line); - -void store_ul(benetel_t *bs, ul_packet_t *ul); -void store_prach(benetel_t *bs, int frame, int slot, void *data); - -#endif /* _BENETEL_5G_LOW_H_ */ diff --git a/radio/ETHERNET/benetel/5g/low_dpdk.c b/radio/ETHERNET/benetel/5g/low_dpdk.c deleted file mode 100644 index b26df07723..0000000000 --- a/radio/ETHERNET/benetel/5g/low_dpdk.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -/* those 2 lines for CPU_SET */ -#define _GNU_SOURCE -#include - -#include -#include -#include -#include - -#include "low.h" - -int dpdk_main(int argc, char **argv, benetel_t *); - -void *dpdk_thread(void *_bs) -{ - benetel_t *bs = _bs; - int n = 0; - char **v = NULL; // = { "softmodem", "-n", "2", "--file-prefix", "pg2", "-l", "6", "--", "-p", "0x1" }; - char *s = bs->dpdk_main_command_line; - char *tok; - - while ((tok = strtok(s, " ")) != NULL) { - n++; - v = realloc(v, n * sizeof(char *)); - if (v == NULL) { - printf("%s: out of memory\n", __FUNCTION__); - exit(1); - } - v[n-1] = tok; - s = NULL; - } - - dpdk_main(n, v, bs); - exit(1); - return 0; -} - -void *benetel_start_dpdk(char *ifname, shared_buffers *buffers, char *dpdk_main_command_line) -{ - benetel_t *bs; - - bs = calloc(1, sizeof(benetel_t)); - if (bs == NULL) { - printf("%s: out of memory\n", __FUNCTION__); - exit(1); - } - - bs->buffers = buffers; - - bs->expected_benetel_frame[0] = 255; - bs->expected_benetel_frame[1] = 255; - - bs->dpdk_main_command_line = dpdk_main_command_line; - - pthread_attr_t attr; - - if (pthread_attr_init(&attr) != 0) { - printf("pthread_attr_init failed\n"); - exit(1); - } - - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(10,&cpuset); - if (pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset) != 0) { - printf("pthread_attr_setaffinity_np failed\n"); - exit(1); - } - - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) != 0) { - printf("pthread_attr_setschedpolicy failed\n"); - exit(1); - } - - struct sched_param params; - params.sched_priority = sched_get_priority_max(SCHED_FIFO); - if (sched_get_priority_max(SCHED_FIFO) == -1) { - printf("sched_get_priority_max failed\n"); - exit(1); - } - if (pthread_attr_setschedparam(&attr, ¶ms) != 0) { - printf("pthread_setschedparam failed\n"); - exit(1); - } - - pthread_t t; - if (pthread_create(&t, &attr, dpdk_thread, bs) != 0) { - printf("%s: thread creation failed\n", __FUNCTION__); - exit(1); - } - - if (pthread_attr_destroy(&attr) != 0) { - printf("pthread_attr_init failed\n"); - exit(1); - } - - return bs; -} diff --git a/radio/ETHERNET/benetel/5g/shared_buffers.c b/radio/ETHERNET/benetel/5g/shared_buffers.c deleted file mode 100644 index a302400eb4..0000000000 --- a/radio/ETHERNET/benetel/5g/shared_buffers.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#include "shared_buffers.h" - -#include -#include -#include - -void init_buffers(shared_buffers *s) -{ - int slot; - - memset(s, 0, sizeof(*s)); - - for (slot = 0; slot < 20; slot++) { - if (pthread_mutex_init(&s->m_dl[slot], NULL) != 0 || - pthread_cond_init(&s->c_dl[slot], NULL) != 0 || - pthread_mutex_init(&s->m_ul[slot], NULL) != 0 || - pthread_cond_init(&s->c_ul[slot], NULL) != 0) { - printf("%s: error initializing mutex/cond\n", __FUNCTION__); - exit(1); - } - } - - /* the gNB's first transmitted DL slot is 6 but the device - * needs to have slots 1, 2 and 3, 4 and 5 ready. Let's pretend - * they are ready. - */ - s->dl_busy[0][1] = 0x3fff; - s->dl_busy[0][2] = 0x3fff; - s->dl_busy[0][3] = 0x3fff; - s->dl_busy[0][4] = 0x3fff; - s->dl_busy[0][5] = 0x3fff; - s->dl_busy[1][1] = 0x3fff; - s->dl_busy[1][2] = 0x3fff; - s->dl_busy[1][3] = 0x3fff; - s->dl_busy[1][4] = 0x3fff; - s->dl_busy[1][5] = 0x3fff; -} - -void lock_dl_buffer(shared_buffers *s, int slot) -{ - if (pthread_mutex_lock(&s->m_dl[slot]) != 0) { - printf("%s: fatal: lock fails\n", __FUNCTION__); - exit(1); - } -} - -void unlock_dl_buffer(shared_buffers *s, int slot) -{ - if (pthread_mutex_unlock(&s->m_dl[slot]) != 0) { - printf("%s: fatal: unlock fails\n", __FUNCTION__); - exit(1); - } -} - -void wait_dl_buffer(shared_buffers *s, int slot) -{ - if (pthread_cond_wait(&s->c_dl[slot], &s->m_dl[slot]) != 0) { - printf("%s: fatal: cond_wait fails\n", __FUNCTION__); - exit(1); - } -} - -void signal_dl_buffer(shared_buffers *s, int slot) -{ - if (pthread_cond_broadcast(&s->c_dl[slot]) != 0) { - printf("%s: fatal: cond_broadcast fails\n", __FUNCTION__); - exit(1); - } -} - -void lock_ul_buffer(shared_buffers *s, int slot) -{ - if (pthread_mutex_lock(&s->m_ul[slot]) != 0) { - printf("%s: fatal: lock fails\n", __FUNCTION__); - printf("%s: fatal: lock fails slot %d\n", __FUNCTION__, slot); - exit(1); - } -} - -void unlock_ul_buffer(shared_buffers *s, int slot) -{ - if (pthread_mutex_unlock(&s->m_ul[slot]) != 0) { - printf("%s: fatal: unlock fails\n", __FUNCTION__); - exit(1); - } -} - -void wait_ul_buffer(shared_buffers *s, int slot) -{ - if (pthread_cond_wait(&s->c_ul[slot], &s->m_ul[slot]) != 0) { - printf("%s: fatal: cond_wait fails\n", __FUNCTION__); - exit(1); - } -} - -void signal_ul_buffer(shared_buffers *s, int slot) -{ - if (pthread_cond_broadcast(&s->c_ul[slot]) != 0) { - printf("%s: fatal: cond_broadcast fails\n", __FUNCTION__); - exit(1); - } -} diff --git a/radio/ETHERNET/benetel/5g/shared_buffers.h b/radio/ETHERNET/benetel/5g/shared_buffers.h deleted file mode 100644 index 9d51b8138d..0000000000 --- a/radio/ETHERNET/benetel/5g/shared_buffers.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#ifndef _BENETEL_5G_SHARED_BUFFERS_H_ -#define _BENETEL_5G_SHARED_BUFFERS_H_ - -#include -#include - -typedef struct { - /* [2] is for two antennas */ - unsigned char dl[2][20][14*1272*4]; - unsigned char ul[2][20][14*1272*4]; - uint16_t dl_busy[2][20]; - uint16_t ul_busy[2][20]; - - pthread_mutex_t m_ul[20]; - pthread_cond_t c_ul[20]; - - pthread_mutex_t m_dl[20]; - pthread_cond_t c_dl[20]; - - unsigned char prach[20][849*4]; - unsigned char prach_busy[20]; - - /* statistics/error counting */ - int ul_overflow; - int dl_underflow; -} shared_buffers; - -void init_buffers(shared_buffers *s); - -void lock_dl_buffer(shared_buffers *s, int slot); -void unlock_dl_buffer(shared_buffers *s, int slot); -void wait_dl_buffer(shared_buffers *s, int slot); -void signal_dl_buffer(shared_buffers *s, int slot); - -void lock_ul_buffer(shared_buffers *s, int slot); -void unlock_ul_buffer(shared_buffers *s, int slot); -void wait_ul_buffer(shared_buffers *s, int slot); -void signal_ul_buffer(shared_buffers *s, int slot); - -#endif /* _BENETEL_5G_SHARED_BUFFERS_H_ */ diff --git a/radio/ETHERNET/benetel/CMakeLists.txt b/radio/ETHERNET/benetel/CMakeLists.txt deleted file mode 100644 index 1a95d1ec2c..0000000000 --- a/radio/ETHERNET/benetel/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -add_boolean_option(OAI_BENETEL4G OFF "Activate OAI's Benetel 4G radio driver" OFF) -if(OAI_BENETEL4G) - add_subdirectory(4g) -endif() - -add_boolean_option(OAI_BENETEL5G OFF "Activate OAI's Benetel 5G radio driver" OFF) -if(OAI_BENETEL5G) - add_subdirectory(5g) -endif() From ece138134bdf04b5d899609f2364a541687f452b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 23 Oct 2025 10:03:28 +0200 Subject: [PATCH 038/275] build_oai: remove non-existing oran_fhlib_4g option --- cmake_targets/build_oai | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index 56c5e9b66c..6f61196e7e 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -278,10 +278,6 @@ function main() { TARGET_LIST="$TARGET_LIST oai_eth_transpro" CMAKE_CMD="$CMAKE_CMD -DOAI_${2^^}=ON" # ^^ makes uppercase ;; - "oran_fhlib_4g") - TARGET_LIST="$TARGET_LIST $2" - CMAKE_CMD="$CMAKE_CMD -DOAI_${2^^}=ON" # ^^ makes uppercase - ;; "oran_fhlib_5g") TARGET_LIST="$TARGET_LIST $2" CMAKE_CMD="$CMAKE_CMD -DOAI_FHI72=ON" From e06d0c71990ddddb5e7e0c801f3dc2c7a8cf2533 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 21 Oct 2025 14:48:51 +0200 Subject: [PATCH 039/275] improve handling of selection of RSRP/SINR report via configuration file --- .../gnb.sa.band254.u0.25prb.rfsim.ntn.conf | 2 +- doc/MAC/mac-usage.md | 19 +++++++++++-------- openair2/GNB_APP/gnb_config.c | 15 +++++++++++---- openair2/GNB_APP/gnb_paramdef.h | 8 ++++---- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 8 +++++++- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c | 19 +++++++++---------- 6 files changed, 43 insertions(+), 28 deletions(-) diff --git a/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf b/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf index a7663ae811..b3884a3d31 100644 --- a/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf +++ b/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf @@ -16,7 +16,7 @@ gNBs = ////////// Physical parameters: - do_SINR = 1; + CSI_report_type = "ssb_sinr"; min_rxtxtime = 6; disable_harq = 1; cu_sibs = [2]; diff --git a/doc/MAC/mac-usage.md b/doc/MAC/mac-usage.md index f4583b7a23..54a30cdd5f 100644 --- a/doc/MAC/mac-usage.md +++ b/doc/MAC/mac-usage.md @@ -257,7 +257,7 @@ configuration](../RRC/rrc-usage.md) as well for SIB configuration. * `do_CSIRS` (default 0): flag whether to use channel-state information reference signal (CSI-RS) * `do_SRS` (default 0): flag whether to use sounding reference signal (SRS) -* `do_SINR` (default 0): flag whether to enable CSI reporting of SSB-SINR (introduced in rel16) +* `CSI_report_type` (default `ssb_rsrp`): parameter to enable different CSI reporting (options: `ssb_rsrp`, `ssb_sinr` and `cri_rsrp`) Default setting of CSI reporting quantity is SSB-RSRP. * `min_rxtxtime` (default 2): minimum feedback time for UE to respond to transmissions (k1 and k2 in 3GPP spec) @@ -281,14 +281,17 @@ configuration](../RRC/rrc-usage.md) as well for SIB configuration. - `du_sibs` (default `[]`): list of SIBs to transmit in the cell. Currently, SIB19 (for NTN) is supported. -| DL MIMO |`do_CSIRS`|`do_SINR`| CSI report Quantity | -| ---------------------------- | -------- | ------- | --------------------------------------------------| -| OFF (pdsch_AntennaPorts = 1) | 0 | 0 | SSB-RSRP | -| OFF (pdsch_AntennaPorts = 1) | 0 | 1 | SSB-SINR | -| OFF (pdsch_AntennaPorts = 1) | 1 | 0 | CSI-Reference signal RSRP | -| OFF (pdsch_AntennaPorts = 1) | 1 | 1 | CSI-Reference signal SINR (not supported yet) | -| ON (pdsch_AntennaPorts > 1) | 1 | 0 | cri-RI-PMI-CQI | +| DL MIMO |`do_CSIRS`|`CSI_report_type`| CSI report Quantity | +| ------------------------------ | -------- | --------------- | --------------------------------------------------| +| any | any | `ssb_rsrp` | SSB-RSRP | +| any | 0 | `cri_rsrp` | SSB-RSRP (no CSI-RS configured) | +| any | 1 | `cri_rsrp` | CRI-RSRP | +| any | any | `ssb_sinr` | SSB-SINR | +| ON (`pdsch_AntennaPorts` > 1) | 1 | any | cri-RI-PMI-CQI | + +Note that activating `cri-RI-PMI-CQI` will result in that report to be produced +in addition to either `SSB-SINR`, `SSB-RSRP` or `CRI-RSRP`. DL-MIMO is configured using following parameters: `pdsch_AntennaPorts_XP` , `pdsch_AntennaPorts_N1` , `pdsch_AntennaPorts_N2`, `maxMIMO_layers` (see also [`RUNMODEM.md`](../RUNMODEM.md)) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 45532572cd..075bbd53fe 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -707,7 +707,7 @@ void RCconfig_verify(configmodule_interface_t *cfg, ngran_node_t node_type) verify_gnb_param_notset(gnbp, GNB_DISABLE_HARQ_IDX, GNB_CONFIG_STRING_DISABLE_HARQ); verify_gnb_param_notset(gnbp, GNB_NUM_DL_HARQ_IDX, GNB_CONFIG_STRING_NUM_DL_HARQPROCESSES); verify_gnb_param_notset(gnbp, GNB_NUM_UL_HARQ_IDX, GNB_CONFIG_STRING_NUM_UL_HARQPROCESSES); - verify_gnb_param_notset(gnbp, GNB_DO_SINR_IDX, GNB_CONFIG_STRING_DOSINR); + verify_gnb_param_notset(gnbp, GNB_CONFIG_REP_IDX, GNB_CONFIG_STRING_CONFIG_REP); // check for some general sections verify_section_notset(cfg, NULL, CONFIG_STRING_L1_LIST); @@ -1432,7 +1432,14 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) config.do_CSIRS = *GNBParamList.paramarray[0][GNB_DO_CSIRS_IDX].iptr; config.do_SRS = *GNBParamList.paramarray[0][GNB_DO_SRS_IDX].iptr; config.max_num_rsrp = *GNBParamList.paramarray[0][GNB_LIMIT_RSRP_REPORT_IDX].iptr; - config.do_SINR = *GNBParamList.paramarray[0][GNB_DO_SINR_IDX].iptr; + if (strcmp(*(GNBParamList.paramarray[0][GNB_CONFIG_REP_IDX].strptr), "ssb_rsrp") == 0) + config.report_type = SSB_RSRP; + else if (strcmp(*(GNBParamList.paramarray[0][GNB_CONFIG_REP_IDX].strptr), "ssb_sinr") == 0) + config.report_type = SSB_SINR; + else if (strcmp(*(GNBParamList.paramarray[0][GNB_CONFIG_REP_IDX].strptr), "cri_rsrp") == 0) + config.report_type = CRI_RSRP; + else + AssertFatal(false, "Invalid report type\n"); config.force_256qam_off = *GNBParamList.paramarray[0][GNB_FORCE256QAMOFF_IDX].iptr; config.force_UL256qam_off = *GNBParamList.paramarray[0][GNB_FORCEUL256QAMOFF_IDX].iptr; config.use_deltaMCS = *GNBParamList.paramarray[0][GNB_USE_DELTA_MCS_IDX].iptr != 0; @@ -1443,10 +1450,10 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) if (config.disable_harq) LOG_W(GNB_APP, "\"disable_harq\" is a REL17 feature and is incompatible with REL15 and REL16 UEs!\n"); LOG_I(GNB_APP, - "CSI-RS %d, SRS %d, SINR:%d, 256 QAM %s, delta_MCS %s, maxMIMO_Layers %d, HARQ feedback %s, num DLHARQ:%d, num ULHARQ:%d\n", + "CSI-RS %d, SRS %d, reprt type:%d, 256 QAM %s, delta_MCS %s, maxMIMO_Layers %d, HARQ feedback %s, num DLHARQ:%d, num ULHARQ:%d\n", config.do_CSIRS, config.do_SRS, - config.do_SINR, + config.report_type, config.force_256qam_off ? "force off" : "may be on", config.use_deltaMCS ? "on" : "off", config.maxMIMO_layers, diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index e4e4bc3d2a..8efc4f999f 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -137,7 +137,7 @@ typedef enum { #define GNB_CONFIG_STRING_UESS_AGG_LEVEL_LIST "uess_agg_levels" #define GNB_CONFIG_STRING_CU_SIB_LIST "cu_sibs" #define GNB_CONFIG_STRING_DU_SIB_LIST "du_sibs" -#define GNB_CONFIG_STRING_DOSINR "do_SINR" +#define GNB_CONFIG_STRING_CONFIG_REP "CSI_report_type" #define GNB_CONFIG_STRING_1ST_ACTIVE_BWP "first_active_bwp" #define GNB_CONFIG_STRING_LIMIT_RSRP_REPORT "max_num_RSRP_reported" @@ -153,7 +153,7 @@ typedef enum { #define GNB_CONFIG_HLP_UESS_AGG_LEVEL_LIST "List of aggregation levels with number of candidates per level. Element 0 - aggregation level 1" #define GNB_CONFIG_HLP_CU_SIBS "List of CU generated SIBs to be transmitted" #define GNB_CONFIG_HLP_DU_SIBS "List of DU generated SIBs to be transmitted" -#define GNB_CONFIG_HLP_DOSINR "Enable CSI feedback using SINR measurements on SSB" +#define GNB_CONFIG_HLP_CONFIG_REP "Define quantity for CSI report (options: ssb_rsrp, ssb_sinr and cri_rsrp)" /*-----------------------------------------------------------------------------------------------------------------------------------------*/ @@ -201,7 +201,7 @@ typedef enum { GNB_CONFIG_HLP_UESS_AGG_LEVEL_LIST, 0, .iptr=NULL, .defintarrayval=NULL, TYPE_INTARRAY, 0}, \ {GNB_CONFIG_STRING_CU_SIB_LIST, GNB_CONFIG_HLP_CU_SIBS, 0, .iptr=NULL, .defintarrayval=0, TYPE_INTARRAY, 0}, \ {GNB_CONFIG_STRING_DU_SIB_LIST, GNB_CONFIG_HLP_DU_SIBS, 0, .iptr=NULL, .defintarrayval=0, TYPE_INTARRAY, 0}, \ -{GNB_CONFIG_STRING_DOSINR, GNB_CONFIG_HLP_DOSINR, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \ +{GNB_CONFIG_STRING_CONFIG_REP, GNB_CONFIG_HLP_CONFIG_REP, 0, .strptr=NULL, .defstrval="ssb_rsrp", TYPE_STRING, 0}, \ {GNB_CONFIG_STRING_1ST_ACTIVE_BWP, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \ {GNB_CONFIG_STRING_LIMIT_RSRP_REPORT, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \ } @@ -246,7 +246,7 @@ typedef enum { #define GNB_UESS_AGG_LEVEL_LIST_IDX 35 #define GNB_CU_SIBS_IDX 36 #define GNB_DU_SIBS_IDX 37 -#define GNB_DO_SINR_IDX 38 +#define GNB_CONFIG_REP_IDX 38 #define GNB_1ST_ACTIVE_BWP_IDX 39 #define GNB_LIMIT_RSRP_REPORT_IDX 40 diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 026d8d563e..dfad999e76 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -179,6 +179,12 @@ typedef struct { int location_and_bw; } nr_bwp_config_t; +typedef enum { + SSB_RSRP, + CRI_RSRP, + SSB_SINR, +} nr_config_report_type_t; + typedef struct nr_mac_config_t { int sib1_tda; nr_pdsch_AntennaPorts_t pdsch_AntennaPorts; @@ -207,7 +213,7 @@ typedef struct nr_mac_config_t { int num_agg_level_candidates[NUM_PDCCH_AGG_LEVELS]; nr_redcap_config_t *redcap; nr_ptrs_config_t *ptrs; - bool do_SINR; + nr_config_report_type_t report_type; } nr_mac_config_t; typedef struct NR_preamble_ue { diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index a2b7c26678..02026d34d1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -2144,7 +2144,7 @@ static void config_rsrp_meas_report(NR_CSI_MeasConfig_t *csi_MeasConfig, for (int csi_list = 0; csi_list < csi_MeasConfig->csi_ResourceConfigToAddModList->list.count; csi_list++) { NR_CSI_ResourceConfig_t *csires = csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_list]; if (csires->csi_RS_ResourceSetList.present == NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_nzp_CSI_RS_SSB) { - if (configuration->do_CSIRS && num_antenna_ports < 4) { + if (configuration->report_type == CRI_RSRP && configuration->do_CSIRS && num_antenna_ports < 4) { if (csires->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList) resource_id = csires->csi_ResourceConfigId; } else { @@ -2166,20 +2166,19 @@ static void config_rsrp_meas_report(NR_CSI_MeasConfig_t *csi_MeasConfig, csirep->reportConfigType.choice.periodic = calloc(1, sizeof(*csirep->reportConfigType.choice.periodic)); set_csi_meas_periodicity(servingcellconfigcommon, csirep, uid, curr_bwp, true); asn1cSeqAdd(&csirep->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list, pucchcsires); - if (configuration->do_CSIRS && num_antenna_ports < 4) { + if (configuration->report_type == SSB_SINR) { + csirep->reportQuantity.present = NR_CSI_ReportConfig__reportQuantity_PR_none; + csirep->reportQuantity.choice.none = (NULL_t)0; + csirep->ext2 = calloc(1, sizeof(*csirep->ext2)); + csirep->ext2->reportQuantity_r16 = calloc(1, sizeof(*csirep->ext2->reportQuantity_r16)); + csirep->ext2->reportQuantity_r16->present = NR_CSI_ReportConfig__ext2__reportQuantity_r16_PR_ssb_Index_SINR_r16; + csirep->ext2->reportQuantity_r16->choice.ssb_Index_SINR_r16 = (NULL_t)0; + } else if (configuration->report_type == CRI_RSRP && configuration->do_CSIRS && num_antenna_ports < 4) { csirep->reportQuantity.present = NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP; csirep->reportQuantity.choice.cri_RSRP = (NULL_t)0; } else { csirep->reportQuantity.present = NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP; csirep->reportQuantity.choice.ssb_Index_RSRP = (NULL_t)0; - if (configuration->do_SINR) { - csirep->reportQuantity.present = NR_CSI_ReportConfig__reportQuantity_PR_none; - csirep->reportQuantity.choice.none = (NULL_t)0; - csirep->ext2 = calloc(1, sizeof(*csirep->ext2)); - csirep->ext2->reportQuantity_r16 = calloc(1, sizeof(*csirep->ext2->reportQuantity_r16)); - csirep->ext2->reportQuantity_r16->present = NR_CSI_ReportConfig__ext2__reportQuantity_r16_PR_ssb_Index_SINR_r16; - csirep->ext2->reportQuantity_r16->choice.ssb_Index_SINR_r16 = (NULL_t)0; - } } csirep->groupBasedBeamReporting.present = NR_CSI_ReportConfig__groupBasedBeamReporting_PR_disabled; csirep->groupBasedBeamReporting.choice.disabled = calloc(1, sizeof(*csirep->groupBasedBeamReporting.choice.disabled)); From 78f36b64c2acc4f55c2ae7ae366b4bcf22a1a1be Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 22 Oct 2025 12:35:25 +0200 Subject: [PATCH 040/275] use of config module handling for CSI report type input --- openair2/GNB_APP/gnb_config.c | 13 ++++--------- openair2/GNB_APP/gnb_paramdef.h | 5 ++++- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 075bbd53fe..056387de92 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1432,14 +1432,8 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) config.do_CSIRS = *GNBParamList.paramarray[0][GNB_DO_CSIRS_IDX].iptr; config.do_SRS = *GNBParamList.paramarray[0][GNB_DO_SRS_IDX].iptr; config.max_num_rsrp = *GNBParamList.paramarray[0][GNB_LIMIT_RSRP_REPORT_IDX].iptr; - if (strcmp(*(GNBParamList.paramarray[0][GNB_CONFIG_REP_IDX].strptr), "ssb_rsrp") == 0) - config.report_type = SSB_RSRP; - else if (strcmp(*(GNBParamList.paramarray[0][GNB_CONFIG_REP_IDX].strptr), "ssb_sinr") == 0) - config.report_type = SSB_SINR; - else if (strcmp(*(GNBParamList.paramarray[0][GNB_CONFIG_REP_IDX].strptr), "cri_rsrp") == 0) - config.report_type = CRI_RSRP; - else - AssertFatal(false, "Invalid report type\n"); + const char *report_type_s = *GNBParamList.paramarray[0][GNB_CONFIG_REP_IDX].strptr; + config.report_type = config_get_processedint(cfg, &GNBParamList.paramarray[0][GNB_CONFIG_REP_IDX]); config.force_256qam_off = *GNBParamList.paramarray[0][GNB_FORCE256QAMOFF_IDX].iptr; config.force_UL256qam_off = *GNBParamList.paramarray[0][GNB_FORCEUL256QAMOFF_IDX].iptr; config.use_deltaMCS = *GNBParamList.paramarray[0][GNB_USE_DELTA_MCS_IDX].iptr != 0; @@ -1450,10 +1444,11 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) if (config.disable_harq) LOG_W(GNB_APP, "\"disable_harq\" is a REL17 feature and is incompatible with REL15 and REL16 UEs!\n"); LOG_I(GNB_APP, - "CSI-RS %d, SRS %d, reprt type:%d, 256 QAM %s, delta_MCS %s, maxMIMO_Layers %d, HARQ feedback %s, num DLHARQ:%d, num ULHARQ:%d\n", + "CSI-RS %d, SRS %d, report type %d (%s), 256 QAM %s, delta_MCS %s, maxMIMO_Layers %d, HARQ feedback %s, num DLHARQ:%d, num ULHARQ:%d\n", config.do_CSIRS, config.do_SRS, config.report_type, + report_type_s, config.force_256qam_off ? "force off" : "may be on", config.use_deltaMCS ? "on" : "off", config.maxMIMO_layers, diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index 8efc4f999f..4ec09d06f0 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -293,7 +293,10 @@ typedef enum { { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ - { .s5 = { NULL } }, \ + { .s3a = { config_checkstr_assign_integer, \ + {"ssb_rsrp", "ssb_sinr", "cri_rsrp"}, \ + {SSB_RSRP, SSB_SINR, CRI_RSRP}, \ + 3 } }, \ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ } From 61b54178179f65f7fa2be95a974a89585db92ed3 Mon Sep 17 00:00:00 2001 From: Shubhika Garg Date: Fri, 17 Oct 2025 11:00:08 +0200 Subject: [PATCH 041/275] ci: Update and implement conditional DockerHub push - Push both AMD64 & ARMv8 Jetson images together for images built on both using `docker buildx imagetools` - Use standard 'docker pull and push' for images built only on AMD64. --- ci-scripts/Jenkinsfile-push-registry | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ci-scripts/Jenkinsfile-push-registry b/ci-scripts/Jenkinsfile-push-registry index ccd91e28ff..60b8777ba9 100644 --- a/ci-scripts/Jenkinsfile-push-registry +++ b/ci-scripts/Jenkinsfile-push-registry @@ -84,9 +84,20 @@ pipeline { sh "docker login -u ${DH_Username} -p ${DH_Password} > /dev/null 2>&1" sh "docker login -u oaicicd -p oaicicd ${OAI_Registry} > /dev/null 2>&1" listOfImages.eachWithIndex { item, iindex -> - sh "docker rmi ${DH_Account}/${item}:${WEEK_TAG} ${DH_Account}/${item}:develop || true" - sh "docker buildx imagetools create --tag ${DH_Account}/${item}:${WEEK_TAG} ${OAI_Registry}/${item}:armv8_develop-${WEEK_SHA} ${OAI_Registry}/${item}:develop-${WEEK_SHA}" - sh "docker buildx imagetools create --tag ${DH_Account}/${item}:develop ${OAI_Registry}/${item}:armv8_develop-${WEEK_SHA} ${OAI_Registry}/${item}:develop-${WEEK_SHA}" + if (["oai-gnb", "oai-nr-ue", "oai-nr-cuup"].contains(item)) { + // For images built on AMD64 and ARMv8 Jetson + echo "Pushing image '${item}' for both AMD64 and ARM64 architectures with tags: ${WEEK_TAG} and develop" + sh "docker rmi ${DH_Account}/${item}:${WEEK_TAG} ${DH_Account}/${item}:develop || true" + sh "docker buildx imagetools create --tag ${DH_Account}/${item}:${WEEK_TAG} ${OAI_Registry}/${item}:armv8_develop-${WEEK_SHA} ${OAI_Registry}/${item}:develop-${WEEK_SHA}" + sh "docker buildx imagetools create --tag ${DH_Account}/${item}:develop ${OAI_Registry}/${item}:armv8_develop-${WEEK_SHA} ${OAI_Registry}/${item}:develop-${WEEK_SHA}" + } else { + sh "docker pull --quiet ${OAI_Registry}/${item}:develop-${WEEK_SHA}" + sh "docker image tag ${OAI_Registry}/${item}:develop-${WEEK_SHA} ${DH_Account}/${item}:develop" + sh "docker image tag ${OAI_Registry}/${item}:develop-${WEEK_SHA} ${DH_Account}/${item}:${WEEK_TAG}" + sh "docker push --quiet ${DH_Account}/${item}:develop" + sh "docker push --quiet ${DH_Account}/${item}:${WEEK_TAG}" + } + sh "docker rmi ${DH_Account}/${item}:${WEEK_TAG} ${DH_Account}/${item}:develop ${OAI_Registry}/${item}:develop-${WEEK_SHA} || true" } // Proxy is not following the same pattern. sh "docker image tag proxy:develop ${DH_Account}/proxy:develop" From 3247c2933f5e1eba18bb4e4f9ad6f6909c92cfec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Soares=20Silva?= Date: Mon, 13 Oct 2025 08:20:52 +0100 Subject: [PATCH 042/275] Rework WLS VNF to be able to start before the PNF The WLS VNF is reworked to be able to start before the PNF, by calling call rte_eal_init and rte_dev_probe to determine if dpdk has been initialized properly, before calling rte_eal_init in the intended process when it is appropriate to do so --- .../oai_integration/wls_integration/wls_vnf.c | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/nfapi/oai_integration/wls_integration/wls_vnf.c b/nfapi/oai_integration/wls_integration/wls_vnf.c index 79934164f4..ab2d5d4454 100644 --- a/nfapi/oai_integration/wls_integration/wls_vnf.c +++ b/nfapi/oai_integration/wls_integration/wls_vnf.c @@ -26,8 +26,10 @@ #include #include #include -#include #include +#include "common/utils/LOG/log.h" +#include +#include #include static nfapi_vnf_config_t *config; @@ -92,17 +94,54 @@ static bool wls_mac_create_mem_array(PWLS_MAC_MEM_SRUCT pMemArray, void *pMemArr return true; } -void mac_dpdk_init() +bool primary_process_running(int argc, char *argv[]) { - char *my_argv[] = {"OAI_VNF", "-c3", "--proc-type=auto", "--file-prefix", WLS_DEV_NAME, "--iova-mode=pa"}; - printf("\nCalling rte_eal_init: "); - for (unsigned long i = 0; i < RTE_DIM(my_argv); i++) { - printf("%s ", my_argv[i]); - } - printf("\n"); + // If the PNF was stopped normally, this is enough, as the mp_socket file does not exist + if (access("/var/run/dpdk/" WLS_DEV_NAME "/mp_socket", W_OK | R_OK) != 0) + return false; - if (rte_eal_init(RTE_DIM(my_argv), my_argv) < 0) - rte_panic("\nCannot init EAL\n"); + // If we run rte_eal_init before the primary process is running, it will result in a 'Connection Refused' error in the logs, but + // the call itself will succeed ( retval not -1 ) + // + // We can use rte_eal_primary_proc_alive to determine if the primary process is running, the issue is that since the init had a + // refused connection, if we try to open WLS after rte_eal_primary_proc_alive reports that the primary is running, it will fail, + // since rte_eal_init failed to connect to the primary process, and we can't call rte_eal_init in the same process more than once, + // or it will result in an EALREADY error + // + // Given this, a new process is created that calls rte_eal_init and checks whether the primary process is running, exiting + // right after and reporting to our VNF the primary status + // + // When the VNF receives the status from the forked process that the + // primary process is running, it then calls rte_eal_init, which at this point we know will be successful, and continue to open + // the WLS instance + pid_t pid = fork(); + if (pid == 0) { + // We set the log level to emergency , effectively disabling logs in the child process + // This prevents too many logs being printed out to the terminal while we wait for the primary process to be available + rte_log_set_global_level(RTE_LOG_EMERG); + AssertFatal(rte_eal_init(argc, argv) != -1, "rte_eal_init() failed\n"); + int status = rte_eal_primary_proc_alive(NULL) == 0; + rte_eal_cleanup(); + _exit(status); + } + int status; + waitpid(pid, &status, 0); + return WIFEXITED(status) && WEXITSTATUS(status) == 0; +} + +bool mac_dpdk_init() +{ + char *my_argv[] = {"OAI_VNF", "-c3", "--proc-type=secondary", "--file-prefix", WLS_DEV_NAME, "--iova-mode=pa"}; + NFAPI_TRACE(NFAPI_TRACE_DEBUG, "\nCalling rte_eal_init: "); + for (int i = 0; i < RTE_DIM(my_argv); i++) { + NFAPI_TRACE(NFAPI_TRACE_DEBUG, "%s ", my_argv[i]); + } + NFAPI_TRACE(NFAPI_TRACE_DEBUG, "\n"); + while (!primary_process_running(RTE_DIM(my_argv), my_argv)) { + NFAPI_TRACE(NFAPI_TRACE_INFO, "DPDK primary process not available yet, retrying in 1 second...\n"); + sleep(1); + } + return rte_eal_init(RTE_DIM(my_argv), my_argv) >= 0; } static uint32_t wls_mac_alloc_mem_array(PWLS_MAC_MEM_SRUCT pMemArray, void **ppBlock) From e08d904af0c06738249136046259a9917750ce7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Soares=20Silva?= Date: Tue, 26 Aug 2025 15:29:21 +0100 Subject: [PATCH 043/275] Implementation of STOP exchange This implements the STOP.request/indication for all 3 transport mechanisms, the VNF will send the STOP.request to the PNF, indicating it will disconnect, and await a STOP.indication from the PNF The WLS VNF is reworked to be able to start before the PNF, by calling call rte_eal_init and rte_dev_probe to determine if dpdk has been initialized properly, before calling rte_eal_init in the intended process when it is appropriate to do so --- executables/nr-softmodem.c | 2 + nfapi/oai_integration/aerial/fapi_nvIPC.c | 28 ++++++--- nfapi/oai_integration/aerial/fapi_nvIPC.h | 2 + nfapi/oai_integration/nfapi_pnf.c | 30 ++++++++++ nfapi/oai_integration/nfapi_pnf.h | 2 +- nfapi/oai_integration/nfapi_vnf.c | 57 +++++++++++++++++++ nfapi/oai_integration/nfapi_vnf.h | 1 + .../socket/include/socket_pnf.h | 1 + .../socket/include/socket_vnf.h | 1 + nfapi/oai_integration/socket/socket_pnf.c | 10 +++- nfapi/oai_integration/socket/socket_vnf.c | 15 +++++ .../wls_integration/include/wls_common.h | 1 - .../wls_integration/include/wls_pnf.h | 2 + .../wls_integration/include/wls_vnf.h | 2 + .../wls_integration/wls_common.c | 12 +++- .../oai_integration/wls_integration/wls_pnf.c | 35 +++++++++++- .../oai_integration/wls_integration/wls_vnf.c | 38 ++++++++++++- nfapi/open-nFAPI/nfapi/src/nfapi_p5.c | 8 +++ .../pnf/public_inc/nfapi_pnf_interface.h | 8 +++ nfapi/open-nFAPI/pnf/src/pnf_interface.c | 12 ++++ .../vnf/public_inc/nfapi_vnf_interface.h | 19 +++++++ nfapi/open-nFAPI/vnf/src/vnf.c | 28 +++++++++ nfapi/open-nFAPI/vnf/src/vnf_interface.c | 12 ++++ openair1/SCHED/nfapi_lte_dummy.c | 24 ++++++++ openair2/GNB_APP/gnb_config.c | 12 ++++ openair2/GNB_APP/gnb_config.h | 2 +- 26 files changed, 344 insertions(+), 20 deletions(-) diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index d8df770cb8..a11941bff4 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -716,6 +716,8 @@ int main( int argc, char **argv ) { itti_wait_tasks_end(NULL); printf("Returned from ITTI signal handler\n"); + nfapi_stop_l1(); + if (RC.nb_nr_L1_inst > 0 || RC.nb_RU > 0) stop_L1(0); diff --git a/nfapi/oai_integration/aerial/fapi_nvIPC.c b/nfapi/oai_integration/aerial/fapi_nvIPC.c index 9cd964cc0c..05c08d391d 100644 --- a/nfapi/oai_integration/aerial/fapi_nvIPC.c +++ b/nfapi/oai_integration/aerial/fapi_nvIPC.c @@ -63,6 +63,18 @@ char cpu_buf_recv[RECV_BUF_LEN]; uint16_t sfn = 0, slot = 0; nv_ipc_t *ipc; +void nvIPC_Stop() +{ + LOG_I(NR_MAC, "Received STOP.indication\n"); + ((vnf_t *)get_config())->terminate = true; +} + +void nvIPC_send_stop_request() +{ + nfapi_nr_stop_request_scf_t req = {.header.message_id = NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST, .header.phy_id = 0}; + LOG_I(NR_MAC, "Sending NFAPI STOP.request\n"); + nfapi_nr_vnf_stop_req(get_config(), get_config()->pnf_list->p5_idx, &req); +} static uint16_t old_sfn = 0; @@ -476,7 +488,6 @@ static int aerial_recv_msg(nv_ipc_t *ipc, nv_ipc_msg_t *recv_msg) return 0; } -bool recv_task_running = false; void *epoll_recv_task(void *arg) { struct epoll_event ev, events[MAX_EVENTS]; @@ -494,11 +505,12 @@ void *epoll_recv_task(void *arg) if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) { LOG_E(NFAPI_VNF, "%s epoll_ctl failed\n", __func__); } - - while (1) { - if (!recv_task_running) { - recv_task_running = true; - } + // From here on out the thread is ready to receive data, simulate the reception + // of a PARAM.response to get the VNF to send a CONFIG.request + nfapi_nr_param_response_scf_t resp_msg = {.header.message_id = NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE}; + nfapi_vnf_config_t * vnf_config = get_config(); + vnf_config->nr_param_resp(vnf_config, 1, &resp_msg); + while (((vnf_t *)vnf_config)->terminate == false) { LOG_D(NFAPI_VNF, "%s: epoll_wait fd_rx=%d ...\n", __func__, ipc_rx_event_fd); int nfds; @@ -521,6 +533,7 @@ void *epoll_recv_task(void *arg) } } close(epoll_fd); + ipc->ipc_destroy(ipc); return NULL; } @@ -565,9 +578,6 @@ int nvIPC_Init(nvipc_params_t nvipc_params_s) LOG_I(NFAPI_VNF, "%s: create IPC interface successful\n", __func__); sleep(1); create_recv_thread(nvipc_params_s.nvipc_poll_core); - while(!recv_task_running){usleep(100000);} - nfapi_nr_param_response_scf_t resp_msg; - vnf_config->nr_param_resp(vnf_config, 1, &resp_msg); return 0; } diff --git a/nfapi/oai_integration/aerial/fapi_nvIPC.h b/nfapi/oai_integration/aerial/fapi_nvIPC.h index 72cf66bde7..8740c1ecd3 100644 --- a/nfapi/oai_integration/aerial/fapi_nvIPC.h +++ b/nfapi/oai_integration/aerial/fapi_nvIPC.h @@ -63,4 +63,6 @@ int oai_fapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req); int oai_fapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req); int oai_fapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req); int oai_fapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req); +void nvIPC_Stop(); +void nvIPC_send_stop_request(); #endif // OPENAIRINTERFACE_FAPI_NVIPC_H diff --git a/nfapi/oai_integration/nfapi_pnf.c b/nfapi/oai_integration/nfapi_pnf.c index 3de11d8003..f884ad9187 100644 --- a/nfapi/oai_integration/nfapi_pnf.c +++ b/nfapi/oai_integration/nfapi_pnf.c @@ -1836,6 +1836,35 @@ int nr_start_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nf return 0; } +void stop_nr_nfapi_pnf() +{ +#ifdef ENABLE_WLS + wls_fapi_nr_pnf_stop(); +#endif +#ifdef ENABLE_SOCKET + socket_nfapi_nr_pnf_stop(); +#endif +} +static bool has_sent_stop_ind = false; +int nr_stop_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nfapi_nr_stop_request_scf_t *req) +{ + if (has_sent_stop_ind) { + // STOP.indication already sent, nothing to do + return 0; + } + NFAPI_TRACE(NFAPI_TRACE_INFO, "Send STOP.indication\n"); + nfapi_nr_stop_indication_scf_t resp = {.header.message_id = NFAPI_NR_PHY_MSG_TYPE_STOP_INDICATION, + .header.phy_id = req->header.phy_id}; + nfapi_nr_stop_indication(config, &resp); + has_sent_stop_ind = true; +#ifdef ENABLE_WLS + wls_pnf_close(pnf_p5_init_and_receive_pthread); +#endif + // raise the SIGTERM to simulate CTRL+C + raise(SIGTERM); + return 0; +} + int measurement_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nfapi_measurement_request_t *req) { nfapi_measurement_response_t resp; memset(&resp, 0, sizeof(resp)); @@ -2191,6 +2220,7 @@ void configure_nr_nfapi_pnf(char *vnf_ip_addr, int vnf_p5_port, char *pnf_ip_add config->nr_param_req = &nr_param_request; config->nr_config_req = &nr_config_request; config->nr_start_req = &nr_start_request; + config->nr_stop_req = &nr_stop_request; config->rssi_req = &rssi_request; config->broadcast_detect_req = &broadcast_detect_request; config->system_information_schedule_req = &system_information_schedule_request; diff --git a/nfapi/oai_integration/nfapi_pnf.h b/nfapi/oai_integration/nfapi_pnf.h index 0ba0676f10..b9362785c9 100644 --- a/nfapi/oai_integration/nfapi_pnf.h +++ b/nfapi/oai_integration/nfapi_pnf.h @@ -37,5 +37,5 @@ int oai_nfapi_nr_crc_indication(nfapi_nr_crc_indication_t *ind); int oai_nfapi_nr_srs_indication(nfapi_nr_srs_indication_t *ind); int oai_nfapi_nr_uci_indication(nfapi_nr_uci_indication_t *ind); int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind); - +void stop_nr_nfapi_pnf(); #endif /* NFAPI_PNF_H_ */ diff --git a/nfapi/oai_integration/nfapi_vnf.c b/nfapi/oai_integration/nfapi_vnf.c index c2193fcaa3..0e9d641666 100644 --- a/nfapi/oai_integration/nfapi_vnf.c +++ b/nfapi/oai_integration/nfapi_vnf.c @@ -1749,6 +1749,62 @@ void vnf_start_thread(void *ptr) { nfapi_vnf_start((nfapi_vnf_config_t *)ptr); } +static bool has_stop_ind = false; +static bool waiting_stop_ind = false; +int nr_stop_ind_cb(nfapi_vnf_config_t *config, int p5_idx, nfapi_nr_stop_indication_scf_t *resp) +{ + NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Received NFAPI_STOP_IND idx:%d phy_id:%d\n", p5_idx, resp->header.phy_id); + has_stop_ind = true; +#ifdef ENABLE_AERIAL + nvIPC_Stop(); +#endif +#ifdef ENABLE_SOCKET + nfapi_vnf_p7_stop(get_p7_vnf_config()); +#endif +#ifdef ENABLE_WLS + wls_vnf_stop(); +#endif + if (!waiting_stop_ind) { + // hasn't been initialized yet, means the PNF stopped before the VNF did + // raise a SIGINT to stop the VNF + raise(SIGINT); + } + return 0; +} + +void stop_nr_nfapi_vnf() +{ + if (has_stop_ind) { + // If it got here with the STOP.indication flag already set, it means it was triggered by the PNF, + // no need to send a STOP.request + return; + } +#ifdef ENABLE_WLS + wls_vnf_send_stop_request(); +#endif +#ifdef ENABLE_AERIAL + nvIPC_send_stop_request(); +#endif +#ifdef ENABLE_SOCKET + socket_nfapi_send_stop_request((vnf_t *)config); +#endif + waiting_stop_ind = true; + uint64_t counter = 0; + vnf_p7_t *p7_vnf = get_p7_vnf(); + while (p7_vnf->terminate == 0 && counter < 50) { + NFAPI_TRACE(NFAPI_TRACE_DEBUG, "Not terminated yet, counter %ld\n", counter); + usleep(1000); + counter++; + } + if (p7_vnf->terminate == 0) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "STOP.indication timed out, exiting\n"); + nfapi_nr_stop_indication_scf_t msg = {.header.message_id = NFAPI_NR_PHY_MSG_TYPE_STOP_INDICATION, .header.phy_id = 0}; + config->nr_stop_ind(config, 0, &msg); + } else { + NFAPI_TRACE(NFAPI_TRACE_DEBUG, "Terminated, exiting\n"); + } +} + void configure_nr_nfapi_vnf(eth_params_t params) { #ifndef ENABLE_AERIAL @@ -1790,6 +1846,7 @@ void configure_nr_nfapi_vnf(eth_params_t params) config->nr_param_resp = &nr_param_resp_cb; config->nr_config_resp = &nr_config_resp_cb; config->nr_start_resp = &nr_start_resp_cb; + config->nr_stop_ind = &nr_stop_ind_cb; config->nr_error_ind = &nr_error_ind_cb; config->vendor_ext = &vendor_nr_ext_cb; config->user_data = vnf; diff --git a/nfapi/oai_integration/nfapi_vnf.h b/nfapi/oai_integration/nfapi_vnf.h index 3e72896d37..d71f9a2926 100644 --- a/nfapi/oai_integration/nfapi_vnf.h +++ b/nfapi/oai_integration/nfapi_vnf.h @@ -148,6 +148,7 @@ typedef struct { void configure_nfapi_vnf(char *vnf_addr, int vnf_p5_port, char *pnf_ip_addr, int pnf_p7_port, int vnf_p7_port); void configure_nr_nfapi_vnf(eth_params_t params); +void stop_nr_nfapi_vnf(); nfapi_vnf_config_t *get_config(); vnf_p7_t *get_p7_vnf(); nfapi_vnf_p7_config_t *get_p7_vnf_config(); diff --git a/nfapi/oai_integration/socket/include/socket_pnf.h b/nfapi/oai_integration/socket/include/socket_pnf.h index ccb3eaea3a..cf5258eabb 100644 --- a/nfapi/oai_integration/socket/include/socket_pnf.h +++ b/nfapi/oai_integration/socket/include/socket_pnf.h @@ -36,4 +36,5 @@ bool pnf_nr_send_p5_message(pnf_t *pnf, nfapi_nr_p4_p5_message_header_t *msg, ui bool pnf_nr_send_p7_message(pnf_p7_t* pnf_p7, nfapi_nr_p7_message_header_t* header, uint32_t msg_len); void *pnf_start_p5_thread(void *ptr); void *pnf_nr_p7_thread_start(void *ptr); +void socket_nfapi_nr_pnf_stop(); #endif // SOCKET_PNF_H diff --git a/nfapi/oai_integration/socket/include/socket_vnf.h b/nfapi/oai_integration/socket/include/socket_vnf.h index c11e009153..65c36d034f 100644 --- a/nfapi/oai_integration/socket/include/socket_vnf.h +++ b/nfapi/oai_integration/socket/include/socket_vnf.h @@ -31,4 +31,5 @@ bool vnf_nr_send_p5_msg(vnf_t *vnf, uint16_t p5_idx, nfapi_nr_p4_p5_message_head bool vnf_nr_send_p7_msg(vnf_p7_t *vnf_p7, nfapi_nr_p7_message_header_t* header); void vnf_start_p5_thread(void* ptr); void* vnf_nr_start_p7_thread(void* ptr); +void socket_nfapi_send_stop_request(vnf_t *vnf); #endif // SOCKET_VNF_H diff --git a/nfapi/oai_integration/socket/socket_pnf.c b/nfapi/oai_integration/socket/socket_pnf.c index ad6c0dec0c..6a96247b74 100644 --- a/nfapi/oai_integration/socket/socket_pnf.c +++ b/nfapi/oai_integration/socket/socket_pnf.c @@ -19,10 +19,16 @@ * contact@openairinterface.org */ #include "socket_pnf.h" -#include "nfapi.h" - #include "nfapi.h" static nfapi_pnf_config_t *config; +void socket_nfapi_nr_pnf_stop() +{ + AssertFatal(config->nr_stop_req, "STOP.request Handler should be set!"); + nfapi_nr_stop_request_scf_t req = {0}; + req.header.phy_id = 0; + req.header.message_id = NFAPI_NR_PHY_MSG_TYPE_PNF_STOP_REQUEST; + config->nr_stop_req(config, 0, &req); +} bool pnf_nr_send_p5_message(pnf_t *pnf, nfapi_nr_p4_p5_message_header_t *msg, uint32_t msg_len) { diff --git a/nfapi/oai_integration/socket/socket_vnf.c b/nfapi/oai_integration/socket/socket_vnf.c index 518f57c2e3..75d4a37026 100644 --- a/nfapi/oai_integration/socket/socket_vnf.c +++ b/nfapi/oai_integration/socket/socket_vnf.c @@ -20,11 +20,26 @@ */ #include "socket_vnf.h" +#include "common/utils/LOG/log.h" #include "nfapi.h" #include "nfapi_vnf.h" #include #include "nfapi/oai_integration/vendor_ext.h" //TODO: Remove this include when removing the Aerial transport stuff + +void socket_stop_nfapi_p5_p7() +{ + get_p7_vnf()->terminate = 1; + get_config()->pnf_disconnect_indication = NULL; +} + +void socket_nfapi_send_stop_request(vnf_t *vnf) +{ + nfapi_nr_stop_request_scf_t req = {.header.message_id = NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST, .header.phy_id = 0}; + nfapi_nr_vnf_stop_req(&vnf->_public, 0, &req); + NFAPI_TRACE(NFAPI_TRACE_INFO, "Sent NFAPI STOP.request\n"); +} + static bool send_p5_msg(vnf_t *vnf, nfapi_vnf_pnf_info_t *pnf, const void *msg, int len, uint8_t stream) { int result = socket_send_p5_msg(vnf->sctp, pnf->p5_sock, &pnf->p5_pnf_sockaddr, msg, len, stream); diff --git a/nfapi/oai_integration/wls_integration/include/wls_common.h b/nfapi/oai_integration/wls_integration/include/wls_common.h index 1b6a8e4cdf..e823e93380 100644 --- a/nfapi/oai_integration/wls_integration/include/wls_common.h +++ b/nfapi/oai_integration/wls_integration/include/wls_common.h @@ -25,7 +25,6 @@ #include #include #include -#include "wls_lib.h" #include "nfapi/open-nFAPI/fapi/inc/nr_fapi_p5_utils.h" #include "nfapi/open-nFAPI/fapi/inc/nr_fapi_p7_utils.h" diff --git a/nfapi/oai_integration/wls_integration/include/wls_pnf.h b/nfapi/oai_integration/wls_integration/include/wls_pnf.h index c678345967..ca89761b27 100644 --- a/nfapi/oai_integration/wls_integration/include/wls_pnf.h +++ b/nfapi/oai_integration/wls_integration/include/wls_pnf.h @@ -30,4 +30,6 @@ bool wls_pnf_nr_send_p7_message(pnf_p7_t* pnf_p7,nfapi_nr_p7_message_header_t *m void *wls_fapi_pnf_nr_start_thread(void *ptr); int wls_fapi_nr_pnf_start(); void wls_pnf_set_p7_config(void *p7_config); +void wls_fapi_nr_pnf_stop(); +void wls_pnf_close(pthread_t p5_thread); #endif // OPENAIRINTERFACE_WLS_PNF_H diff --git a/nfapi/oai_integration/wls_integration/include/wls_vnf.h b/nfapi/oai_integration/wls_integration/include/wls_vnf.h index 46a8766b6d..9206a03d62 100644 --- a/nfapi/oai_integration/wls_integration/include/wls_vnf.h +++ b/nfapi/oai_integration/wls_integration/include/wls_vnf.h @@ -29,4 +29,6 @@ void *wls_fapi_vnf_nr_start_thread(void *ptr); int wls_fapi_nr_vnf_start(); 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); bool wls_vnf_nr_send_p7_message(vnf_p7_t* vnf_p7,nfapi_nr_p7_message_header_t* msg); +void wls_vnf_send_stop_request(); +void wls_vnf_stop(); #endif //OPENAIRINTERFACE_WLS_VNF_H diff --git a/nfapi/oai_integration/wls_integration/wls_common.c b/nfapi/oai_integration/wls_integration/wls_common.c index fe4112eba8..6b471e0e96 100644 --- a/nfapi/oai_integration/wls_integration/wls_common.c +++ b/nfapi/oai_integration/wls_integration/wls_common.c @@ -20,7 +20,7 @@ */ #include "wls_common.h" - +#include "wls_lib.h" void fill_fapi_list_elem(p_fapi_api_queue_elem_t currElem, p_fapi_api_queue_elem_t nextElem, const uint8_t msgType, const uint8_t numMsgInBlock, const uint32_t alignOffset) { currElem->msg_type = msgType; @@ -90,9 +90,19 @@ uint8_t wls_send_fapi_msg(PWLS_MAC_CTX pWls, const uint16_t message_id, const in { int num_avail_blocks = WLS_NumBlocks(pWls->hWls); NFAPI_TRACE(NFAPI_TRACE_DEBUG,"num_avail_blocks is %d\n", num_avail_blocks); + int counter = 0; while (num_avail_blocks < 2) { num_avail_blocks = WLS_NumBlocks(pWls->hWls); NFAPI_TRACE(NFAPI_TRACE_DEBUG,"num_avail_blocks is %d\n", num_avail_blocks); + if (message_id == NFAPI_NR_PHY_MSG_TYPE_STOP_INDICATION) { + // In the case of the STOP.indication, it can happen that this is called after the VNF stopped + // For example, if it crashed for some reason, in that case, implement a timeout and return + usleep(10 * 1000); + counter++; + if (counter == 10) { + return true; + } + } } /* get PA blocks for header and msg */ uint64_t pa_hdr = dequeueBlock(pWls); diff --git a/nfapi/oai_integration/wls_integration/wls_pnf.c b/nfapi/oai_integration/wls_integration/wls_pnf.c index bed517ad01..e6fd609346 100644 --- a/nfapi/oai_integration/wls_integration/wls_pnf.c +++ b/nfapi/oai_integration/wls_integration/wls_pnf.c @@ -29,9 +29,10 @@ #include #include +#include "wls_lib.h" #include "pnf.h" #include "nr_nfapi_p7.h" -#include "nr_fapi_p5.h" +#include "nfapi/open-nFAPI/fapi/inc/nr_fapi_p7_utils.h" // A copy of the PNF P7 config is needed for the message processing thread, in other transport mechanisms where P7 is handled in a // different thread, the config is passed to it upon the thread creation @@ -65,6 +66,28 @@ void *wls_fapi_pnf_nr_start_thread(void *ptr) return (void *)0; } +void wls_pnf_close(pthread_t p5_thread) +{ + _this->terminate = 1; + WLS_WakeUp(wls_mac_get_ctx()->hWls); + pthread_join(p5_thread, NULL); + if (WLS_Close(wls_mac_get_ctx()->hWls) != 0) { + NFAPI_TRACE(NFAPI_TRACE_INFO, "[PNF] WLS_CLose call failed...\n"); + } else { + NFAPI_TRACE(NFAPI_TRACE_INFO, "[PNF] WLS_CLose called successfully...\n"); + } + rte_eal_cleanup(); +} + +void wls_fapi_nr_pnf_stop() +{ + AssertFatal(cfg->nr_stop_req, "STOP.request Handler should be set!"); + nfapi_nr_stop_request_scf_t req = {0}; + req.header.phy_id = 0; + req.header.message_id = NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST; + cfg->nr_stop_req(cfg, 0, &req); +} + int wls_fapi_nr_pnf_start() { int64_t ret; @@ -194,6 +217,9 @@ bool wls_pnf_nr_send_p7_message(pnf_p7_t *pnf_p7, nfapi_nr_p7_message_header_t * NFAPI_TRACE(NFAPI_TRACE_DEBUG, "num_avail_blocks is %d\n", num_avail_blocks); while (num_avail_blocks < 2) { num_avail_blocks = WLS_NumBlocks(pWls->hWls); + if (_this->terminate) { + return false; + } NFAPI_TRACE(NFAPI_TRACE_DEBUG, "num_avail_blocks is %d\n", num_avail_blocks); } /* get PA blocks for header and msg */ @@ -254,7 +280,7 @@ void phy_mac_recv() bool to_return = false; while (_this->terminate == 0) { int numMsgToGet = WLS_Wait(pWls->hWls); - if (numMsgToGet == 0) { + if (numMsgToGet == 0 || _this->terminate) { continue; } uint64_t PAs[numMsgToGet]; @@ -279,6 +305,11 @@ void phy_mac_recv() } } } + // A STOP.indication might have been processed, which set's terminate to 1 + // don't return pages in that case + if (_this->terminate) { + break; + } if (to_return) { // We only return the pages to be enqueued in case we're using the OAI VNF, // The Radisys O-DU handles enqueueing the blocks differently, and also doesn't handle receiving a single block diff --git a/nfapi/oai_integration/wls_integration/wls_vnf.c b/nfapi/oai_integration/wls_integration/wls_vnf.c index ab2d5d4454..c72e17a2b6 100644 --- a/nfapi/oai_integration/wls_integration/wls_vnf.c +++ b/nfapi/oai_integration/wls_integration/wls_vnf.c @@ -21,7 +21,8 @@ #include "wls_vnf.h" #include "vnf_p7.h" - +#include "common/utils/LOG/log.h" +#include "wls_lib.h" #include #include #include @@ -31,8 +32,9 @@ #include #include #include +#include +#include "nfapi_vnf.h" -static nfapi_vnf_config_t *config; static WLS_MAC_CTX wls_mac_iface; vnf_t *_vnf = NULL; @@ -306,6 +308,33 @@ static int vnf_wls_init() return 1; } +void wls_vnf_stop() +{ + vnf_p7_t *p7_vnf = get_p7_vnf(); + _vnf->terminate = 1; + p7_vnf->terminate = 1; + rte_eal_cleanup(); +} + +void wls_vnf_send_stop_request() +{ + PWLS_MAC_CTX pWls = wls_mac_get_ctx(); + nfapi_nr_stop_request_scf_t req = {.header.message_id = NFAPI_NR_PHY_MSG_TYPE_STOP_REQUEST, .header.phy_id = 0}; + vnf_p7_t *p7_vnf = get_p7_vnf(); + nfapi_vnf_config_t * config = get_config(); + if (p7_vnf == NULL || pWls->hWls == NULL) { + nfapi_nr_stop_indication_scf_t msg; + msg.header.message_id = NFAPI_NR_PHY_MSG_TYPE_STOP_INDICATION; + msg.header.phy_id = 0; + config->nr_stop_ind(config, 0, &msg); + return; + } + + if (pWls->hWls != NULL) { + nfapi_nr_vnf_stop_req(config, 0, &req); + } +} + void *wls_fapi_vnf_nr_start_thread(void *ptr) { NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] IN WLS PNF NFAPI start thread %s\n", __FUNCTION__); @@ -366,7 +395,7 @@ int wls_fapi_nr_vnf_start(nfapi_vnf_config_t *cfg) PWLS_MAC_CTX pWls = wls_mac_get_ctx(); while (_vnf->terminate == 0) { int numMsgToGet = WLS_Wait(pWls->hWls); - if (numMsgToGet == 0) { + if (numMsgToGet == 0 || _vnf->terminate) { continue; } @@ -384,6 +413,9 @@ int wls_fapi_nr_vnf_start(nfapi_vnf_config_t *cfg) procPhyMessages(len, (void *)(msg + 1), msg->msg_type); } } + if (_vnf->terminate) { + break; + } for (int i = 0; i < pa_idx; i++) { if (WLS_EnqueueBlock(pWls->hWls, PAs[i]) == -1) { NFAPI_TRACE(NFAPI_TRACE_DEBUG, "Error in WLS_EnqueueBlock for block %lu\n", PAs[i]); diff --git a/nfapi/open-nFAPI/nfapi/src/nfapi_p5.c b/nfapi/open-nFAPI/nfapi/src/nfapi_p5.c index 4516302e48..7d7388bad5 100644 --- a/nfapi/open-nFAPI/nfapi/src/nfapi_p5.c +++ b/nfapi/open-nFAPI/nfapi/src/nfapi_p5.c @@ -1254,6 +1254,10 @@ uint8_t pack_nr_p5_message_body(nfapi_nr_p4_p5_message_header_t *header, result = pack_stop_request(header, ppWritePackedMsg, end, config); break; + case NFAPI_NR_PHY_MSG_TYPE_STOP_INDICATION: + result = pack_nr_stop_indication(header, ppWritePackedMsg, end, config); + break; + case NFAPI_NR_PHY_MSG_TYPE_STOP_RESPONSE: result = pack_stop_response(header, ppWritePackedMsg, end, config); break; @@ -2447,6 +2451,10 @@ bool nfapi_nr_p5_message_unpack(void *pMessageBuf, result = unpack_stop_request(&pReadPackedMessage, end, pMessageHeader, config); break; + case NFAPI_NR_PHY_MSG_TYPE_STOP_INDICATION: + result = unpack_nr_stop_indication(&pReadPackedMessage, end, pMessageHeader, config); + break; + case NFAPI_NR_PHY_MSG_TYPE_STOP_RESPONSE: result = unpack_stop_response(&pReadPackedMessage, end, pMessageHeader, config); break; diff --git a/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h b/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h index 957e032f69..c6d5ee7c4d 100644 --- a/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h +++ b/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h @@ -497,6 +497,14 @@ int nfapi_nr_pnf_start_resp(nfapi_pnf_config_t* config, nfapi_nr_start_response_ */ int nfapi_pnf_stop_resp(nfapi_pnf_config_t* config, nfapi_stop_response_t* resp); +/*! Send the STOP.indication + * \param config A pointer to a pnf configuration + * \param resp A pointer to the message structure + * \return 0 for success, -1 for failure + * + */ +int nfapi_nr_stop_indication(nfapi_pnf_config_t* config, nfapi_nr_stop_indication_scf_t* resp); + /*! Send the MEASUREMENT.response * \param config A pointer to a pnf configuraiton * \param resp A pointer to the message structure diff --git a/nfapi/open-nFAPI/pnf/src/pnf_interface.c b/nfapi/open-nFAPI/pnf/src/pnf_interface.c index d80ab168fa..76db8ac2d5 100644 --- a/nfapi/open-nFAPI/pnf/src/pnf_interface.c +++ b/nfapi/open-nFAPI/pnf/src/pnf_interface.c @@ -387,6 +387,18 @@ int nfapi_pnf_stop_resp(nfapi_pnf_config_t* config, nfapi_stop_response_t* resp) return pnf_pack_and_send_p5_message(_this, &(resp->header), sizeof(nfapi_stop_response_t)); } +int nfapi_nr_stop_indication(nfapi_pnf_config_t* config, nfapi_nr_stop_indication_scf_t* resp) +{ + if (config == NULL || resp == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__); + return -1; + } + + pnf_t* _this = (pnf_t*)(config); + AssertFatal(config->send_p5_msg, "Function pointer must be configured|"); + return config->send_p5_msg(_this, &(resp->header), sizeof(nfapi_nr_stop_indication_scf_t)); +} + int nfapi_pnf_measurement_resp(nfapi_pnf_config_t* config, nfapi_measurement_response_t* resp) { if (config == NULL || resp == NULL) diff --git a/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h b/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h index db303b8b4a..3f326e841c 100644 --- a/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h +++ b/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h @@ -210,6 +210,24 @@ typedef struct nfapi_vnf_config int (*pnf_start_resp)(nfapi_vnf_config_t* config, int p5_idx, nfapi_pnf_start_response_t* resp); int (*pnf_nr_start_resp)(nfapi_vnf_config_t* config, int p5_idx, nfapi_nr_pnf_start_response_t* resp); + /*! A callback for the STOP.indication + * \param config A pointer to the vnf configuration + * \param p5_idx The p5 index used to indicate a particular pnf p5 connection + * \param ind A data structure for the decoded STOP.indication This will + * have been allocated on the stack. + * \return not currently used. + * + * The STOP.indication contains the result of the STOP.request + * + * + * ind may contain pointers to dynamically allocated sub structures + * such as the vendor_extention. The dynamically allocated structure will + * be deallocated on return. If the client wishes to 'keep' the structures + * then the substructure pointers should be set to 0 and then the client should + * use the codec_config.deallocate function to release it at a future point + */ + int (*nr_stop_ind)(nfapi_vnf_config_t* config, int p5_idx, nfapi_nr_stop_indication_scf_t* ind); + /*! A callback for the PNF_STOP.resp * \param config A pointer to the vnf configuration * \param p5_idx The p5 index used to indicate a particular pnf p5 connection @@ -640,6 +658,7 @@ int nfapi_nr_vnf_start_req(nfapi_vnf_config_t* config, int p5_idx, nfapi_nr_star * \return 0 means success, -1 failure */ int nfapi_vnf_stop_req(nfapi_vnf_config_t* config, int p5_idx, nfapi_stop_request_t* req); +int nfapi_nr_vnf_stop_req(nfapi_vnf_config_t* config, int p5_idx, nfapi_nr_stop_request_scf_t* req); /*! Send the MEASUREMENT.request * \param config A pointer to a vnf config diff --git a/nfapi/open-nFAPI/vnf/src/vnf.c b/nfapi/open-nFAPI/vnf/src/vnf.c index b61d5f26ae..84d4026188 100644 --- a/nfapi/open-nFAPI/vnf/src/vnf.c +++ b/nfapi/open-nFAPI/vnf/src/vnf.c @@ -665,6 +665,30 @@ void vnf_nr_handle_error_indication(void *pRecvMsg, int recvMsgLen, nfapi_vnf_co } } +void vnf_nr_handle_stop_indication(void* pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx) +{ + // ensure it's valid + if (pRecvMsg == NULL || config == NULL) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: NULL parameters\n", __FUNCTION__); + } else { + NFAPI_TRACE(NFAPI_TRACE_INFO, "Received STOP.indication\n"); + + nfapi_nr_stop_indication_scf_t msg; + + // unpack the message + if (config->unpack_func(pRecvMsg, recvMsgLen, &msg, sizeof(msg), &config->codec_config)) { + if (config->nr_stop_ind) { + (config->nr_stop_ind)(config, p5_idx, &msg); + } + } else { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Unpack message failed, ignoring\n", __FUNCTION__); + } + + // make sure to release any dynamic part of the message + if (msg.vendor_extension) + config->codec_config.deallocate(msg.vendor_extension); + } +} void vnf_handle_stop_response(void *pRecvMsg, int recvMsgLen, nfapi_vnf_config_t* config, int p5_idx) { @@ -1172,6 +1196,10 @@ void vnf_nr_handle_p4_p5_message(void *pRecvMsg, int recvMsgLen, int p5_idx, nfa vnf_handle_stop_response(pRecvMsg, recvMsgLen, config, p5_idx); break; + case NFAPI_NR_PHY_MSG_TYPE_STOP_INDICATION: + vnf_nr_handle_stop_indication(pRecvMsg, recvMsgLen, config, p5_idx); + break; + default: { if(messageHeader.message_id >= NFAPI_VENDOR_EXT_MSG_MIN && diff --git a/nfapi/open-nFAPI/vnf/src/vnf_interface.c b/nfapi/open-nFAPI/vnf/src/vnf_interface.c index 111d88e8dc..d421976cf1 100644 --- a/nfapi/open-nFAPI/vnf/src/vnf_interface.c +++ b/nfapi/open-nFAPI/vnf/src/vnf_interface.c @@ -665,6 +665,18 @@ int nfapi_vnf_stop_req(nfapi_vnf_config_t* config, int p5_idx, nfapi_stop_reques return vnf_pack_and_send_p5_message(_this, p5_idx, &req->header, sizeof(nfapi_stop_request_t)); } + +int nfapi_nr_vnf_stop_req(nfapi_vnf_config_t* config, int p5_idx, nfapi_nr_stop_request_scf_t* req) +{ + if (config == 0 || req == 0) + return -1; + + vnf_t* _this = (vnf_t*)(config); + AssertFatal(config->send_p5_msg, "Function pointer must be configured\n"); + NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Sending NFAPI STOP.request\n"); + return config->send_p5_msg(_this, p5_idx, &req->header, sizeof(nfapi_nr_stop_request_scf_t)); +} + int nfapi_vnf_measurement_req(nfapi_vnf_config_t* config, int p5_idx, nfapi_measurement_request_t* req) { if(config == 0 || req == 0) diff --git a/openair1/SCHED/nfapi_lte_dummy.c b/openair1/SCHED/nfapi_lte_dummy.c index 0028055c61..500f69ae86 100644 --- a/openair1/SCHED/nfapi_lte_dummy.c +++ b/openair1/SCHED/nfapi_lte_dummy.c @@ -130,3 +130,27 @@ 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() +{ +} + +void socket_nfapi_send_stop_request(vnf_t *vnf) +{ +} diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index dcd17efe22..ee318c6a85 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1354,6 +1354,18 @@ void config_pdcp(configmodule_interface_t *cfg, nr_pdcp_configuration_t *pdcp_co pdcp_config->drb.discard_timer = config_get_processedint(cfg, &pdcp_params[CONFIG_NR_PDCP_DRB_DISCARD_TIMER_IDX]); } +void nfapi_stop_l1() +{ + if (NFAPI_MODE && (NFAPI_MODE == NFAPI_MODE_AERIAL || NFAPI_MODE == NFAPI_MODE_VNF)) { + stop_nr_nfapi_vnf(); + } else if (NFAPI_MODE && NFAPI_MODE == NFAPI_MODE_PNF) { + // PNF should just wait for the STOP.request to come from the L2 + // If instead it was stopped first, in some cases it makes sense for it to terminate itself (WLS) + // Sends a STOP.indication to the VNF + stop_nr_nfapi_pnf(); + } +} + static void get_bwp_config(nr_mac_config_t *configuration) { char path[MAX_OPTNAME_SIZE * 2 + 8]; diff --git a/openair2/GNB_APP/gnb_config.h b/openair2/GNB_APP/gnb_config.h index 200400144b..96cf898ba0 100644 --- a/openair2/GNB_APP/gnb_config.h +++ b/openair2/GNB_APP/gnb_config.h @@ -64,7 +64,7 @@ f1ap_gnb_du_system_info_t *get_sys_info(NR_BCCH_BCH_Message_t *mib, const NR_BCC int gNB_app_handle_f1ap_gnb_cu_configuration_update(f1ap_gnb_cu_configuration_update_t *gnb_cu_cfg_update); MessageDef *RCconfig_NR_CU_E1(const E1_t *entity); ngran_node_t get_node_type(void); - +void nfapi_stop_l1(); #ifdef E2_AGENT #include "openair2/E2AP/e2_agent_arg.h" e2_agent_args_t RCconfig_NR_E2agent(void); From c83bd8745943842331fa4374808b49d313f6f0e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Soares=20Silva?= Date: Fri, 1 Aug 2025 13:19:04 +0100 Subject: [PATCH 044/275] Fixup WLS documentation and Makefile patch --- cmake_targets/tools/install_wls_lib.patch | 61 ++++++++++++----------- doc/nfapi.md | 9 +++- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/cmake_targets/tools/install_wls_lib.patch b/cmake_targets/tools/install_wls_lib.patch index 5050b79a4e..c551f842b5 100644 --- a/cmake_targets/tools/install_wls_lib.patch +++ b/cmake_targets/tools/install_wls_lib.patch @@ -1,5 +1,5 @@ diff --git a/wls_lib/Makefile b/wls_lib/Makefile -index 2f8e4b2..e657488 100644 +index 2f8e4b2..7b99a26 100644 --- a/wls_lib/Makefile +++ b/wls_lib/Makefile @@ -1,20 +1,6 @@ @@ -23,7 +23,7 @@ index 2f8e4b2..e657488 100644 +################################################################ +# +################################################################ - + MYCUSTOMTAB=' ' MYCUSTOMSPACE='============================================================================================' @@ -24,11 +10,17 @@ MYCUSTOMSPACE1='------------------------------------------------------------' @@ -48,9 +48,12 @@ index 2f8e4b2..e657488 100644 else ifeq ($(WIRELESS_SDK_TOOLCHAIN),icx) CC := icx CPP := icx -@@ -44,9 +36,9 @@ ifeq ($(WIRELESS_SDK_TARGET_ISA),sse) +@@ -42,11 +34,11 @@ endif + ifeq ($(WIRELESS_SDK_TARGET_ISA),sse) + TARGET_PROCESSOR := -xSSE4.2 else ifeq ($(WIRELESS_SDK_TARGET_ISA),avx2) - TARGET_PROCESSOR := -xCORE-AVX2 +- TARGET_PROCESSOR := -xCORE-AVX2 ++ TARGET_PROCESSOR := -march=core-avx2 else ifeq ($(WIRELESS_SDK_TARGET_ISA),avx512) - TARGET_PROCESSOR := -xCORE-AVX512 + TARGET_PROCESSOR := -march=skylake-avx512 @@ -63,7 +66,7 @@ index 2f8e4b2..e657488 100644 @@ -66,15 +58,38 @@ PROJECT_DIR := ./ BUILDDIR := make PROJECT_BINARY := $(PROJECT_NAME).so - + +PKGCONF ?= pkg-config +LIBDPDK_VERSION=20.11.3 + @@ -79,7 +82,7 @@ index 2f8e4b2..e657488 100644 +$(error "Please define RTE_SDK environment variable OR install DPDK version $(LIBDPDK_VERSION)") +endif endif - + -RTE_INC := $(shell PKG_CONFIG_PATH=/usr/lib64/pkgconfig:$(RTE_SDK)/build/meson-uninstalled pkgconf --cflags-only-I libdpdk) +# At least one of the options is defined + @@ -88,10 +91,10 @@ index 2f8e4b2..e657488 100644 +else ifeq ($(DPDK_INSTALLED),1) + RTE_INC := $(shell PKG_CONFIG_PATH=/usr/lib64/pkgconfig $(PKGCONF) --cflags-only-I libdpdk) +endif - + CC_SRC = wls_lib_dpdk.c \ syslib.c - + +CC_HDR = syslib.h \ + ttypes.h \ + wls.h \ @@ -104,74 +107,74 @@ index 2f8e4b2..e657488 100644 @@ -84,18 +99,19 @@ CC_FLAGS += -std=gnu11 -Wall -Wno-deprecated-declarations \ -Wimplicit-function-declaration \ -g -O3 -mcmodel=large $(TARGET_PROCESSOR) - + -INC := -I$(RTE_INC) +INC := -I$(RTE_INC) -I../intel_fapi/ DEF := - + AS_FLAGS := AR_FLAGS := rc - + PROJECT_OBJ_DIR := $(BUILDDIR)/obj +PROJECT_DEP_DIR := $(PROJECT_OBJ_DIR)/$(PROJECT_NAME).dep.d - + CC_OBJS := $(patsubst %.c,%.o,$(CC_SRC)) AS_OBJS := $(patsubst %.s,%.o,$(AS_SRC)) OBJS := $(CC_OBJS) $(AS_OBJS) $(LIBS) -DIRLIST := $(addprefix $(PROJECT_OBJ_DIR)/,$(sort $(dir $(OBJS)))) +DIRLIST := $(addprefix $(PROJECT_OBJ_DIR)/,$(sort $(dir $(OBJS)))) $(PROJECT_DEP_DIR) - + CC_OBJTARGETS := $(addprefix $(PROJECT_OBJ_DIR)/,$(CC_OBJS)) - + @@ -104,9 +120,7 @@ CC_FLAGS_FULL := $(CC_FLAGS) $(INC) $(DEF) - + AS_FLAGS := $(AS_FLAGS) $(INC) - + -PROJECT_DEP_FILE := $(PROJECT_OBJ_DIR)/$(PROJECT_NAME).dep - -ifeq ($(wildcard $(PROJECT_DEP_FILE)),$(PROJECT_DEP_FILE)) +ifeq ($(wildcard $(PROJECT_DEP_DIR)),$(PROJECT_DEP_DIR)) GENERATE_DEPS := else - + @@ -119,11 +133,11 @@ all : welcome_line $(PROJECT_BINARY) - + .PHONY : clear_dep clear_dep: - @$(RM) $(PROJECT_DEP_FILE) - @echo [DEP] $(subst $(PROJECT_OBJ_DIR)/,,$(PROJECT_DEP_FILE)) + @echo [DEP] rm $(PROJECT_DEP_DIR)/$(@F).dep + @$(RM) $(PROJECT_DEP_DIR)/$(@F).dep - + -$(CC_DEPS) : - @$(CC) -MM $(subst __up__,../,$(subst __dep__,,$@)) -MT $(PROJECT_OBJ_DIR)/$(patsubst %.c,%.o,$(subst __up__,../,$(subst __dep__,,$@))) $(CC_FLAGS_FULL) >> $(PROJECT_DEP_FILE) +$(CC_DEPS) : $(PROJECT_DEP_DIR) + @$(CC) $(CC_FLAGS_FULL) -MM $(subst __up__,../,$(subst __dep__,,$@)) -MT $(PROJECT_OBJ_DIR)/$(patsubst %.c,%.o,$(subst __up__,../,$(subst __dep__,,$@))) >> $(PROJECT_DEP_DIR)/$(@F).dep - + .PHONY : generate_deps generate_deps : clear_dep $(CC_DEPS) @@ -136,17 +150,17 @@ echo_start_build : $(DIRLIST) : -@$(MD) $@ - + -$(CC_OBJTARGETS) : +$(CC_OBJTARGETS) : $(GENERATE_DEPS) @echo [CC] $(subst $(PROJECT_OBJ_DIR)/,,$@) @$(CC) -c $(CC_FLAGS_FULL) -o"$@" $(patsubst %.o,%.c,$(subst $(PROJECT_OBJ_DIR)/,,$@)) - + -$(AS_OBJTARGETS) : +$(AS_OBJTARGETS) : $(CC_OBJTARGETS) $(CPP_OBJTARGETS) @echo [AS] $(subst $(PROJECT_OBJ_DIR)/,,$@) @$(AS) $(AS_FLAGS) -o"$@" $(patsubst %.o,%.s,$(subst $(PROJECT_OBJ_DIR)/,,$@)) - + -ifeq ($(wildcard $(PROJECT_DEP_FILE)),$(PROJECT_DEP_FILE)) +ifeq ($(wildcard $(PROJECT_DEP_DIR)),$(PROJECT_DEP_DIR)) - + -include $(PROJECT_DEP_FILE) +include $(PROJECT_DEP_DIR)/* - + endif - + @@ -156,7 +170,7 @@ clean: @$(RM) $(CC_OBJTARGETS) $(AS_OBJTARGETS) ifneq ($(wildcard $(PROJECT_DIR)/$(PROJECT_MAKE)),) @@ -179,17 +182,17 @@ index 2f8e4b2..e657488 100644 - @$(RM) $(PROJECT_BINARY) $(PROJECT_BINARY_LIB) $(PROJECT_DEP_FILE) + @$(RM) -rf $(PROJECT_BINARY) $(PROJECT_BINARY_LIB) $(PROJECT_DEP_DIR) endif - + xclean: clean @@ -172,7 +186,24 @@ welcome_line : debug : all release : all - + -$(PROJECT_BINARY) : $(DIRLIST) echo_start_build $(GENERATE_DEPS) $(PRE_BUILD) $(CC_OBJTARGETS) $(AS_OBJTARGETS) +$(PROJECT_BINARY) : $(DIRLIST) echo_start_build $(GENERATE_DEPS) $(CC_OBJTARGETS) $(AS_OBJTARGETS) @echo [AR] $(subst $(BUILDDIR)/,,$@) @$(CC) $(CC_OBJTARGETS) $(AS_OBJTARGETS) -shared -fPIC -o $@ - + +############################################################## +# Install configuration +############################################################## diff --git a/doc/nfapi.md b/doc/nfapi.md index 214aa2799f..c372de6744 100644 --- a/doc/nfapi.md +++ b/doc/nfapi.md @@ -194,14 +194,19 @@ Clone the Radisys repository (We're currently using the `oai_integration` branch git clone https://gerrit.o-ran-sc.org/r/o-du/l2 -b oai-integration -Build the O-DU +Build the O-DU (More details can be found in O-RAN's documentation page [here](https://docs.o-ran-sc.org/projects/o-ran-sc-o-du-l2/en/latest/user-guide.html#i-execution-on-locally-compiling-o-du-high-source-code)) cd ~/l2/build/odu make clean_all;make odu PHY=INTEL_L1 MACHINE=BIT64 MODE=TDD;make cu_stub NODE=TEST_STUB MACHINE=BIT64 MODE=TDD;make ric_stub NODE=TEST_STUB MACHINE=BIT64 MODE=TDD +The O-DU requires some virtual interfaces to be setup in order to enable communication between the O-DU, CU_stub and RIC_stub Setup local interfaces for the cu_stub, ric_stub and o_du +The IP addresses shown match the ones specified in OSC ODU configuration file, found in /build/config/tdd_odu_config.xml +The parameters referenced are , and - sudo ifconfig enp7s0:ODU "192.168.130.81" && sudo ifconfig enp7s0:CU_STUB "192.168.130.82" && sudo ifconfig enp7s0:RIC_STUB "192.168.130.80" + sudo ifconfig :ODU "192.168.130.81" + sudo ifconfig :CU_STUB "192.168.130.82" + sudo ifconfig :RIC_STUB "192.168.130.80" Run cu_stu and ric_stub in separate terminals From 09d9bb5b6aa0b50d3b4ef49ee2734a4f3d4c28af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Soares=20Silva?= Date: Fri, 1 Aug 2025 17:15:17 +0100 Subject: [PATCH 045/275] Fix build for simulators This change is needed due to the previous addition of the function nfapi_stop_l1() in gnb_config.c which in turn calls 2 functions that do not exist in the simulators, since they're not linked against the nfapi libraries --- openair1/SIMULATION/NR_PHY/nr_unitary_defs.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openair1/SIMULATION/NR_PHY/nr_unitary_defs.h b/openair1/SIMULATION/NR_PHY/nr_unitary_defs.h index 74fbf1051b..a84e57aa7f 100644 --- a/openair1/SIMULATION/NR_PHY/nr_unitary_defs.h +++ b/openair1/SIMULATION/NR_PHY/nr_unitary_defs.h @@ -79,6 +79,16 @@ int oai_nfapi_ul_config_req(nfapi_ul_config_request_t *ul_config_req) return (0); } +void stop_nr_nfapi_vnf() +{ + abort(); +} + +void stop_nr_nfapi_pnf() +{ + abort(); +} + void fill_scc_sim(NR_ServingCellConfigCommon_t *scc, uint64_t *ssb_bitmap, int N_RB_DL, int N_RB_UL, int mu_dl, int mu_ul); void fix_scc(NR_ServingCellConfigCommon_t *scc, uint64_t ssbmap); void prepare_scc(NR_ServingCellConfigCommon_t *scc); From 83fed82bcf2537ad4002336016350c081356e27a Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Thu, 23 Oct 2025 17:27:18 +0200 Subject: [PATCH 046/275] fix direct bugs of not used RU_RX_SLOT_DEPTH defined constant with this commit, the gNB can work if we change RU_RX_SLOT_DEPTH to another value than 4 but it remain not well designed --- executables/nr-gnb.c | 2 +- openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c | 2 +- openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c | 2 +- openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 2 +- openair1/PHY/NR_TRANSPORT/pucch_rx.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 505adf825c..30f14bde24 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -190,7 +190,7 @@ static void rx_func(processingData_L1_t *info) //WA: comment rotation in tx/rx if (gNB->phase_comp) { //apply the rx signal rotation here - int soffset = (slot_rx & 3) * gNB->frame_parms.symbols_per_slot * gNB->frame_parms.ofdm_symbol_size; + int soffset = (slot_rx % RU_RX_SLOT_DEPTH) * gNB->frame_parms.symbols_per_slot * gNB->frame_parms.ofdm_symbol_size; for (int bb = 0; bb < gNB->common_vars.num_beams_period; bb++) { for (int aa = 0; aa < gNB->frame_parms.nb_antennas_rx; aa++) { apply_nr_rotation_RX(&gNB->frame_parms, diff --git a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c index 2e6e4a54a6..94a417501c 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c +++ b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c @@ -136,7 +136,7 @@ void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_sy // TODO modify the measurements to take into account concurrent beams for (int s = first_symb; s < first_symb + num_symb; s++) { - int offset0 = ((slot&3)*frame_parms->symbols_per_slot + s) * frame_parms->ofdm_symbol_size; + 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 !(I0_SKIP_DC && rb == frame_parms->N_RB_UL >> 1)) { // skip middle PRB because of artificial noise possibly created by FFT diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 93d7feebe0..d73a315354 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -111,7 +111,7 @@ static void nr_pusch_antenna_processing(void *arg) c16_t **ul_ch_estimates = (c16_t **)pusch_vars->ul_ch_estimates; NR_DL_FRAME_PARMS *frame_parms = rdata->frame_parms; const int symbolSize = frame_parms->ofdm_symbol_size; - const int slot_offset = (Ns & 3) * frame_parms->symbols_per_slot * symbolSize; + const int slot_offset = (Ns % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * symbolSize; const int delta = get_delta(p, pusch_pdu->dmrs_config_type); const int symbol_offset = symbolSize * symbol; const int k0 = bwp_start_subcarrier; diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 28f3e2cc67..527063d29d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -1212,7 +1212,7 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, int end_sc = (start_sc + rel15_ul->rb_size * NR_NB_SC_PER_RB - 1) % frame_parms->ofdm_symbol_size; for (int s = rel15_ul->start_symbol_index; s < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); s++) { - int offset0 = ((slot & 3) * frame_parms->symbols_per_slot + s) * frame_parms->ofdm_symbol_size; + int offset0 = ((slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot + s) * frame_parms->ofdm_symbol_size; int offset = offset0 + (frame_parms->first_carrier_offset + start_sc) % frame_parms->ofdm_symbol_size; c16_t *ul_ch = &gNB->common_vars.rxdataF[beam_nb][aarx][offset]; if (end_sc < start_sc) { diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index ddf16a0541..2a3987e755 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -161,7 +161,7 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB, nfapi_nr_pucch_pdu_t *pucch_pdu) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - int soffset = (slot & 3) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size; + int soffset = (slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size; AssertFatal(pucch_pdu->bit_len_harq > 0 || pucch_pdu->sr_flag > 0, "Either bit_len_harq (%d) or sr_flag (%d) must be > 0\n", From 2d7999cb28930912ebf6fa4e579682c96b9c9f89 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 8 Oct 2025 10:44:27 +0200 Subject: [PATCH 047/275] fix order of information when encoding/decoding RSRP meaas report with more than 1 resource --- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 26 ++++++++----------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 14 +++++----- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 28e7b7d42b..363ce5d098 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -2936,13 +2936,13 @@ static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, } else nb_meas = 2; - struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList SSB_resource; + struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList *SSB_resource = NULL; for (int csi_ssb_idx = 0; csi_ssb_idx < csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.count; csi_ssb_idx++) { if (csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceSetId == *(csi_resourceconfig->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->csi_SSB_ResourceSetList->list.array[0])){ - SSB_resource = csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList; + SSB_resource = &csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList; ///only one SSB resource set from spec 38.331 IE CSI-ResourceConfig - nb_ssb = SSB_resource.list.count; + nb_ssb = SSB_resource->list.count; break; } } @@ -2957,7 +2957,7 @@ static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, for (int measured_ssb_idx = 0; measured_ssb_idx < MAX_NB_SSB; measured_ssb_idx++) { // searching for the SSB index in the SSB resource table for (int ssb_resource = 0; ssb_resource < nb_ssb; ssb_resource++) { - if (*SSB_resource.list.array[ssb_resource] == measured_ssb_idx) { + if (*SSB_resource->list.array[ssb_resource] == measured_ssb_idx) { sorted_rsrp_measurements[sorted_idx].ssb_index = ssb_resource; sorted_rsrp_measurements[sorted_idx].ssb_rsrp_dBm = mac->ssb_measurements[measured_ssb_idx].ssb_rsrp_dBm; sorted_rsrp_measurements[sorted_idx].ssb_sinr_dB = mac->ssb_measurements[measured_ssb_idx].ssb_sinr_dB; @@ -2968,12 +2968,12 @@ static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, } qsort(sorted_rsrp_measurements, nb_ssb, sizeof(NR_RSRP_meas_t), compare_ssb_rsrp); - uint32_t ssbi; - - if (ssbri_bits > 0) { - ssbi = sorted_rsrp_measurements[0].ssb_index; - temp_payload = reverse_bits(ssbi, ssbri_bits); - bits += ssbri_bits; + for (int i = 0; i < nb_meas; i++) { + if (ssbri_bits > 0) { + uint32_t ssbi = sorted_rsrp_measurements[i].ssb_index; + temp_payload |= (reverse_bits(ssbi, ssbri_bits) << bits); + bits += ssbri_bits; + } } uint8_t rsrp_idx = get_rsrp_index(sorted_rsrp_measurements[0].ssb_rsrp_dBm); @@ -2982,15 +2982,11 @@ static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, // from the second SSB, differential report for (int i = 1; i < nb_meas; i++) { - ssbi = sorted_rsrp_measurements[i].ssb_index; - temp_payload |= (reverse_bits(ssbi, ssbri_bits) << bits); - bits += ssbri_bits; - rsrp_idx = get_rsrp_diff_index(sorted_rsrp_measurements[0].ssb_rsrp_dBm,sorted_rsrp_measurements[i].ssb_rsrp_dBm); temp_payload |= (reverse_bits(rsrp_idx, 4) << bits); bits += 4; // 4 bits for subsequent RSRP } - break; // resorce found + break; // resource found } } int max_bits = sizeof(((csi_payload_t *)0)->part1_payload) * 8; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index f3de970683..e689a8a978 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -589,11 +589,13 @@ static void evaluate_rsrp_report(gNB_MAC_INST *nrmac, rsrp_report->nr_reports = csi_report->CSI_report_bitlen.nb_ssbri_cri; int bitlen = csi_report->CSI_report_bitlen.cri_ssbri_bitlen; - uint8_t curr_payload = pickandreverse_bits(payload, bitlen, *cumul_bits); - rsrp_report->resource_id[0] = *(index_list[bitlen > 0 ? ((curr_payload) & ~(~1U << (bitlen - 1))) : bitlen]); - *cumul_bits += bitlen; + for (int i = 0; i < rsrp_report->nr_reports; i++) { + uint8_t idx_payload = pickandreverse_bits(payload, bitlen, *cumul_bits); + rsrp_report->resource_id[i] = *(index_list[bitlen > 0 ? ((idx_payload) & ~(~1U << (bitlen - 1))) : bitlen]); + *cumul_bits += bitlen; + } - curr_payload = pickandreverse_bits(payload, 7, *cumul_bits); + uint8_t curr_payload = pickandreverse_bits(payload, 7, *cumul_bits); int rsrp = curr_payload & 0x7f; *cumul_bits += 7; csi_report->nb_of_csi_ssb_report++; @@ -605,10 +607,6 @@ static void evaluate_rsrp_report(gNB_MAC_INST *nrmac, } for (int i = 1; i < rsrp_report->nr_reports; i++) { - curr_payload = pickandreverse_bits(payload, bitlen, *cumul_bits); - rsrp_report->resource_id[i] = *(index_list[bitlen > 0 ? ((curr_payload) & ~(~1U << (bitlen - 1))) : bitlen]); - *cumul_bits += bitlen; - curr_payload = pickandreverse_bits(payload, 4, *cumul_bits); csi_report->nb_of_csi_ssb_report++; rsrp_report->RSRP[i] = get_diff_rsrp(curr_payload & 0x0f, rsrp_report->RSRP[0]); From 747689596aa4e9c137c7a3bac02f9ae981bd2aad Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 21 Oct 2025 16:16:36 +0200 Subject: [PATCH 048/275] fix SINR report order at UE --- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 25 ++++++++------------ 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 363ce5d098..aeb15fe864 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -2795,13 +2795,13 @@ static csi_payload_t get_ssb_sinr_payload(NR_UE_MAC_INST_t *mac, } else nb_meas = 2; - struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList SSB_resource; + struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList *SSB_resource = NULL; for (int csi_ssb_idx = 0; csi_ssb_idx < csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.count; csi_ssb_idx++) { if (csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceSetId == *(csi_resourceconfig->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->csi_SSB_ResourceSetList->list.array[0])) { - SSB_resource = csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList; + SSB_resource = &csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList; /// only one SSB resource set from spec 38.331 IE CSI-ResourceConfig - nb_ssb = SSB_resource.list.count; + nb_ssb = SSB_resource->list.count; break; } } @@ -2816,7 +2816,7 @@ static csi_payload_t get_ssb_sinr_payload(NR_UE_MAC_INST_t *mac, for (int measured_ssb_idx = 0; measured_ssb_idx < MAX_NB_SSB; measured_ssb_idx++) { // searching for the SSB index in the SSB resource table for (int ssb_resource = 0; ssb_resource < nb_ssb; ssb_resource++) { - if (*SSB_resource.list.array[ssb_resource] == measured_ssb_idx) { + if (*SSB_resource->list.array[ssb_resource] == measured_ssb_idx) { sorted_sinr_measurements[sorted_idx].ssb_index = ssb_resource; sorted_sinr_measurements[sorted_idx].ssb_rsrp_dBm = mac->ssb_measurements[measured_ssb_idx].ssb_rsrp_dBm; sorted_sinr_measurements[sorted_idx].ssb_sinr_dB = mac->ssb_measurements[measured_ssb_idx].ssb_sinr_dB; @@ -2827,25 +2827,20 @@ static csi_payload_t get_ssb_sinr_payload(NR_UE_MAC_INST_t *mac, } qsort(sorted_sinr_measurements, nb_ssb, sizeof(NR_RSRP_meas_t), compare_ssb_sinr); - uint8_t ssbi; - // TS38.212 v16.5.0: Table 6.3.1.1.2-8A - if (ssbri_bits > 0) { - ssbi = sorted_sinr_measurements[0].ssb_index; - temp_payload = reverse_bits(ssbi, ssbri_bits); - bits += ssbri_bits; + for (int i = 0; i < nb_meas; i++) { + if (ssbri_bits > 0) { + uint8_t ssbi = sorted_sinr_measurements[i].ssb_index; + temp_payload |= (reverse_bits(ssbi, ssbri_bits) << bits); + bits += ssbri_bits; + } } uint8_t sinr_idx = get_sinr_index(sorted_sinr_measurements[0].ssb_sinr_dB); temp_payload |= (reverse_bits(sinr_idx, 7) << bits); bits += 7; // 7 bits for highest SINR - // from the second SSB, differential report for (int i = 1; i < nb_meas; i++) { - ssbi = sorted_sinr_measurements[i].ssb_index; - temp_payload |= (reverse_bits(ssbi, ssbri_bits) << bits); - bits += ssbri_bits; - sinr_idx = get_sinr_diff_index(sorted_sinr_measurements[0].ssb_sinr_dB, sorted_sinr_measurements[i].ssb_sinr_dB); temp_payload |= (reverse_bits(sinr_idx, 4) << bits); bits += 4; // 4 bits for differential SINR From 350263460b6a9bd47acc6a75beb83b6b112a595d Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 24 Apr 2025 10:09:56 +0200 Subject: [PATCH 049/275] Refactor processing of sdap_Config in add_drb (PDCP) * use get_sdap_Config to process sdap_Config * move SDAP config logic to the scope where it is relevant * add SDAP entity direcly in RRC, not from PDCP * replaced nr_pdcp_add_drbs with single add_drb call: it was no longer efficient to keep it in the new logic. The addMod list can be looped directly in RRC. * add specific function to add bearers from addMod list in E1, NSA, and UE --- doc/SW_archi.md | 2 +- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 25 +++- openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c | 125 ++++++-------------- openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h | 11 +- openair2/RRC/NR/rrc_gNB_nsa.c | 27 ++++- openair2/RRC/NR_UE/rrc_UE.c | 30 ++++- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 2 +- 7 files changed, 111 insertions(+), 111 deletions(-) diff --git a/doc/SW_archi.md b/doc/SW_archi.md index 9c3736ac2a..5b902a6675 100644 --- a/doc/SW_archi.md +++ b/doc/SW_archi.md @@ -325,7 +325,7 @@ The PDCP implementation is secured by a general mutex, akin to the design of the Initialization of the PDCP layer follows a structure similar to that of the RLC layer. The function `nr_pdcp_layer_init()` initializes PDCP, while a second initialization function, `pdcp_module_init()`, must also be invoked. -To manage UE connections, `nr_pdcp_add_srbs()` is employed for adding UE SRBs in PDCP, while `nr_pdcp_remove_UE()` is used for their removal. Similarly, `nr_pdcp_add_drbs()` adds UE DRBs in PDCP, with `nr_pdcp_remove_UE()` handling their removal. +To manage UE connections, `nr_pdcp_add_srbs()` is employed for adding UE SRBs in PDCP, while `nr_pdcp_remove_UE()` is used for their removal. Similarly, `add_drb()` adds UE DRB in PDCP, with `nr_pdcp_remove_UE()` handling their removal. ## PDCP Tx flow diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index b19ccceb22..f29c26842b 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -141,6 +141,24 @@ static instance_t get_f1_gtp_instance(void) return inst->gtpInst; } +/** @brief Add bearer in PDCP/SDAP for 5GC association (SA) */ +static void e1_add_bearers(const int ue_id, const NR_DRB_ToAddModList_t *addMod, const nr_pdcp_entity_security_keys_and_algos_t *sp) +{ + for (int i = 0; i < addMod->list.count; i++) { + NR_DRB_ToAddMod_t *drb = addMod->list.array[i]; + DevAssert(drb->cnAssociation); + DevAssert(drb->cnAssociation->present != NR_DRB_ToAddMod__cnAssociation_PR_NOTHING); + DevAssert(drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config); + DevAssert(drb->cnAssociation->choice.sdap_Config); + // get SDAP config + sdap_config_t sdap = nr_sdap_get_config(GNB_FLAG_YES, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity); + // add SDAP entity + new_nr_sdap_entity(GNB_FLAG_YES, ue_id, sdap); + // add PDCP entity + add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp); + } +} + void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) { bool need_ue_id_mgmt = e1_used(); @@ -208,11 +226,10 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) security_parameters.integrity_algorithm = req->secInfo.integrityProtectionAlgorithm; memcpy(security_parameters.ciphering_key, req->secInfo.encryptionKey, NR_K_KEY_SIZE); memcpy(security_parameters.integrity_key, req->secInfo.integrityProtectionKey, NR_K_KEY_SIZE); - nr_pdcp_add_drbs(true, // set this to notify PDCP that his not UE - cu_up_ue_id, - &DRB_configList, - &security_parameters); + + e1_add_bearers(cu_up_ue_id, &DRB_configList, &security_parameters); ASN_STRUCT_RESET(asn_DEF_NR_DRB_ToAddModList, &DRB_configList.list); + if (f1inst >= 0) { /* we have F1(-U) */ teid_t dummy_teid = 0xffff; // we will update later with answer from DU in_addr_t dummy_address = {0}; // IPv4, updated later with answer from DU diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index ce885b6b33..6bd2d9354f 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -576,74 +576,31 @@ void add_srb(int is_gnb, } void add_drb(int is_gnb, - ue_id_t UEid, - struct NR_DRB_ToAddMod *s, + const ue_id_t UEid, + const NR_PDCP_Config_t *pdcp, + sdap_config_t *sdap, const nr_pdcp_entity_security_keys_and_algos_t *security_parameters) { - nr_pdcp_entity_t *pdcp_drb; nr_pdcp_ue_t *ue; - int drb_id = s->drb_Identity; - if (!s->pdcp_Config || !s->pdcp_Config->drb) { - LOG_E(PDCP, "DRB field mandatory present at setup. DRB %d not configured.\n", drb_id); + if (!pdcp->drb) { + LOG_E(PDCP, "Missing PDCP config. DRB %d not configured.\n", sdap->drb_id); return; } - struct NR_PDCP_Config__drb *drb = s->pdcp_Config->drb; + struct NR_PDCP_Config__drb *drb = pdcp->drb; int sn_size_ul = decode_sn_size_ul(*drb->pdcp_SN_SizeUL); int sn_size_dl = decode_sn_size_dl(*drb->pdcp_SN_SizeDL); int discard_timer = decode_discard_timer(*drb->discardTimer); - int has_integrity; - int has_ciphering; - /* if pdcp_Config->t_Reordering is not present, it means infinity (-1) */ int t_reordering = -1; - if (s->pdcp_Config->t_Reordering != NULL) { - t_reordering = decode_t_reordering(*s->pdcp_Config->t_Reordering); + if (pdcp->t_Reordering != NULL) { + t_reordering = decode_t_reordering(*pdcp->t_Reordering); } - if (drb->integrityProtection != NULL) - has_integrity = 1; - else - has_integrity = 0; - - if (s->pdcp_Config->ext1 != NULL && s->pdcp_Config->ext1->cipheringDisabled != NULL) - has_ciphering = 0; - else - has_ciphering = 1; - - if ((!s->cnAssociation) || s->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_NOTHING) { - LOG_E(PDCP, "fatal, cnAssociation is missing or present is NR_DRB_ToAddMod__cnAssociation_PR_NOTHING\n"); - exit(-1); - } - - int pdusession_id; - bool has_sdap_rx = false; - bool has_sdap_tx = false; - bool is_sdap_DefaultDRB = false; - NR_QFI_t *mappedQFIs2Add = NULL; - uint8_t mappedQFIs2AddCount=0; - if (s->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity) - pdusession_id = s->cnAssociation->choice.eps_BearerIdentity; - else { - if (!s->cnAssociation->choice.sdap_Config) { - LOG_E(PDCP,"fatal, sdap_Config is null"); - exit(-1); - } - pdusession_id = s->cnAssociation->choice.sdap_Config->pdu_Session; - has_sdap_rx = is_sdap_rx(is_gnb, s->cnAssociation->choice.sdap_Config); - has_sdap_tx = is_sdap_tx(is_gnb, s->cnAssociation->choice.sdap_Config); - is_sdap_DefaultDRB = s->cnAssociation->choice.sdap_Config->defaultDRB == true ? 1 : 0; - mappedQFIs2Add = (NR_QFI_t*)s->cnAssociation->choice.sdap_Config->mappedQoS_FlowsToAdd->list.array[0]; - mappedQFIs2AddCount = s->cnAssociation->choice.sdap_Config->mappedQoS_FlowsToAdd->list.count; - LOG_D(SDAP, "Captured mappedQoS_FlowsToAdd from RRC: %ld \n", *mappedQFIs2Add); - } - /* TODO(?): accept different UL and DL SN sizes? */ - if (sn_size_ul != sn_size_dl) { - LOG_E(PDCP, "fatal, bad SN sizes, must be same. ul=%d, dl=%d\n", sn_size_ul, sn_size_dl); - exit(1); - } + int has_integrity = (drb->integrityProtection != NULL) ? 1 : 0; + int has_ciphering = (pdcp->ext1 != NULL && pdcp->ext1->cipheringDisabled != NULL) ? 0 : 1; /* get actual ciphering and integrity algorithm based on pdcp_Config */ nr_pdcp_entity_security_keys_and_algos_t actual_security_parameters = *security_parameters; @@ -652,29 +609,33 @@ void add_drb(int is_gnb, nr_pdcp_manager_lock(nr_pdcp_ue_manager); ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, UEid); - if (nr_pdcp_get_rb(ue, drb_id, false) != NULL) { - LOG_W(PDCP, "warning DRB %d already exist for UE ID %ld, do nothing\n", drb_id, UEid); + if (nr_pdcp_get_rb(ue, sdap->drb_id, false) != NULL) { + LOG_W(PDCP, "warning DRB %d already exist for UE ID %ld, do nothing\n", sdap->drb_id, UEid); } else { - pdcp_drb = new_nr_pdcp_entity(NR_PDCP_DRB_AM, is_gnb, drb_id, pdusession_id, - has_sdap_rx, has_sdap_tx, deliver_sdu_drb, ue, - is_gnb ? - deliver_pdu_drb_gnb : deliver_pdu_drb_ue, - ue, - sn_size_dl, t_reordering, discard_timer, - &actual_security_parameters); - nr_pdcp_ue_add_drb_pdcp_entity(ue, drb_id, pdcp_drb); + // code assumption: same SN size for both DL and UL + if (sn_size_ul != sn_size_dl) { + LOG_E(PDCP, "fatal, bad SN sizes, must be same. ul=%d, dl=%d\n", sn_size_ul, sn_size_dl); + exit(1); + } - LOG_I(PDCP, "added drb %d to UE ID %ld\n", drb_id, UEid); - /* add new SDAP entity for the PDU session the DRB belongs to */ - new_nr_sdap_entity(is_gnb, - has_sdap_rx, - has_sdap_tx, - UEid, - pdusession_id, - is_sdap_DefaultDRB, - drb_id, - mappedQFIs2Add, - mappedQFIs2AddCount); + // add PDCP entity + nr_pdcp_entity_t *pdcp_drb = new_nr_pdcp_entity(NR_PDCP_DRB_AM, + is_gnb, + sdap->drb_id, + sdap->pdusession_id, + sdap->sdap_rx, + sdap->sdap_tx, + deliver_sdu_drb, + ue, + is_gnb ? deliver_pdu_drb_gnb : deliver_pdu_drb_ue, + ue, + sn_size_dl, + t_reordering, + discard_timer, + &actual_security_parameters); + nr_pdcp_ue_add_drb_pdcp_entity(ue, sdap->drb_id, pdcp_drb); + + LOG_I(PDCP, "Added DRB %d to UE ID %ld\n", sdap->drb_id, UEid); } nr_pdcp_manager_unlock(nr_pdcp_ue_manager); } @@ -692,22 +653,6 @@ void nr_pdcp_add_srbs(eNB_flag_t enb_flag, LOG_W(PDCP, "nr_pdcp_add_srbs() with void list\n"); } -void nr_pdcp_add_drbs(eNB_flag_t enb_flag, - ue_id_t UEid, - NR_DRB_ToAddModList_t *const drb2add_list, - const nr_pdcp_entity_security_keys_and_algos_t *security_parameters) -{ - if (drb2add_list != NULL) { - for (int i = 0; i < drb2add_list->list.count; i++) { - add_drb(enb_flag, - UEid, - drb2add_list->list.array[i], - security_parameters); - } - } else - LOG_W(PDCP, "nr_pdcp_add_drbs() with void list\n"); -} - uint64_t get_pdcp_optmask(void) { return pdcp_optmask; diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h index 375bd27458..75f41e2190 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h @@ -33,6 +33,7 @@ #include "nr_pdcp_ue_manager.h" struct NR_DRB_ToAddMod; struct NR_SRB_ToAddMod; +struct sdap_configuration_s; void nr_pdcp_layer_init(void); @@ -42,19 +43,15 @@ bool nr_pdcp_data_ind(const protocol_ctxt_t *const ctxt_pP, const sdu_size_t sdu_buffer_size, uint8_t *const sdu_buffer); -void nr_pdcp_add_drbs(eNB_flag_t enb_flag, - ue_id_t UEid, - NR_DRB_ToAddModList_t *const drb2add_list, - const nr_pdcp_entity_security_keys_and_algos_t *security_parameters); - void nr_pdcp_add_srbs(eNB_flag_t enb_flag, ue_id_t UEid, NR_SRB_ToAddModList_t *const srb2add_list, const nr_pdcp_entity_security_keys_and_algos_t *security_parameters); void add_drb(int is_gnb, - ue_id_t UEid, - struct NR_DRB_ToAddMod *s, + const ue_id_t UEid, + const NR_PDCP_Config_t *pdcp, + struct sdap_configuration_s *sdap, const nr_pdcp_entity_security_keys_and_algos_t *security_parameters); void nr_pdcp_remove_UE(ue_id_t ue_id); diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index f0f7201517..b6bf34729c 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -161,6 +161,28 @@ static NR_RadioBearerConfig_t *get_default_rbconfig(int eps_bearer_id, return rbconfig; } +static void rrc_nsa_add_drb(const int ue_id, + const NR_DRB_ToAddModList_t *addMod, + const nr_pdcp_entity_security_keys_and_algos_t *sp) +{ + for (int i = 0; i < addMod->list.count; i++) { + NR_DRB_ToAddMod_t *drb = addMod->list.array[i]; + DevAssert(drb->cnAssociation); + DevAssert(drb->cnAssociation->present != NR_DRB_ToAddMod__cnAssociation_PR_NOTHING); + DevAssert(drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity); + // get SDAP config + sdap_config_t sdap = {0}; + // EPC association + sdap.pdusession_id = drb->cnAssociation->choice.eps_BearerIdentity; + sdap.drb_id = drb->drb_Identity; + sdap.defaultDRB = true; + // add SDAP entity (terminated at gNB, since it's EPC) + new_nr_sdap_entity(GNB_FLAG_YES, ue_id, sdap); + // add PDCP entity + add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp); + } +} + /* generate prototypes for the tree management functions (RB_INSERT used in rrc_add_nsa_user) */ RB_PROTOTYPE(rrc_nr_ue_tree_s, rrc_gNB_ue_context_s, entries, rrc_gNB_compare_ue_rnti_id); @@ -304,10 +326,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc, x2ap_ENDC_sgnb_addition_req_t *m, sctp_ } DevAssert(UE->rb_config != NULL); - nr_pdcp_add_drbs(GNB_FLAG_YES, - UE->rrc_ue_id, - UE->rb_config->drb_ToAddModList, - &security_parameters); + rrc_nsa_add_drb(UE->rrc_ue_id, UE->rb_config->drb_ToAddModList, &security_parameters); /* assumption: only a single bearer, see above */ NR_DRB_ToAddModList_t *rb_list = UE->rb_config->drb_ToAddModList; diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index d23dd14c58..81bd3b65bb 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -599,6 +599,31 @@ static void nr_rrc_process_dedicatedNAS_MessageList(NR_UE_RRC_INST_t *rrc, NR_RR } } +/** @brief Add bearer in PDCP/SDAP for 5GC association (SA) */ +static void rrc_ue_add_bearer(const int ue_id, const NR_DRB_ToAddMod_t *drb, const nr_pdcp_entity_security_keys_and_algos_t *sp) +{ + DevAssert(drb->cnAssociation); + DevAssert(drb->cnAssociation->present != NR_DRB_ToAddMod__cnAssociation_PR_NOTHING); + // get SDAP config + sdap_config_t sdap = {0}; + if (drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity) { + DevAssert(drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity); + + // EPC association + sdap.pdusession_id = drb->cnAssociation->choice.eps_BearerIdentity; + sdap.drb_id = drb->drb_Identity; + sdap.defaultDRB = true; + } else { + DevAssert(drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config); + DevAssert(drb->cnAssociation->choice.sdap_Config); + sdap = nr_sdap_get_config(GNB_FLAG_NO, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity); + } + // add SDAP entity + new_nr_sdap_entity(GNB_FLAG_NO, ue_id, sdap); + // add PDCP entity + add_drb(GNB_FLAG_NO, ue_id, drb->pdcp_Config, &sdap, sp); +} + /** * @brief add, modify and release SRBs and/or DRBs * @ref 3GPP TS 38.331 @@ -706,10 +731,7 @@ static void nr_rrc_ue_process_RadioBearerConfig(NR_UE_RRC_INST_t *ue_rrc, NR_Rad nr_reconfigure_sdap_entity(sdap_Config, ue_rrc->ue_id, sdap_Config->pdu_Session, DRB_id); } else { set_DRB_status(ue_rrc ,DRB_id, RB_ESTABLISHED); - add_drb(false, - ue_rrc->ue_id, - radioBearerConfig->drb_ToAddModList->list.array[cnt], - &security_up_parameters); + rrc_ue_add_bearer(ue_rrc->ue_id, radioBearerConfig->drb_ToAddModList->list.array[cnt], &security_up_parameters); } } } // drb_ToAddModList // diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index 3e3d95c3db..6ba76422fd 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -77,7 +77,7 @@ typedef struct qfi2drb_s { void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); -typedef struct { +typedef struct sdap_configuration_s { int pdusession_id; int drb_id; bool sdap_rx; From 68a3a849bca890b94a5d010f8bbfa7d398e3fefe Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 23 Apr 2025 23:47:48 +0200 Subject: [PATCH 050/275] refactor: Use centralized get_sdap_Config in nr_reconfigure_sdap_entity Replace inline parameter extraction with get_sdap_Config() function call to: - Eliminate code duplication in SDAP configuration handling - Streamline QFI release processing with structured config object - Ensure consistent SDAP configuration extraction across all SDAP operations --- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 4c02318e57..b5bb31933e 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -704,26 +704,19 @@ void nr_reconfigure_sdap_entity(NR_SDAP_Config_t *sdap_config, ue_id_t ue_id, in ue_id, pdusession_id); /* QFI to DRB mapping */ - NR_QFI_t *mappedQFIs2Add = (NR_QFI_t *)sdap_config->mappedQoS_FlowsToAdd->list.array[0]; - uint8_t mappedQFIs2AddCount = sdap_config->mappedQoS_FlowsToAdd->list.count; - bool has_sdap_rx = is_sdap_rx(is_gnb, sdap_config); - bool has_sdap_tx = is_sdap_tx(is_gnb, sdap_config); + sdap_config_t sdap = get_sdap_Config(is_gnb, sdap_config, drb_id); nr_sdap_ue_qfi2drb_config(sdap_entity, sdap_entity->default_drb, ue_id, - mappedQFIs2Add, - mappedQFIs2AddCount, + sdap.mappedQFIs2Add, + sdap.mappedQFIs2AddCount, drb_id, - has_sdap_rx, - has_sdap_tx); + sdap.sdap_rx, + sdap.sdap_tx); /* handle QFIs to DRB mapping rule to release */ - if (sdap_config->mappedQoS_FlowsToRelease) { - NR_QFI_t *mappedQFIs2release = (NR_QFI_t *)sdap_config->mappedQoS_FlowsToRelease->list.array[0]; - uint8_t mappedQFIs2RemoveCount = sdap_config->mappedQoS_FlowsToRelease->list.count; - for(int i = 0; i < mappedQFIs2RemoveCount; i++){ - uint8_t qfi = mappedQFIs2release[i]; - sdap_entity->qfi2drb_map_delete(sdap_entity, qfi); - } + for (int i = 0; i < sdap.mappedQFIs2ReleaseCount; i++) { + uint8_t qfi = sdap.mappedQFIs2Release[i]; + sdap_entity->qfi2drb_map_delete(sdap_entity, qfi); } } From 7b42a65ac7c5f29711f3bc29a2856cc53dcc0a20 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 24 Apr 2025 10:25:49 +0200 Subject: [PATCH 051/275] Refactor new_nr_sdap_entity to use sdap_config_t and always update mapping rules * change function signature: pass the sdap_config_t * update qfi2drb mapping when receiving a configuration for an already existing SDAP entity * always update mapping rules, not only for default DRB --- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 72 +++++++++++++------------- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 10 +--- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index b5bb31933e..9dcc309539 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -464,67 +464,65 @@ static void nr_sdap_ue_qfi2drb_config(nr_sdap_entity_t *existing_sdap_entity, * @param mapped_qfi_2_add, list of QoS flows to add/update * @param mappedQFIs2AddCount, number of QoS flows to add/update */ -nr_sdap_entity_t *new_nr_sdap_entity(int is_gnb, - bool has_sdap_rx, - bool has_sdap_tx, - ue_id_t ue_id, - int pdusession_id, - bool is_defaultDRB, - uint8_t drb_identity, - NR_QFI_t *mapped_qfi_2_add, - uint8_t mappedQFIs2AddCount) +nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap) { /* check whether the SDAP entity already exists and update QFI to DRB mapping rules in that case */ - if (nr_sdap_get_entity(ue_id, pdusession_id)) { - LOG_E(SDAP, "SDAP Entity for UE already exists with RNTI/UE ID: %lu and PDU SESSION ID: %d\n", ue_id, pdusession_id); - nr_sdap_entity_t *existing_sdap_entity = nr_sdap_get_entity(ue_id, pdusession_id); - rb_id_t pdcp_entity = existing_sdap_entity->default_drb; - if(!is_gnb) - nr_sdap_ue_qfi2drb_config(existing_sdap_entity, - pdcp_entity, + nr_sdap_entity_t *sdap_entity = nr_sdap_get_entity(ue_id, sdap.pdusession_id); + if (sdap_entity) { + LOG_E(SDAP, "SDAP Entity for UE already exists with RNTI/UE ID: %lu and PDU SESSION ID: %d\n", ue_id, sdap.pdusession_id); + if (!is_gnb) { + nr_sdap_ue_qfi2drb_config(sdap_entity, + sdap_entity->default_drb, ue_id, - mapped_qfi_2_add, - mappedQFIs2AddCount, - drb_identity, - has_sdap_rx, - has_sdap_tx); - return existing_sdap_entity; + sdap.mappedQFIs2Add, + sdap.mappedQFIs2AddCount, + sdap.drb_id, + sdap.sdap_rx, + sdap.sdap_tx); + } else { + // store QFI to DRB mapping rules + for (int i = 0; i < sdap.mappedQFIs2AddCount; i++) { + sdap_entity->qfi2drb_map_update(sdap_entity, sdap.mappedQFIs2Add[i], sdap.drb_id, sdap.sdap_rx, sdap.sdap_tx); + } + } + return sdap_entity; } - nr_sdap_entity_t *sdap_entity; - sdap_entity = calloc(1, sizeof(nr_sdap_entity_t)); - - if(sdap_entity == NULL) { - LOG_E(SDAP, "SDAP Entity creation failed, out of memory\n"); - exit(1); - } + sdap_entity = calloc_or_fail(1, sizeof(*sdap_entity)); + // SDAP entity ids sdap_entity->ue_id = ue_id; - sdap_entity->pdusession_id = pdusession_id; + sdap_entity->pdusession_id = sdap.pdusession_id; sdap_entity->is_gnb = is_gnb; + // rx/tx entities sdap_entity->tx_entity = nr_sdap_tx_entity; sdap_entity->rx_entity = nr_sdap_rx_entity; + // control pdu function pointers sdap_entity->sdap_construct_ctrl_pdu = nr_sdap_construct_ctrl_pdu; sdap_entity->sdap_map_ctrl_pdu = nr_sdap_map_ctrl_pdu; sdap_entity->sdap_submit_ctrl_pdu = nr_sdap_submit_ctrl_pdu; + // QFI to DRB mapping functions pointers sdap_entity->qfi2drb_map_update = nr_sdap_qfi2drb_map_update; sdap_entity->qfi2drb_map_delete = nr_sdap_qfi2drb_map_del; sdap_entity->qfi2drb_map = nr_sdap_qfi2drb_map; - sdap_entity->pdusession_sock = -1; - if(is_defaultDRB) { - sdap_entity->default_drb = drb_identity; - LOG_I(SDAP, "Default DRB for the created SDAP entity: %ld \n", sdap_entity->default_drb); - LOG_D(SDAP, "RRC updating mapping rules: %d\n", mappedQFIs2AddCount); - for (int i = 0; i < mappedQFIs2AddCount; i++) - sdap_entity->qfi2drb_map_update(sdap_entity, mapped_qfi_2_add[i], sdap_entity->default_drb, has_sdap_rx, has_sdap_tx); + // set default DRB + if (sdap.defaultDRB) { + sdap_entity->default_drb = sdap.drb_id; + LOG_I(SDAP, "Default DRB for the created SDAP entity: DRB %ld \n", sdap_entity->default_drb); } + for (int i = 0; i < sdap.mappedQFIs2AddCount; i++) { + // store QFI to DRB mapping rules + sdap_entity->qfi2drb_map_update(sdap_entity, sdap.mappedQFIs2Add[i], sdap.drb_id, sdap.sdap_rx, sdap.sdap_tx); + } + + // update SDAP entity list pointers sdap_entity->next_entity = sdap_info.sdap_entity_llist; sdap_info.sdap_entity_llist = sdap_entity; diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index 6ba76422fd..7670d4b03b 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -179,15 +179,7 @@ void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_u * TS 37.324 4.4 5.1.1 SDAP entity establishment * Establish an SDAP entity. */ -nr_sdap_entity_t *new_nr_sdap_entity(int is_gnb, - bool has_sdap_rx, - bool has_sdap_tx, - ue_id_t ue_id, - int pdusession_id, - bool is_defaultDRB, - uint8_t default_DRB, - NR_QFI_t *mapped_qfi_2_add, - uint8_t mappedQFIs2AddCount); +nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap); /* Entity Handling Related Functions */ nr_sdap_entity_t *nr_sdap_get_entity(ue_id_t ue_id, int pdusession_id); From 7376c9545c08dbfeb776a3675f780a348d0ff2a3 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 23 Apr 2025 23:32:44 +0200 Subject: [PATCH 052/275] Refactor nr_sdap_qfi2drb_map The goal of this function is to return the DRB ID mapped to the input QFI, for both DL and UL. The function looks up the DRB that is mapped to the QFI, if no mapping rule exists returns the default DRB. Returns 0 If no mapping and no default DRB exists for that QFI The function should not update the stored mapping rules, that is done when receiving a new SDAP configuration. In this function it was removed. See TS 37.324, 5.2.1 Uplink > If there is no stored QoS flow to DRB mapping rule for the QoS flow as specified in the subclause 5.3, > map the SDAP SDU to the default DRB else, map the SDAP SDU to the DRB according to the stored QoS flow to DRB mapping rule. --- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 42 +++++++++++++++----------- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 13 +------- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 9dcc309539..be00f9dab8 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -357,27 +357,33 @@ void nr_sdap_qfi2drb_map_del(nr_sdap_entity_t *entity, uint8_t qfi){ } /** - * @brief maps the QFIs to the default DRB if not mapping rule exists - * @return DRB that is mapped to the QFI, 0 if no mapping and no default DRB exists for that QFI -*/ -rb_id_t nr_sdap_qfi2drb_map(nr_sdap_entity_t *entity, uint8_t qfi){ - rb_id_t pdcp_entity; - - pdcp_entity = entity->qfi2drb_table[qfi].drb_id; - - if(pdcp_entity){ - LOG_D(SDAP, "Mapping rule exists for QFI: %u\n", qfi); - return pdcp_entity; - } else if(entity->default_drb) { - LOG_D(SDAP, "Mapping QFI: %u to Default DRB: %ld\n", qfi, entity->default_drb); - entity->qfi2drb_map_update(entity, qfi, entity->default_drb, entity->qfi2drb_table[qfi].has_sdap_rx, entity->qfi2drb_table[qfi].has_sdap_tx); + * @brief get the DRB ID mapped to the QFI, for both DL and UL + * @return DRB that is mapped to the QFI + * or the default DRB if no mapping rule exists + * or 0 if no mapping and no default DRB exists for that QFI + * @ref TS 37.324, 5.2.1 Uplink + * If there is no stored QoS flow to DRB mapping rule for the + * QoS flow as specified in the subclause 5.3, and a default DRB is configured, + * map the SDAP SDU to the default DRB else, map the SDAP SDU to the DRB according + * to the stored QoS flow to DRB mapping rule. */ +static int nr_sdap_qfi2drb(nr_sdap_entity_t *entity, uint8_t qfi) +{ + /* Fetch DRB ID mapped to QFI */ + int drb_id = entity->qfi2drb_table[qfi].drb_id; + if (drb_id) { + /* QoS flow to DRB mapping rule exists, return corresponding DRB ID */ + LOG_D(SDAP, "Existing QoS flow to DRB mapping rule: QFI %u to DRB %d\n", qfi, drb_id); + return drb_id; + } else if (entity->default_drb) { + /* QoS flow to DRB mapping rule does not exist, map SDAP SDU to default DRB, e.g. return default DRB of the SDAP entity */ + LOG_D(SDAP, "QoS flow to DRB mapping rule does not exists! mapping SDU to Default DRB: %ld\n", entity->default_drb); return entity->default_drb; } else { - LOG_D(SDAP, "Mapping rule and default DRB do not exist for QFI:%u\n", qfi); + /* Note: UE undefined behaviour when neither a default DRB + nor a stored QoS flow to DRB mapping rule exists */ + LOG_E(SDAP, "Mapping rule and default DRB do not exist for QFI:%u\n", qfi); return SDAP_MAP_RULE_EMPTY; } - - return pdcp_entity; } nr_sdap_ul_hdr_t nr_sdap_construct_ctrl_pdu(uint8_t qfi){ @@ -508,7 +514,7 @@ nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, cons // QFI to DRB mapping functions pointers sdap_entity->qfi2drb_map_update = nr_sdap_qfi2drb_map_update; sdap_entity->qfi2drb_map_delete = nr_sdap_qfi2drb_map_del; - sdap_entity->qfi2drb_map = nr_sdap_qfi2drb_map; + sdap_entity->qfi2drb_map = nr_sdap_qfi2drb; sdap_entity->pdusession_sock = -1; // set default DRB diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index 7670d4b03b..e8315ca846 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -105,7 +105,7 @@ typedef struct nr_sdap_entity_s { void (*qfi2drb_map_update)(struct nr_sdap_entity_s *entity, uint8_t qfi, rb_id_t drb, bool has_sdap_rx, bool has_sdap_tx); void (*qfi2drb_map_delete)(struct nr_sdap_entity_s *entity, uint8_t qfi); - rb_id_t (*qfi2drb_map)(struct nr_sdap_entity_s *entity, uint8_t qfi); + int (*qfi2drb_map)(struct nr_sdap_entity_s *entity, uint8_t qfi); nr_sdap_ul_hdr_t (*sdap_construct_ctrl_pdu)(uint8_t qfi); rb_id_t (*sdap_map_ctrl_pdu)(struct nr_sdap_entity_s *entity, rb_id_t pdcp_entity, int map_type, uint8_t dl_qfi); @@ -144,17 +144,6 @@ void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, uint8_t qfi, rb_id_t d /* QFI to DRB Mapping Related Function */ void nr_sdap_qfi2drb_map_del(nr_sdap_entity_t *entity, uint8_t qfi); -/* - * TS 37.324 - * 4.4 Functions - * Mapping between a QoS flow and a DRB for both DL and UL. - * - * 5.2.1 Uplink - * If there is no stored QoS flow to DRB mapping rule for the QoS flow as specified in the subclause 5.3, map the SDAP SDU to the default DRB - * else, map the SDAP SDU to the DRB according to the stored QoS flow to DRB mapping rule. - */ -rb_id_t nr_sdap_qfi2drb_map(nr_sdap_entity_t *entity, uint8_t qfi); - /* * TS 37.324 5.3 QoS flow to DRB Mapping * construct an end-marker control PDU, as specified in the subclause 6.2.3, for the QoS flow; From bae1de16d6a4019c7f8e79db52c01c7397eca9a1 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 23 Apr 2025 23:38:19 +0200 Subject: [PATCH 053/275] Refactor SDAP UL: QoS flow to DRB Mapping configuration and end-marker control PDU mapping The function nr_sdap_ue_qfi2drb_config should do UL QoS flow to DRB mapping configuration for a SDAP entity has already been established according to TS 37.324, 5.3 QoS flow to DRB Mapping, 5.3.1 Configuration Procedures. It should handle both mapping rules to add and to remove. To this purpose: * Simplify function signature by passing the sdap config. * Improve readability of the function. * Move handling of sdap.mappedQFIs2ReleaseCount (in nr_reconfigure_sdap_entity) inside the function. * Simplify end-marker control PDU mapping function by removing unused DRB ID input parameter (the function is fetching the DRB ID based on the input map type) * use int instead of legacy rb_id_t * limit scope of functions whenever necessary --- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 97 +++++++++++--------------- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 4 +- 2 files changed, 42 insertions(+), 59 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index be00f9dab8..71805bc750 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -281,7 +281,7 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, */ if(!entity->qfi2drb_table[sdap_hdr->QFI].drb_id && entity->default_drb){ nr_sdap_ul_hdr_t sdap_ctrl_pdu = entity->sdap_construct_ctrl_pdu(sdap_hdr->QFI); - rb_id_t sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, pdcp_entity, SDAP_CTRL_PDU_MAP_DEF_DRB, sdap_hdr->QFI); + int sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, SDAP_CTRL_PDU_MAP_DEF_DRB, sdap_hdr->QFI); entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu); } @@ -296,7 +296,7 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, */ if (pdcp_entity != entity->qfi2drb_table[sdap_hdr->QFI].drb_id) { nr_sdap_ul_hdr_t sdap_ctrl_pdu = entity->sdap_construct_ctrl_pdu(sdap_hdr->QFI); - rb_id_t sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, pdcp_entity, SDAP_CTRL_PDU_MAP_RULE_DRB, sdap_hdr->QFI); + int sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, SDAP_CTRL_PDU_MAP_RULE_DRB, sdap_hdr->QFI); entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu); } @@ -397,15 +397,16 @@ nr_sdap_ul_hdr_t nr_sdap_construct_ctrl_pdu(uint8_t qfi){ return sdap_end_marker_hdr; } -rb_id_t nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, rb_id_t pdcp_entity, int map_type, uint8_t dl_qfi){ - rb_id_t drb_of_endmarker = 0; +int nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, int map_type, uint8_t dl_qfi) +{ + int drb_of_endmarker = 0; if(map_type == SDAP_CTRL_PDU_MAP_DEF_DRB){ drb_of_endmarker = entity->default_drb; - LOG_D(SDAP, "Mapping Control PDU QFI: %u to Default DRB: %ld\n", dl_qfi, drb_of_endmarker); + LOG_D(SDAP, "Mapping Control PDU QFI: %u to Default DRB: %d\n", dl_qfi, drb_of_endmarker); } if(map_type == SDAP_CTRL_PDU_MAP_RULE_DRB){ drb_of_endmarker = entity->qfi2drb_map(entity, dl_qfi); - LOG_D(SDAP, "Mapping Control PDU QFI: %u to DRB: %ld\n", dl_qfi, drb_of_endmarker); + LOG_D(SDAP, "Mapping Control PDU QFI: %u to DRB: %d\n", dl_qfi, drb_of_endmarker); } return drb_of_endmarker; } @@ -421,39 +422,40 @@ void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_u } } -/** - * @brief UL QoS flow to DRB mapping configuration for an existing SDAP entity - * according to TS 37.324, 5.3 QoS flow to DRB Mapping, clause 5.3.1 Configuration Procedures - */ -static void nr_sdap_ue_qfi2drb_config(nr_sdap_entity_t *existing_sdap_entity, - rb_id_t pdcp_entity, - ue_id_t ue_id, - NR_QFI_t *mapped_qfi_2_add, - uint8_t mappedQFIs2AddCount, - uint8_t drb_identity, - bool has_sdap_rx, - bool has_sdap_tx) +/** @brief UL QoS flow to DRB mapping configuration for a SDAP entity has already been established + * according to TS 37.324, 5.3 QoS flow to DRB Mapping, 5.3.1 Configuration Procedures. + * The function handles both UL QoS Flows mapping rules to add and to remove. */ +static void nr_sdap_ue_qfi2drb_config(nr_sdap_entity_t *entity, const ue_id_t ue_id, const sdap_config_t sdap) { - LOG_D(SDAP, "RRC Configuring SDAP Entity\n"); - for(int i = 0; i < mappedQFIs2AddCount; i++){ - uint8_t qfi = mapped_qfi_2_add[i]; - /* a default DRB exists and there is no QFI to DRB mapping rule for the QFI */ - if (existing_sdap_entity->default_drb && existing_sdap_entity->qfi2drb_table[qfi].drb_id == SDAP_NO_MAPPING_RULE) { - nr_sdap_ul_hdr_t sdap_ctrl_pdu = existing_sdap_entity->sdap_construct_ctrl_pdu(qfi); - rb_id_t sdap_ctrl_pdu_drb = - existing_sdap_entity->sdap_map_ctrl_pdu(existing_sdap_entity, pdcp_entity, SDAP_CTRL_PDU_MAP_DEF_DRB, qfi); - existing_sdap_entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu); + // handle QFIs to DRB mapping rule to add + for (int i = 0; i < sdap.mappedQFIs2AddCount; i++) { + uint8_t qfi = sdap.mappedQFIs2Add[i]; + /* a default DRB exists and there is no stored QFI to DRB mapping rule for the QFI */ + if (entity->default_drb && entity->qfi2drb_table[qfi].drb_id == SDAP_NO_MAPPING_RULE) { + // construct an end-marker control PDU (6.2.3 TS 37.324) + nr_sdap_ul_hdr_t sdap_ctrl_pdu = entity->sdap_construct_ctrl_pdu(qfi); + // map the end-marker control PDU to the default DRB + int sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, SDAP_CTRL_PDU_MAP_DEF_DRB, qfi); + // submit the end-marker control PDU to the lower layers + entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu); } - /* the stored UL QFI to DRB mapping rule is different from the configured one and has UL SDAP header*/ - if (existing_sdap_entity->qfi2drb_table[qfi].drb_id != drb_identity && existing_sdap_entity->qfi2drb_table[qfi].has_sdap_tx) { - nr_sdap_ul_hdr_t sdap_ctrl_pdu = existing_sdap_entity->sdap_construct_ctrl_pdu(qfi); - rb_id_t sdap_ctrl_pdu_drb = - existing_sdap_entity->sdap_map_ctrl_pdu(existing_sdap_entity, pdcp_entity, SDAP_CTRL_PDU_MAP_RULE_DRB, qfi); - existing_sdap_entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu); + /* the stored UL QFI to DRB mapping rule is different from the configured one and has UL SDAP header */ + if (entity->qfi2drb_table[qfi].drb_id != sdap.drb_id && entity->qfi2drb_table[qfi].has_sdap_tx) { + // construct an end-marker control PDU (6.2.3 TS 37.324) + nr_sdap_ul_hdr_t sdap_ctrl_pdu = entity->sdap_construct_ctrl_pdu(qfi); + // map the end-marker control PDU to the DRB according to the stored QoS flow to DRB mapping rule + int sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, SDAP_CTRL_PDU_MAP_RULE_DRB, qfi); + // submit the end-marker control PDU to the lower layers + entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu); } - /* update QFI to DRB mapping rules */ + // store QFI to DRB mapping rules LOG_D(SDAP, "Storing the configured QoS flow to DRB mapping rule\n"); - existing_sdap_entity->qfi2drb_map_update(existing_sdap_entity, qfi, drb_identity, has_sdap_rx, has_sdap_tx); + entity->qfi2drb_map_update(entity, qfi, sdap.drb_id, sdap.sdap_rx, sdap.sdap_tx); + } + + // handle QFIs to DRB mapping rule to release + for (int i = 0; i < sdap.mappedQFIs2ReleaseCount; i++) { + entity->qfi2drb_map_delete(entity, sdap.mappedQFIs2Release[i]); } } @@ -478,14 +480,7 @@ nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, cons if (sdap_entity) { LOG_E(SDAP, "SDAP Entity for UE already exists with RNTI/UE ID: %lu and PDU SESSION ID: %d\n", ue_id, sdap.pdusession_id); if (!is_gnb) { - nr_sdap_ue_qfi2drb_config(sdap_entity, - sdap_entity->default_drb, - ue_id, - sdap.mappedQFIs2Add, - sdap.mappedQFIs2AddCount, - sdap.drb_id, - sdap.sdap_rx, - sdap.sdap_tx); + nr_sdap_ue_qfi2drb_config(sdap_entity, ue_id, sdap); } else { // store QFI to DRB mapping rules for (int i = 0; i < sdap.mappedQFIs2AddCount; i++) { @@ -708,20 +703,8 @@ void nr_reconfigure_sdap_entity(NR_SDAP_Config_t *sdap_config, ue_id_t ue_id, in ue_id, pdusession_id); /* QFI to DRB mapping */ - sdap_config_t sdap = get_sdap_Config(is_gnb, sdap_config, drb_id); - nr_sdap_ue_qfi2drb_config(sdap_entity, - sdap_entity->default_drb, - ue_id, - sdap.mappedQFIs2Add, - sdap.mappedQFIs2AddCount, - drb_id, - sdap.sdap_rx, - sdap.sdap_tx); - /* handle QFIs to DRB mapping rule to release */ - for (int i = 0; i < sdap.mappedQFIs2ReleaseCount; i++) { - uint8_t qfi = sdap.mappedQFIs2Release[i]; - sdap_entity->qfi2drb_map_delete(sdap_entity, qfi); - } + sdap_config_t sdap = nr_sdap_get_config(is_gnb, sdap_config, drb_id); + nr_sdap_ue_qfi2drb_config(sdap_entity, ue_id, sdap); } void set_qfi(uint8_t qfi, uint8_t pduid, ue_id_t ue_id) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index e8315ca846..ada9cfb26c 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -108,7 +108,7 @@ typedef struct nr_sdap_entity_s { int (*qfi2drb_map)(struct nr_sdap_entity_s *entity, uint8_t qfi); nr_sdap_ul_hdr_t (*sdap_construct_ctrl_pdu)(uint8_t qfi); - rb_id_t (*sdap_map_ctrl_pdu)(struct nr_sdap_entity_s *entity, rb_id_t pdcp_entity, int map_type, uint8_t dl_qfi); + int (*sdap_map_ctrl_pdu)(struct nr_sdap_entity_s *entity, int map_type, uint8_t dl_qfi); void (*sdap_submit_ctrl_pdu)(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); bool (*tx_entity)(struct nr_sdap_entity_s *entity, @@ -156,7 +156,7 @@ nr_sdap_ul_hdr_t nr_sdap_construct_ctrl_pdu(uint8_t qfi); * 1.) default DRB or * 2.) DRB according to the stored QoS flow to DRB mapping rule */ -rb_id_t nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, rb_id_t pdcp_entity, int map_type, uint8_t dl_qfi); +int nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, int map_type, uint8_t dl_qfi); /* * TS 37.324 5.3 QoS flow to DRB Mapping From 3efaafa63390ab4db7c96fc61a5b9c87d762c23d Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 24 Apr 2025 12:55:09 +0200 Subject: [PATCH 054/275] Refactor QFI to DRB mapping logic nr_sdap_qfi2drb_map_update function pointer should process QFIs to add and release from the entity mapping, therefore a new function pointer for QFIs to add was introduced and the function pointer to release the QFIs was called in the same function. Now the nr_sdap_qfi2drb_map_update is: (1) looping through the QFIs to add/update and store in the qfi2drb_table (2) looping through the QFIs to release and delete from qfi2drb_table Also: * Update the function signature to use sdap_config_t and nr_sdap_entity_t * log error when the update fails * limit the scope of the function * clang-formatted * removed limit on table entries set by AVLBL_DRB, replaced with MAX_DRBS_PER_UE --- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 70 ++++++++++++++++---------- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 16 +++--- 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 71805bc750..ec6386a62c 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -333,27 +333,45 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, } } -/** - * @brief update QFI to DRB mapping rules -*/ -void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, uint8_t qfi, rb_id_t drb, bool has_sdap_rx, bool has_sdap_tx) +/** @brief Update QFI to DRB mapping rules + * @param qfi the QoS Flow index, used as unique index of the qfi2drb mapping table + * @param drb the DRB ID to be mapped */ +static void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, const sdap_config_t *sdap) { - if(qfi < SDAP_MAX_QFI && - qfi > SDAP_MAP_RULE_EMPTY && - drb > 0 && - drb <= AVLBL_DRB){ - entity->qfi2drb_table[qfi].drb_id = drb; - entity->qfi2drb_table[qfi].has_sdap_rx = has_sdap_rx; - entity->qfi2drb_table[qfi].has_sdap_tx = has_sdap_tx; - LOG_D(SDAP, "Updated mapping: QFI %u -> DRB %ld \n", qfi, entity->qfi2drb_table[qfi].drb_id); - } else { - LOG_D(SDAP, "Map updated failed, QFI: %u, DRB: %ld\n", qfi, drb); + for (int i = 0; i < sdap->mappedQFIs2AddCount; i++) { + uint8_t qfi = sdap->mappedQFIs2Add[i]; + LOG_D(SDAP, "Updating QFI to DRB mapping rules: %d mapped QFIs for DRB %d\n", sdap->mappedQFIs2AddCount, sdap->drb_id); + if (qfi < SDAP_MAX_QFI && qfi > SDAP_MAP_RULE_EMPTY && sdap->drb_id > 0 && sdap->drb_id <= MAX_DRBS_PER_UE) { + entity->qfi2drb_map_add(entity, qfi, sdap->drb_id, sdap->sdap_rx, sdap->sdap_tx); + } else { + LOG_E(SDAP, "Failed to update qfi2drb mapping: QFI=%d, DRB=%d\n", qfi, sdap->drb_id); + } + } + for (int i = 0; i < sdap->mappedQFIs2ReleaseCount; i++) { + uint8_t qfi = sdap->mappedQFIs2Release[i]; + LOG_D(SDAP, "Deelting QFI to DRB mapping rules: QFI=%d for DRB=%d\n", qfi, sdap->drb_id); + entity->qfi2drb_map_delete(entity, qfi); } } -void nr_sdap_qfi2drb_map_del(nr_sdap_entity_t *entity, uint8_t qfi){ - entity->qfi2drb_table[qfi].drb_id = SDAP_NO_MAPPING_RULE; - LOG_D(SDAP, "Deleted mapping for QFI: %u \n", qfi); +static void nr_sdap_qfi2drb_map_add(nr_sdap_entity_t *entity, + const uint8_t qfi, + const uint8_t drb_id, + const uint8_t role_rx, + const uint8_t role_tx) +{ + qfi2drb_t *qfi2drb = &entity->qfi2drb_table[qfi]; + LOG_D(SDAP, "%s mapping: QFI %u -> DRB %d \n", qfi2drb->drb_id == SDAP_NO_MAPPING_RULE ? "Add" : "Update", qfi, drb_id); + qfi2drb->drb_id = drb_id; + qfi2drb->has_sdap_rx = role_rx; + qfi2drb->has_sdap_tx = role_tx; +} + +static void nr_sdap_qfi2drb_map_del(nr_sdap_entity_t *entity, const uint8_t qfi) +{ + qfi2drb_t *qfi2drb = &entity->qfi2drb_table[qfi]; + qfi2drb->drb_id = SDAP_NO_MAPPING_RULE; + LOG_D(SDAP, "Deleted mapping for QFI=%d, DRB=%d\n", qfi, qfi2drb->drb_id); } /** @@ -448,15 +466,16 @@ static void nr_sdap_ue_qfi2drb_config(nr_sdap_entity_t *entity, const ue_id_t ue // submit the end-marker control PDU to the lower layers entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu); } - // store QFI to DRB mapping rules - LOG_D(SDAP, "Storing the configured QoS flow to DRB mapping rule\n"); - entity->qfi2drb_map_update(entity, qfi, sdap.drb_id, sdap.sdap_rx, sdap.sdap_tx); } // handle QFIs to DRB mapping rule to release for (int i = 0; i < sdap.mappedQFIs2ReleaseCount; i++) { entity->qfi2drb_map_delete(entity, sdap.mappedQFIs2Release[i]); } + + // store QFI to DRB mapping rules + LOG_D(SDAP, "Storing the configured QoS flow to DRB mapping rule\n"); + entity->qfi2drb_map_update(entity, &sdap); } /** @@ -483,9 +502,7 @@ nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, cons nr_sdap_ue_qfi2drb_config(sdap_entity, ue_id, sdap); } else { // store QFI to DRB mapping rules - for (int i = 0; i < sdap.mappedQFIs2AddCount; i++) { - sdap_entity->qfi2drb_map_update(sdap_entity, sdap.mappedQFIs2Add[i], sdap.drb_id, sdap.sdap_rx, sdap.sdap_tx); - } + sdap_entity->qfi2drb_map_update(sdap_entity, &sdap); } return sdap_entity; } @@ -508,6 +525,7 @@ nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, cons // QFI to DRB mapping functions pointers sdap_entity->qfi2drb_map_update = nr_sdap_qfi2drb_map_update; + sdap_entity->qfi2drb_map_add = nr_sdap_qfi2drb_map_add; sdap_entity->qfi2drb_map_delete = nr_sdap_qfi2drb_map_del; sdap_entity->qfi2drb_map = nr_sdap_qfi2drb; sdap_entity->pdusession_sock = -1; @@ -518,10 +536,8 @@ nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, cons LOG_I(SDAP, "Default DRB for the created SDAP entity: DRB %ld \n", sdap_entity->default_drb); } - for (int i = 0; i < sdap.mappedQFIs2AddCount; i++) { - // store QFI to DRB mapping rules - sdap_entity->qfi2drb_map_update(sdap_entity, sdap.mappedQFIs2Add[i], sdap.drb_id, sdap.sdap_rx, sdap.sdap_tx); - } + // store QFI to DRB mapping rules + sdap_entity->qfi2drb_map_update(sdap_entity, &sdap); // update SDAP entity list pointers sdap_entity->next_entity = sdap_info.sdap_entity_llist; diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index ada9cfb26c..2d87961a62 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -38,7 +38,6 @@ #define SDAP_HDR_LENGTH (1) #define SDAP_MAX_QFI (64) #define SDAP_MAP_RULE_EMPTY (0) -#define AVLBL_DRB (5) #define SDAP_NO_MAPPING_RULE (0) #define SDAP_REFLECTIVE_MAPPING (1) #define SDAP_RQI_HANDLING (1) @@ -103,8 +102,13 @@ typedef struct nr_sdap_entity_s { qfi2drb_t qfi2drb_table[SDAP_MAX_QFI]; - void (*qfi2drb_map_update)(struct nr_sdap_entity_s *entity, uint8_t qfi, rb_id_t drb, bool has_sdap_rx, bool has_sdap_tx); - void (*qfi2drb_map_delete)(struct nr_sdap_entity_s *entity, uint8_t qfi); + void (*qfi2drb_map_update)(struct nr_sdap_entity_s *entity, const sdap_config_t *sdap); + void (*qfi2drb_map_delete)(struct nr_sdap_entity_s *entity, const uint8_t qfi); + void (*qfi2drb_map_add)(struct nr_sdap_entity_s *entity, + const uint8_t qfi, + const uint8_t drb_id, + const uint8_t role_rx, + const uint8_t role_tx); int (*qfi2drb_map)(struct nr_sdap_entity_s *entity, uint8_t qfi); nr_sdap_ul_hdr_t (*sdap_construct_ctrl_pdu)(uint8_t qfi); @@ -138,12 +142,6 @@ typedef struct nr_sdap_entity_s { struct nr_sdap_entity_s *next_entity; } nr_sdap_entity_t; -/* QFI to DRB Mapping Related Function */ -void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, uint8_t qfi, rb_id_t drb, bool has_sdap_rx, bool has_sdap_tx); - -/* QFI to DRB Mapping Related Function */ -void nr_sdap_qfi2drb_map_del(nr_sdap_entity_t *entity, uint8_t qfi); - /* * TS 37.324 5.3 QoS flow to DRB Mapping * construct an end-marker control PDU, as specified in the subclause 6.2.3, for the QoS flow; From 823340a2e23c5439c564de5417670b8fd9923e64 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 24 Apr 2025 16:33:37 +0200 Subject: [PATCH 055/275] Refactor handling of DL/UL SDAP header in the SDAP entity The TX/RX entity should have different behavior according to 5.2.1, 5.2.2, 3GPP TS 37.324, depending on whether it is UL and DL. The gNB can play a role both as UL RX entity and DL TX entity, while the UE can play a role both as UL TX entity and DL RX entity. The goal of this commit is to extend the information stored in the SDAP entity as the current code is just marking the entity as TX/RX. The commit introduces a bitmap to store the role of the entity, which can be DL RX, DL TX, UL RX and UL TX. Based on this the TX SDAP entity knows whether a DL/UL PDU should be build, and the RX SDAP entity knows whether a DL/UL PDU should be processed. See also Figure 4.2.2-1. SDAP headers are present only if enable_sdap option is used. --- openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c | 4 +- openair2/SDAP/nr_sdap/nr_sdap.c | 1 - openair2/SDAP/nr_sdap/nr_sdap_entity.c | 80 +++++++++++------------ openair2/SDAP/nr_sdap/nr_sdap_entity.h | 31 +++------ 4 files changed, 50 insertions(+), 66 deletions(-) diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index 6bd2d9354f..8f2bf0a043 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -623,8 +623,8 @@ void add_drb(int is_gnb, is_gnb, sdap->drb_id, sdap->pdusession_id, - sdap->sdap_rx, - sdap->sdap_tx, + (sdap->role & (SDAP_UL_RX | SDAP_DL_RX)) != 0, + (sdap->role & (SDAP_UL_TX | SDAP_DL_TX)) != 0, deliver_sdu_drb, ue, is_gnb ? deliver_pdu_drb_gnb : deliver_pdu_drb_ue, diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index f6602c7597..58dc495c14 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -100,7 +100,6 @@ void sdap_data_ind(rb_id_t pdcp_entity, sdap_entity->rx_entity(sdap_entity, pdcp_entity, is_gnb, - has_sdap_rx, pdusession_id, ue_id, buf, diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index ec6386a62c..89c9748a8e 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -42,32 +42,20 @@ static nr_sdap_entity_info sdap_info; instance_t *N3GTPUInst = NULL; -/** - * @brief indicates whether it is a receiving SDAP entity - * i.e. for UE, header for DL data is present - * for gNB, header for UL data is present - */ -bool is_sdap_rx(bool is_gnb, NR_SDAP_Config_t *sdap_config) +/** @brief Returns a bitmap indicating the SDAP entity role, + * i.e. for UL transmission, header for UL data is present in RX/TX + * for DL transmission, header for DL data is present in RX/TX */ +static int get_sdap_role(bool is_gnb, const NR_SDAP_Config_t *sdap_config) { - if (is_gnb) { - return sdap_config->sdap_HeaderUL == NR_SDAP_Config__sdap_HeaderUL_present; - } else { - return sdap_config->sdap_HeaderDL == NR_SDAP_Config__sdap_HeaderDL_present; - } -} - -/** - * @brief indicates whether it is a transmitting SDAP entity - * i.e. for UE, header for UL data is present - * for gNB, header for DL data is present - */ -bool is_sdap_tx(bool is_gnb, NR_SDAP_Config_t *sdap_config) -{ - if (is_gnb) { - return sdap_config->sdap_HeaderDL == NR_SDAP_Config__sdap_HeaderDL_present; - } else { - return sdap_config->sdap_HeaderUL == NR_SDAP_Config__sdap_HeaderUL_present; - } + sdap_role_t role_ul = is_gnb ? SDAP_UL_RX : SDAP_UL_TX; + sdap_role_t role_dl = is_gnb ? SDAP_DL_TX : SDAP_DL_RX; + int role = 0; + if (sdap_config->sdap_HeaderUL == NR_SDAP_Config__sdap_HeaderUL_present) + role |= role_ul; + if (sdap_config->sdap_HeaderDL == NR_SDAP_Config__sdap_HeaderDL_present) + role |= role_dl; + DevAssert(role != 0); + return role; } void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu) @@ -107,7 +95,8 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, bool ret = false; /*Hardcode DRB ID given from upper layer (ue/gnb_tun_read_thread rb_id), it will change if we have SDAP*/ rb_id_t sdap_drb_id = rb_id; - int pdcp_ent_has_sdap = 0; + bool sdap_ul_tx = false; + bool sdap_dl_tx = false; if(sdu_buffer == NULL) { LOG_E(SDAP, "%s:%d:%s: NULL sdu_buffer \n", __FILE__, __LINE__, __FUNCTION__); @@ -119,12 +108,13 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, if(pdcp_entity){ sdap_drb_id = pdcp_entity; - pdcp_ent_has_sdap = entity->qfi2drb_table[qfi].has_sdap_tx; + sdap_ul_tx = entity->qfi2drb_table[qfi].entity_role & SDAP_UL_TX; // UE TX entity + sdap_dl_tx = entity->qfi2drb_table[qfi].entity_role & SDAP_DL_TX; // gNB TX entity LOG_D(SDAP, "TX - QFI: %u is mapped to DRB ID: %ld\n", qfi, entity->qfi2drb_table[qfi].drb_id); } - if(!pdcp_ent_has_sdap){ - LOG_D(SDAP, "TX - DRB ID: %ld does not have SDAP\n", entity->qfi2drb_table[qfi].drb_id); + if (!sdap_ul_tx && !sdap_dl_tx) { + LOG_D(SDAP, "TX - DRB ID: %ld does not have SDAP header\n", entity->qfi2drb_table[qfi].drb_id); ret = nr_pdcp_data_req_drb(ctxt_p, srb_flag, sdap_drb_id, @@ -147,7 +137,7 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, return 0; } - if(ctxt_p->enb_flag) { // gNB + if (sdap_dl_tx) { // create DL Data PDU with SDAP header offset = SDAP_HDR_LENGTH; /* * TS 37.324 4.4 Functions @@ -165,7 +155,7 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, LOG_D(SDAP, "TX Entity QFI: %u \n", sdap_hdr.QFI); LOG_D(SDAP, "TX Entity RQI: %u \n", sdap_hdr.RQI); LOG_D(SDAP, "TX Entity RDI: %u \n", sdap_hdr.RDI); - } else { // nrUE + } else if (sdap_ul_tx) { // create UL Data PDU with SDAP header offset = SDAP_HDR_LENGTH; /* * TS 37.324 4.4 Functions @@ -213,7 +203,6 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, rb_id_t pdcp_entity, int is_gnb, - bool has_sdap_rx, int pdusession_id, ue_id_t ue_id, char *buf, @@ -221,9 +210,18 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, { /* The offset of the SDAP header, it might be 0 if has_sdap_rx is not true in the pdcp entity. */ int offset=0; + int qfi = buf[0] & 0x3F; // QFI is always the first 6 bits in the first octet + if (qfi <= 0 || qfi >= SDAP_MAX_QFI) { + LOG_E(SDAP, "Invalid QFI %d received in SDAP header\n", qfi); + return; + } + + // Fetch entity role from the qfi2drb_table + bool sdap_ul_rx = entity->qfi2drb_table[qfi].entity_role & SDAP_UL_RX; // gNB RX entity + bool sdap_dl_rx = entity->qfi2drb_table[qfi].entity_role & SDAP_DL_RX; // UE RX entity if (is_gnb) { // gNB - if (has_sdap_rx) { // Handling the SDAP Header + if (sdap_ul_rx) { // UL Data/Control PDU with SDAP header offset = SDAP_HDR_LENGTH; nr_sdap_ul_hdr_t *sdap_hdr = (nr_sdap_ul_hdr_t *)buf; LOG_D(SDAP, "RX Entity Received QFI: %u\n", sdap_hdr->QFI); @@ -255,7 +253,7 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, * 5.2.2 Downlink * if the DRB from which this SDAP data PDU is received is configured by RRC with the presence of SDAP header. */ - if (has_sdap_rx) { // Handling the SDAP Header + if (sdap_dl_rx) { // DL Data/Control PDU with SDAP header offset = SDAP_HDR_LENGTH; /* * TS 37.324 5.2 Data transfer @@ -342,7 +340,7 @@ static void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, const sdap_conf uint8_t qfi = sdap->mappedQFIs2Add[i]; LOG_D(SDAP, "Updating QFI to DRB mapping rules: %d mapped QFIs for DRB %d\n", sdap->mappedQFIs2AddCount, sdap->drb_id); if (qfi < SDAP_MAX_QFI && qfi > SDAP_MAP_RULE_EMPTY && sdap->drb_id > 0 && sdap->drb_id <= MAX_DRBS_PER_UE) { - entity->qfi2drb_map_add(entity, qfi, sdap->drb_id, sdap->sdap_rx, sdap->sdap_tx); + entity->qfi2drb_map_add(entity, qfi, sdap->drb_id, sdap->role); } else { LOG_E(SDAP, "Failed to update qfi2drb mapping: QFI=%d, DRB=%d\n", qfi, sdap->drb_id); } @@ -357,14 +355,12 @@ static void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, const sdap_conf static void nr_sdap_qfi2drb_map_add(nr_sdap_entity_t *entity, const uint8_t qfi, const uint8_t drb_id, - const uint8_t role_rx, - const uint8_t role_tx) + const uint8_t role) { qfi2drb_t *qfi2drb = &entity->qfi2drb_table[qfi]; LOG_D(SDAP, "%s mapping: QFI %u -> DRB %d \n", qfi2drb->drb_id == SDAP_NO_MAPPING_RULE ? "Add" : "Update", qfi, drb_id); qfi2drb->drb_id = drb_id; - qfi2drb->has_sdap_rx = role_rx; - qfi2drb->has_sdap_tx = role_tx; + qfi2drb->entity_role = role; } static void nr_sdap_qfi2drb_map_del(nr_sdap_entity_t *entity, const uint8_t qfi) @@ -458,7 +454,8 @@ static void nr_sdap_ue_qfi2drb_config(nr_sdap_entity_t *entity, const ue_id_t ue entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu); } /* the stored UL QFI to DRB mapping rule is different from the configured one and has UL SDAP header */ - if (entity->qfi2drb_table[qfi].drb_id != sdap.drb_id && entity->qfi2drb_table[qfi].has_sdap_tx) { + bool ul_sdap_header = (entity->qfi2drb_table[qfi].entity_role & SDAP_UL_TX) != 0; + if (entity->qfi2drb_table[qfi].drb_id != sdap.drb_id && ul_sdap_header) { // construct an end-marker control PDU (6.2.3 TS 37.324) nr_sdap_ul_hdr_t sdap_ctrl_pdu = entity->sdap_construct_ctrl_pdu(qfi); // map the end-marker control PDU to the DRB according to the stored QoS flow to DRB mapping rule @@ -674,8 +671,7 @@ sdap_config_t nr_sdap_get_config(const int is_gnb, const NR_SDAP_Config_t *sdap_ DevAssert(sdap_Config); sdap_config_t sdapConfig = {0}; sdapConfig.drb_id = drb_id; - sdapConfig.sdap_rx = is_sdap_rx(is_gnb, sdap_Config); - sdapConfig.sdap_tx = is_sdap_tx(is_gnb, sdap_Config); + sdapConfig.role = get_sdap_role(is_gnb, sdap_Config); sdapConfig.defaultDRB = sdap_Config->defaultDRB; // 3GPP TS 38.331 The network sets sdap-HeaderUL to present if the field defaultDRB is set to true if (sdapConfig.defaultDRB && (sdap_Config->sdap_HeaderUL != NR_SDAP_Config__sdap_HeaderUL_present)) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index 2d87961a62..874b58b828 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -68,10 +68,16 @@ typedef struct nr_sdap_ul_hdr_s { uint8_t DC:1; } __attribute__((packed)) nr_sdap_ul_hdr_t; +typedef enum { + SDAP_UL_TX = (1 << 0), + SDAP_UL_RX = (1 << 1), + SDAP_DL_TX = (1 << 2), + SDAP_DL_RX = (1 << 3) +} sdap_role_t; + typedef struct qfi2drb_s { rb_id_t drb_id; - bool has_sdap_rx; - bool has_sdap_tx; + int entity_role; } qfi2drb_t; void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); @@ -79,8 +85,7 @@ void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_s typedef struct sdap_configuration_s { int pdusession_id; int drb_id; - bool sdap_rx; - bool sdap_tx; + int role; bool defaultDRB; NR_QFI_t mappedQFIs2Add[SDAP_MAX_QFI]; uint8_t mappedQFIs2AddCount; @@ -107,8 +112,7 @@ typedef struct nr_sdap_entity_s { void (*qfi2drb_map_add)(struct nr_sdap_entity_s *entity, const uint8_t qfi, const uint8_t drb_id, - const uint8_t role_rx, - const uint8_t role_tx); + const uint8_t role); int (*qfi2drb_map)(struct nr_sdap_entity_s *entity, uint8_t qfi); nr_sdap_ul_hdr_t (*sdap_construct_ctrl_pdu)(uint8_t qfi); @@ -132,7 +136,6 @@ typedef struct nr_sdap_entity_s { void (*rx_entity)(struct nr_sdap_entity_s *entity, rb_id_t pdcp_entity, int is_gnb, - bool has_sdap_rx, int pdusession_id, ue_id_t ue_id, char *buf, @@ -193,20 +196,6 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id); */ bool nr_sdap_delete_ue_entities(ue_id_t ue_id); -/** - * @brief indicates whether it is a receiving SDAP entity - * i.e. for UE, header for DL data is present - * for gNB, header for UL data is present - */ -bool is_sdap_rx(bool is_gnb, NR_SDAP_Config_t *sdap_config); - -/** - * @brief indicates whether it is a transmitting SDAP entity - * i.e. for UE, header for UL data is present - * for gNB, header for DL data is present - */ -bool is_sdap_tx(bool is_gnb, NR_SDAP_Config_t *sdap_config); - /** * @brief Run the SDAP reconfiguration for the DRB * @param[in] ue_id Unique identifier for the User Equipment. ID Range [0, 65536]. From 8207a4c352bb41af60d32f29067fa9a00738292c Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 8 Jul 2025 15:33:28 +0200 Subject: [PATCH 056/275] SDAP refactoring: replace legacy (LTE) type rb_id_t with int --- openair2/SDAP/nr_sdap/nr_sdap.c | 4 ++-- openair2/SDAP/nr_sdap/nr_sdap.h | 4 ++-- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 22 +++++++++++----------- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 14 +++++++------- openair3/ocp-gtpu/gtp_itf.h | 2 +- tests/nr-cuup/nr-cuup-load-test.c | 2 +- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index 58dc495c14..a32ce7152c 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -47,7 +47,7 @@ static void reblock_tun_socket(int fd) bool sdap_data_req(protocol_ctxt_t *ctxt_p, const ue_id_t ue_id, const srb_flag_t srb_flag, - const rb_id_t rb_id, + const int rb_id, const mui_t mui, const confirm_t confirm, const sdu_size_t sdu_buffer_size, @@ -82,7 +82,7 @@ bool sdap_data_req(protocol_ctxt_t *ctxt_p, return ret; } -void sdap_data_ind(rb_id_t pdcp_entity, +void sdap_data_ind(int pdcp_entity, int is_gnb, bool has_sdap_rx, int pdusession_id, diff --git a/openair2/SDAP/nr_sdap/nr_sdap.h b/openair2/SDAP/nr_sdap/nr_sdap.h index 3877e58603..54a26e2a5b 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.h +++ b/openair2/SDAP/nr_sdap/nr_sdap.h @@ -35,7 +35,7 @@ bool sdap_data_req(protocol_ctxt_t *ctxt_p, const ue_id_t ue_id, const srb_flag_t srb_flag, - const rb_id_t rb_id, + const int rb_id, const mui_t mui, const confirm_t confirm, const sdu_size_t sdu_buffer_size, @@ -53,7 +53,7 @@ bool sdap_data_req(protocol_ctxt_t *ctxt_p, * Uplink - gNB * Downlink - nrUE */ -void sdap_data_ind(rb_id_t pdcp_entity, +void sdap_data_ind(int pdcp_entity, int is_gnb, bool has_sdap_rx, int pdusession_id, diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 89c9748a8e..198b8792fe 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -58,7 +58,7 @@ static int get_sdap_role(bool is_gnb, const NR_SDAP_Config_t *sdap_config) return role; } -void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu) +void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu) { protocol_ctxt_t ctxt = { .rntiMaybeUEid = ue_id }; @@ -72,7 +72,7 @@ void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_s PDCP_TRANSMISSION_MODE_UNKNOWN, NULL, NULL); - LOG_D(SDAP, "Control PDU - Submitting Control PDU to DRB ID: %ld\n", sdap_ctrl_pdu_drb); + LOG_D(SDAP, "Control PDU - Submitting Control PDU to DRB ID: %d\n", sdap_ctrl_pdu_drb); LOG_D(SDAP, "QFI: %u\n R: %u\n D/C: %u\n", ctrl_pdu.QFI, ctrl_pdu.R, ctrl_pdu.DC); return; } @@ -80,7 +80,7 @@ void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_s static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, protocol_ctxt_t *ctxt_p, const srb_flag_t srb_flag, - const rb_id_t rb_id, + const int rb_id, const mui_t mui, const confirm_t confirm, const sdu_size_t sdu_buffer_size, @@ -94,7 +94,7 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, int offset=0; bool ret = false; /*Hardcode DRB ID given from upper layer (ue/gnb_tun_read_thread rb_id), it will change if we have SDAP*/ - rb_id_t sdap_drb_id = rb_id; + int sdap_drb_id = rb_id; bool sdap_ul_tx = false; bool sdap_dl_tx = false; @@ -104,17 +104,17 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, } uint8_t sdap_buf[SDAP_MAX_PDU]; - rb_id_t pdcp_entity = entity->qfi2drb_map(entity, qfi); + int pdcp_entity = entity->qfi2drb_map(entity, qfi); if(pdcp_entity){ sdap_drb_id = pdcp_entity; sdap_ul_tx = entity->qfi2drb_table[qfi].entity_role & SDAP_UL_TX; // UE TX entity sdap_dl_tx = entity->qfi2drb_table[qfi].entity_role & SDAP_DL_TX; // gNB TX entity - LOG_D(SDAP, "TX - QFI: %u is mapped to DRB ID: %ld\n", qfi, entity->qfi2drb_table[qfi].drb_id); + LOG_D(SDAP, "TX - QFI: %u is mapped to DRB ID: %d\n", qfi, entity->qfi2drb_table[qfi].drb_id); } if (!sdap_ul_tx && !sdap_dl_tx) { - LOG_D(SDAP, "TX - DRB ID: %ld does not have SDAP header\n", entity->qfi2drb_table[qfi].drb_id); + LOG_D(SDAP, "TX - DRB ID: %d does not have SDAP header\n", entity->qfi2drb_table[qfi].drb_id); ret = nr_pdcp_data_req_drb(ctxt_p, srb_flag, sdap_drb_id, @@ -201,7 +201,7 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, } static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, - rb_id_t pdcp_entity, + int pdcp_entity, int is_gnb, int pdusession_id, ue_id_t ue_id, @@ -390,7 +390,7 @@ static int nr_sdap_qfi2drb(nr_sdap_entity_t *entity, uint8_t qfi) return drb_id; } else if (entity->default_drb) { /* QoS flow to DRB mapping rule does not exist, map SDAP SDU to default DRB, e.g. return default DRB of the SDAP entity */ - LOG_D(SDAP, "QoS flow to DRB mapping rule does not exists! mapping SDU to Default DRB: %ld\n", entity->default_drb); + LOG_D(SDAP, "QoS flow to DRB mapping rule does not exists! mapping SDU to Default DRB: %d\n", entity->default_drb); return entity->default_drb; } else { /* Note: UE undefined behaviour when neither a default DRB @@ -428,7 +428,7 @@ int nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, int map_type, uint8_t dl_qfi) /** * @brief Submit the end-marker control PDU to PDCP according to TS 37.324, clause 5.3 */ -void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu) +void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu) { if(sdap_ctrl_pdu_drb){ nr_pdcp_submit_sdap_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, ctrl_pdu); @@ -530,7 +530,7 @@ nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, cons // set default DRB if (sdap.defaultDRB) { sdap_entity->default_drb = sdap.drb_id; - LOG_I(SDAP, "Default DRB for the created SDAP entity: DRB %ld \n", sdap_entity->default_drb); + LOG_I(SDAP, "Default DRB for the created SDAP entity: DRB %d \n", sdap_entity->default_drb); } // store QFI to DRB mapping rules diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index 874b58b828..b529dc0b4a 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -76,11 +76,11 @@ typedef enum { } sdap_role_t; typedef struct qfi2drb_s { - rb_id_t drb_id; + int drb_id; int entity_role; } qfi2drb_t; -void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); +void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); typedef struct sdap_configuration_s { int pdusession_id; @@ -95,7 +95,7 @@ typedef struct sdap_configuration_s { typedef struct nr_sdap_entity_s { ue_id_t ue_id; - rb_id_t default_drb; + int default_drb; /// sdap_tun_read_thread needs to know if we are gNB/UE, so for noS1 mode, /// store which one we are bool is_gnb; @@ -117,12 +117,12 @@ typedef struct nr_sdap_entity_s { nr_sdap_ul_hdr_t (*sdap_construct_ctrl_pdu)(uint8_t qfi); int (*sdap_map_ctrl_pdu)(struct nr_sdap_entity_s *entity, int map_type, uint8_t dl_qfi); - void (*sdap_submit_ctrl_pdu)(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); + void (*sdap_submit_ctrl_pdu)(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); bool (*tx_entity)(struct nr_sdap_entity_s *entity, protocol_ctxt_t *ctxt_p, const srb_flag_t srb_flag, - const rb_id_t rb_id, + const int rb_id, const mui_t mui, const confirm_t confirm, const sdu_size_t sdu_buffer_size, @@ -134,7 +134,7 @@ typedef struct nr_sdap_entity_s { const bool rqi); void (*rx_entity)(struct nr_sdap_entity_s *entity, - rb_id_t pdcp_entity, + int pdcp_entity, int is_gnb, int pdusession_id, ue_id_t ue_id, @@ -163,7 +163,7 @@ int nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, int map_type, uint8_t dl_qfi) * TS 37.324 5.3 QoS flow to DRB Mapping * Submit the end-marker control PDU to the lower layer. */ -void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); +void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); /* * TS 37.324 4.4 5.1.1 SDAP entity establishment diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index 4fcb8190e4..a0d1580cf9 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -49,7 +49,7 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; typedef bool (*gtpCallbackSDAP)(protocol_ctxt_t *ctxt_pP, const ue_id_t ue_id, const srb_flag_t srb_flagP, - const rb_id_t rb_idP, + const int rb_idP, const mui_t muiP, const confirm_t confirmP, const sdu_size_t sdu_buffer_sizeP, diff --git a/tests/nr-cuup/nr-cuup-load-test.c b/tests/nr-cuup/nr-cuup-load-test.c index 0c417ca5aa..1af262e146 100644 --- a/tests/nr-cuup/nr-cuup-load-test.c +++ b/tests/nr-cuup/nr-cuup-load-test.c @@ -165,7 +165,7 @@ static struct ue_stat { static bool recv_ng(protocol_ctxt_t *ctxt, const ue_id_t ue_id, const srb_flag_t flag, - const rb_id_t rb, + const int rb, const mui_t mui, const confirm_t confirm, const sdu_size_t size, From d760a16827d3786e89887177daad7b9167d8eebc Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Mon, 8 Sep 2025 16:14:05 +0200 Subject: [PATCH 057/275] SDAP TX: explicit check on qfi2drb_map output Each QFI should always be mapped to a DRB. If that is not the case, we have an undefined behavior. This should not occur. This commit prints an error in tx_entity and returns. --- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 198b8792fe..83b6896b0e 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -34,6 +34,8 @@ #include "tun_if.h" #include "nr_sdap.h" +#define NO_SDAP_HEADER 0 + typedef struct { nr_sdap_entity_t *sdap_entity_llist; } nr_sdap_entity_info; @@ -49,12 +51,11 @@ static int get_sdap_role(bool is_gnb, const NR_SDAP_Config_t *sdap_config) { sdap_role_t role_ul = is_gnb ? SDAP_UL_RX : SDAP_UL_TX; sdap_role_t role_dl = is_gnb ? SDAP_DL_TX : SDAP_DL_RX; - int role = 0; + int role = NO_SDAP_HEADER; if (sdap_config->sdap_HeaderUL == NR_SDAP_Config__sdap_HeaderUL_present) role |= role_ul; if (sdap_config->sdap_HeaderDL == NR_SDAP_Config__sdap_HeaderDL_present) role |= role_dl; - DevAssert(role != 0); return role; } @@ -106,11 +107,14 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, uint8_t sdap_buf[SDAP_MAX_PDU]; int pdcp_entity = entity->qfi2drb_map(entity, qfi); - if(pdcp_entity){ + if (pdcp_entity != SDAP_MAP_RULE_EMPTY) { sdap_drb_id = pdcp_entity; sdap_ul_tx = entity->qfi2drb_table[qfi].entity_role & SDAP_UL_TX; // UE TX entity sdap_dl_tx = entity->qfi2drb_table[qfi].entity_role & SDAP_DL_TX; // gNB TX entity LOG_D(SDAP, "TX - QFI: %u is mapped to DRB ID: %d\n", qfi, entity->qfi2drb_table[qfi].drb_id); + } else { + LOG_E(SDAP, "QFI %u not mapped to any DRB - dropping SDU of size %d\n", qfi, sdu_buffer_size); + return false; } if (!sdap_ul_tx && !sdap_dl_tx) { @@ -672,6 +676,7 @@ sdap_config_t nr_sdap_get_config(const int is_gnb, const NR_SDAP_Config_t *sdap_ sdap_config_t sdapConfig = {0}; sdapConfig.drb_id = drb_id; sdapConfig.role = get_sdap_role(is_gnb, sdap_Config); + LOG_D(SDAP, "SDAP headers %s\n", sdapConfig.role == NO_SDAP_HEADER ? "absent" : "present"); sdapConfig.defaultDRB = sdap_Config->defaultDRB; // 3GPP TS 38.331 The network sets sdap-HeaderUL to present if the field defaultDRB is set to true if (sdapConfig.defaultDRB && (sdap_Config->sdap_HeaderUL != NR_SDAP_Config__sdap_HeaderUL_present)) From 0348aa08b91ead59171cbb8ea45f4aa95765c298 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 14 Oct 2025 19:10:02 +0200 Subject: [PATCH 058/275] Remove rb_id parameter from SDAP TX entity - QFI determines DRB mapping - Remove rb_id parameter from nr_sdap_tx_entity() function signature - Update tx_entity function pointer - Remove rb_id parameter from sdap_data_req() function signature - Remove unused rb_id variable from sdap_tun_read_thread The rb_id parameter was redundant since SDAP determines DRB ID from QFI mapping via entity->qfi2drb_map(entity, qfi). This simplifies the interface. --- executables/lte-softmodem.c | 1 - openair2/SDAP/nr_sdap/nr_sdap.c | 5 ----- openair2/SDAP/nr_sdap/nr_sdap.h | 1 - openair2/SDAP/nr_sdap/nr_sdap_entity.c | 12 ++++-------- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 1 - openair3/ocp-gtpu/gtp_itf.cpp | 1 - openair3/ocp-gtpu/gtp_itf.h | 1 - tests/nr-cuup/nr-cuup-load-test.c | 1 - 8 files changed, 4 insertions(+), 19 deletions(-) diff --git a/executables/lte-softmodem.c b/executables/lte-softmodem.c index 77fdb21eb4..80b532607e 100644 --- a/executables/lte-softmodem.c +++ b/executables/lte-softmodem.c @@ -151,7 +151,6 @@ double cpuf; bool sdap_data_req(protocol_ctxt_t *ctxt_p, const ue_id_t ue_id, const srb_flag_t srb_flag, - const rb_id_t rb_id, const mui_t mui, const confirm_t confirm, const sdu_size_t sdu_buffer_size, diff --git a/openair2/SDAP/nr_sdap/nr_sdap.c b/openair2/SDAP/nr_sdap/nr_sdap.c index a32ce7152c..1aec02814e 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.c +++ b/openair2/SDAP/nr_sdap/nr_sdap.c @@ -47,7 +47,6 @@ static void reblock_tun_socket(int fd) bool sdap_data_req(protocol_ctxt_t *ctxt_p, const ue_id_t ue_id, const srb_flag_t srb_flag, - const int rb_id, const mui_t mui, const confirm_t confirm, const sdu_size_t sdu_buffer_size, @@ -69,7 +68,6 @@ bool sdap_data_req(protocol_ctxt_t *ctxt_p, bool ret = sdap_entity->tx_entity(sdap_entity, ctxt_p, srb_flag, - rb_id, mui, confirm, sdu_buffer_size, @@ -115,8 +113,6 @@ static void *sdap_tun_read_thread(void *arg) int len; reblock_tun_socket(entity->pdusession_sock); - int rb_id = 1; - while (!entity->stop_thread) { len = read(entity->pdusession_sock, &rx_buf, NL_MAX_PAYLOAD); if (len == -1) { @@ -147,7 +143,6 @@ static void *sdap_tun_read_thread(void *arg) entity->tx_entity(entity, &ctxt, SRB_FLAG_NO, - rb_id, RLC_MUI_UNDEFINED, RLC_SDU_CONFIRM_NO, len, diff --git a/openair2/SDAP/nr_sdap/nr_sdap.h b/openair2/SDAP/nr_sdap/nr_sdap.h index 54a26e2a5b..1164641970 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap.h +++ b/openair2/SDAP/nr_sdap/nr_sdap.h @@ -35,7 +35,6 @@ bool sdap_data_req(protocol_ctxt_t *ctxt_p, const ue_id_t ue_id, const srb_flag_t srb_flag, - const int rb_id, const mui_t mui, const confirm_t confirm, const sdu_size_t sdu_buffer_size, diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 83b6896b0e..438e05dd60 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -81,7 +81,6 @@ void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, protocol_ctxt_t *ctxt_p, const srb_flag_t srb_flag, - const int rb_id, const mui_t mui, const confirm_t confirm, const sdu_size_t sdu_buffer_size, @@ -94,8 +93,6 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, /* The offset of the SDAP header, it might be 0 if has_sdap_tx is not true in the pdcp entity. */ int offset=0; bool ret = false; - /*Hardcode DRB ID given from upper layer (ue/gnb_tun_read_thread rb_id), it will change if we have SDAP*/ - int sdap_drb_id = rb_id; bool sdap_ul_tx = false; bool sdap_dl_tx = false; @@ -105,10 +102,9 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, } uint8_t sdap_buf[SDAP_MAX_PDU]; - int pdcp_entity = entity->qfi2drb_map(entity, qfi); + int drb_id = entity->qfi2drb_map(entity, qfi); - if (pdcp_entity != SDAP_MAP_RULE_EMPTY) { - sdap_drb_id = pdcp_entity; + if (drb_id != SDAP_MAP_RULE_EMPTY) { sdap_ul_tx = entity->qfi2drb_table[qfi].entity_role & SDAP_UL_TX; // UE TX entity sdap_dl_tx = entity->qfi2drb_table[qfi].entity_role & SDAP_DL_TX; // gNB TX entity LOG_D(SDAP, "TX - QFI: %u is mapped to DRB ID: %d\n", qfi, entity->qfi2drb_table[qfi].drb_id); @@ -121,7 +117,7 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, LOG_D(SDAP, "TX - DRB ID: %d does not have SDAP header\n", entity->qfi2drb_table[qfi].drb_id); ret = nr_pdcp_data_req_drb(ctxt_p, srb_flag, - sdap_drb_id, + drb_id, mui, confirm, sdu_buffer_size, @@ -189,7 +185,7 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity, */ ret = nr_pdcp_data_req_drb(ctxt_p, srb_flag, - sdap_drb_id, + drb_id, mui, confirm, sdu_buffer_size + offset, diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index b529dc0b4a..a5ac2953cf 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -122,7 +122,6 @@ typedef struct nr_sdap_entity_s { bool (*tx_entity)(struct nr_sdap_entity_s *entity, protocol_ctxt_t *ctxt_p, const srb_flag_t srb_flag, - const int rb_id, const mui_t mui, const confirm_t confirm, const sdu_size_t sdu_buffer_size, diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 9be1873612..aca2fcbb32 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -1244,7 +1244,6 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st if (!uedata.callBackSDAP(&ctxt, uedata.ue_id, srb_flag, - rb_id, mui, confirm, sdu_buffer_size, diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index a0d1580cf9..c31f8c935e 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -49,7 +49,6 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; typedef bool (*gtpCallbackSDAP)(protocol_ctxt_t *ctxt_pP, const ue_id_t ue_id, const srb_flag_t srb_flagP, - const int rb_idP, const mui_t muiP, const confirm_t confirmP, const sdu_size_t sdu_buffer_sizeP, diff --git a/tests/nr-cuup/nr-cuup-load-test.c b/tests/nr-cuup/nr-cuup-load-test.c index 1af262e146..533b119c9e 100644 --- a/tests/nr-cuup/nr-cuup-load-test.c +++ b/tests/nr-cuup/nr-cuup-load-test.c @@ -165,7 +165,6 @@ static struct ue_stat { static bool recv_ng(protocol_ctxt_t *ctxt, const ue_id_t ue_id, const srb_flag_t flag, - const int rb, const mui_t mui, const confirm_t confirm, const sdu_size_t size, From 04a1acf3767ff4bce0b0cad176a3bf927fca279e Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 21 Oct 2025 16:56:22 +0200 Subject: [PATCH 059/275] PDCP: rename add_drb to nr_pdcp_add_drb --- doc/SW_archi.md | 2 +- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 2 +- openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c | 10 +++++----- openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h | 10 +++++----- openair2/RRC/NR/rrc_gNB_nsa.c | 2 +- openair2/RRC/NR_UE/rrc_UE.c | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/SW_archi.md b/doc/SW_archi.md index 5b902a6675..c47a3cbc91 100644 --- a/doc/SW_archi.md +++ b/doc/SW_archi.md @@ -325,7 +325,7 @@ The PDCP implementation is secured by a general mutex, akin to the design of the Initialization of the PDCP layer follows a structure similar to that of the RLC layer. The function `nr_pdcp_layer_init()` initializes PDCP, while a second initialization function, `pdcp_module_init()`, must also be invoked. -To manage UE connections, `nr_pdcp_add_srbs()` is employed for adding UE SRBs in PDCP, while `nr_pdcp_remove_UE()` is used for their removal. Similarly, `add_drb()` adds UE DRB in PDCP, with `nr_pdcp_remove_UE()` handling their removal. +To manage UE connections, `nr_pdcp_add_srbs()` is employed for adding UE SRBs in PDCP, while `nr_pdcp_remove_UE()` is used for their removal. Similarly, `nr_pdcp_add_drb()` adds UE DRB in PDCP, with `nr_pdcp_remove_UE()` handling their removal. ## PDCP Tx flow diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index f29c26842b..4d81733380 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -155,7 +155,7 @@ static void e1_add_bearers(const int ue_id, const NR_DRB_ToAddModList_t *addMod, // add SDAP entity new_nr_sdap_entity(GNB_FLAG_YES, ue_id, sdap); // add PDCP entity - add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp); + nr_pdcp_add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp); } } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index 8f2bf0a043..94d44fe8ce 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -575,11 +575,11 @@ void add_srb(int is_gnb, nr_pdcp_manager_unlock(nr_pdcp_ue_manager); } -void add_drb(int is_gnb, - const ue_id_t UEid, - const NR_PDCP_Config_t *pdcp, - sdap_config_t *sdap, - const nr_pdcp_entity_security_keys_and_algos_t *security_parameters) +void nr_pdcp_add_drb(int is_gnb, + const ue_id_t UEid, + const NR_PDCP_Config_t *pdcp, + const sdap_config_t *sdap, + const nr_pdcp_entity_security_keys_and_algos_t *security_parameters) { nr_pdcp_ue_t *ue; diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h index 75f41e2190..884ea11f60 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h @@ -48,11 +48,11 @@ void nr_pdcp_add_srbs(eNB_flag_t enb_flag, NR_SRB_ToAddModList_t *const srb2add_list, const nr_pdcp_entity_security_keys_and_algos_t *security_parameters); -void add_drb(int is_gnb, - const ue_id_t UEid, - const NR_PDCP_Config_t *pdcp, - struct sdap_configuration_s *sdap, - const nr_pdcp_entity_security_keys_and_algos_t *security_parameters); +void nr_pdcp_add_drb(int is_gnb, + const ue_id_t UEid, + const NR_PDCP_Config_t *pdcp, + const struct sdap_configuration_s *sdap, + const nr_pdcp_entity_security_keys_and_algos_t *security_parameters); void nr_pdcp_remove_UE(ue_id_t ue_id); void nr_pdcp_reestablishment(ue_id_t ue_id, diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index b6bf34729c..0c32a446ce 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -179,7 +179,7 @@ static void rrc_nsa_add_drb(const int ue_id, // add SDAP entity (terminated at gNB, since it's EPC) new_nr_sdap_entity(GNB_FLAG_YES, ue_id, sdap); // add PDCP entity - add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp); + nr_pdcp_add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp); } } diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 81bd3b65bb..b852fad206 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -621,7 +621,7 @@ static void rrc_ue_add_bearer(const int ue_id, const NR_DRB_ToAddMod_t *drb, con // add SDAP entity new_nr_sdap_entity(GNB_FLAG_NO, ue_id, sdap); // add PDCP entity - add_drb(GNB_FLAG_NO, ue_id, drb->pdcp_Config, &sdap, sp); + nr_pdcp_add_drb(GNB_FLAG_NO, ue_id, drb->pdcp_Config, &sdap, sp); } /** From 27be4e26a910bb6e4998b2229a8f29434fa33be8 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 21 Oct 2025 18:02:41 +0200 Subject: [PATCH 060/275] refactor: improve SDAP entity add/update flow management This commit refactors the SDAP entity QoS flow to DRB mapping functionality to improve code organization and enhance maintainability. Key changes: - Split nr_sdap_qfi2drb_map_update into focused functions: * nr_sdap_add_qos_flows_to_drb: handles QFI addition to DRB * nr_sdap_rm_qos_flows_from_drb: handles QFI removal from DRB * nr_sdap_ue_control_pdu_config: handles UE control PDU generation - Consolidate add/modify logic into reusable nr_sdap_addmod_entity function - Remove entity existence checking and updating from new_nr_sdap_entity: this function should only add SDAP entities - Improve function separation of concerns: do SDAP entity add or update separately and improve modularity by splitting operations in self contained functions - At SDAP creation, do only add of QFI to DRB mapping - rename new_nr_sdap_entity to nr_sdap_add_entity for function naming consinstency --- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 2 +- openair2/RRC/NR/rrc_gNB_nsa.c | 2 +- openair2/RRC/NR_UE/rrc_UE.c | 2 +- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 115 ++++++++++---------- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 9 +- 5 files changed, 61 insertions(+), 69 deletions(-) diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index 4d81733380..ed9cc37c18 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -153,7 +153,7 @@ static void e1_add_bearers(const int ue_id, const NR_DRB_ToAddModList_t *addMod, // get SDAP config sdap_config_t sdap = nr_sdap_get_config(GNB_FLAG_YES, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity); // add SDAP entity - new_nr_sdap_entity(GNB_FLAG_YES, ue_id, sdap); + nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, sdap); // add PDCP entity nr_pdcp_add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp); } diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index 0c32a446ce..996dc3528e 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -177,7 +177,7 @@ static void rrc_nsa_add_drb(const int ue_id, sdap.drb_id = drb->drb_Identity; sdap.defaultDRB = true; // add SDAP entity (terminated at gNB, since it's EPC) - new_nr_sdap_entity(GNB_FLAG_YES, ue_id, sdap); + nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, sdap); // add PDCP entity nr_pdcp_add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp); } diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index b852fad206..6681eed855 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -619,7 +619,7 @@ static void rrc_ue_add_bearer(const int ue_id, const NR_DRB_ToAddMod_t *drb, con sdap = nr_sdap_get_config(GNB_FLAG_NO, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity); } // add SDAP entity - new_nr_sdap_entity(GNB_FLAG_NO, ue_id, sdap); + nr_sdap_addmod_entity(GNB_FLAG_NO, ue_id, sdap); // add PDCP entity nr_pdcp_add_drb(GNB_FLAG_NO, ue_id, drb->pdcp_Config, &sdap, sp); } diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 438e05dd60..f0e791c683 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -331,27 +331,6 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, } } -/** @brief Update QFI to DRB mapping rules - * @param qfi the QoS Flow index, used as unique index of the qfi2drb mapping table - * @param drb the DRB ID to be mapped */ -static void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, const sdap_config_t *sdap) -{ - for (int i = 0; i < sdap->mappedQFIs2AddCount; i++) { - uint8_t qfi = sdap->mappedQFIs2Add[i]; - LOG_D(SDAP, "Updating QFI to DRB mapping rules: %d mapped QFIs for DRB %d\n", sdap->mappedQFIs2AddCount, sdap->drb_id); - if (qfi < SDAP_MAX_QFI && qfi > SDAP_MAP_RULE_EMPTY && sdap->drb_id > 0 && sdap->drb_id <= MAX_DRBS_PER_UE) { - entity->qfi2drb_map_add(entity, qfi, sdap->drb_id, sdap->role); - } else { - LOG_E(SDAP, "Failed to update qfi2drb mapping: QFI=%d, DRB=%d\n", qfi, sdap->drb_id); - } - } - for (int i = 0; i < sdap->mappedQFIs2ReleaseCount; i++) { - uint8_t qfi = sdap->mappedQFIs2Release[i]; - LOG_D(SDAP, "Deelting QFI to DRB mapping rules: QFI=%d for DRB=%d\n", qfi, sdap->drb_id); - entity->qfi2drb_map_delete(entity, qfi); - } -} - static void nr_sdap_qfi2drb_map_add(nr_sdap_entity_t *entity, const uint8_t qfi, const uint8_t drb_id, @@ -437,13 +416,12 @@ void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hd } /** @brief UL QoS flow to DRB mapping configuration for a SDAP entity has already been established - * according to TS 37.324, 5.3 QoS flow to DRB Mapping, 5.3.1 Configuration Procedures. - * The function handles both UL QoS Flows mapping rules to add and to remove. */ -static void nr_sdap_ue_qfi2drb_config(nr_sdap_entity_t *entity, const ue_id_t ue_id, const sdap_config_t sdap) + * according to TS 37.324, 5.3 QoS flow to DRB Mapping, 5.3.1 Configuration Procedures. */ +static void nr_sdap_ue_control_pdu_config(nr_sdap_entity_t *entity, const ue_id_t ue_id, const sdap_config_t *sdap) { // handle QFIs to DRB mapping rule to add - for (int i = 0; i < sdap.mappedQFIs2AddCount; i++) { - uint8_t qfi = sdap.mappedQFIs2Add[i]; + for (int i = 0; i < sdap->mappedQFIs2AddCount; i++) { + uint8_t qfi = sdap->mappedQFIs2Add[i]; /* a default DRB exists and there is no stored QFI to DRB mapping rule for the QFI */ if (entity->default_drb && entity->qfi2drb_table[qfi].drb_id == SDAP_NO_MAPPING_RULE) { // construct an end-marker control PDU (6.2.3 TS 37.324) @@ -455,7 +433,7 @@ static void nr_sdap_ue_qfi2drb_config(nr_sdap_entity_t *entity, const ue_id_t ue } /* the stored UL QFI to DRB mapping rule is different from the configured one and has UL SDAP header */ bool ul_sdap_header = (entity->qfi2drb_table[qfi].entity_role & SDAP_UL_TX) != 0; - if (entity->qfi2drb_table[qfi].drb_id != sdap.drb_id && ul_sdap_header) { + if (entity->qfi2drb_table[qfi].drb_id != sdap->drb_id && ul_sdap_header) { // construct an end-marker control PDU (6.2.3 TS 37.324) nr_sdap_ul_hdr_t sdap_ctrl_pdu = entity->sdap_construct_ctrl_pdu(qfi); // map the end-marker control PDU to the DRB according to the stored QoS flow to DRB mapping rule @@ -464,15 +442,42 @@ static void nr_sdap_ue_qfi2drb_config(nr_sdap_entity_t *entity, const ue_id_t ue entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu); } } +} - // handle QFIs to DRB mapping rule to release - for (int i = 0; i < sdap.mappedQFIs2ReleaseCount; i++) { - entity->qfi2drb_map_delete(entity, sdap.mappedQFIs2Release[i]); +/** @brief Add QoS flows to a DRB */ +static void nr_sdap_add_qos_flows_to_drb(nr_sdap_entity_t *entity, const sdap_config_t *sdap) +{ + for (int i = 0; i < sdap->mappedQFIs2AddCount; i++) { + uint8_t qfi = sdap->mappedQFIs2Add[i]; + LOG_D(SDAP, "Adding QFI to DRB mapping rules: %d mapped QFIs for DRB %d\n", sdap->mappedQFIs2AddCount, sdap->drb_id); + if (qfi < SDAP_MAX_QFI && qfi > SDAP_MAP_RULE_EMPTY && sdap->drb_id > 0 && sdap->drb_id <= MAX_DRBS_PER_UE) { + entity->qfi2drb_map_add(entity, qfi, sdap->drb_id, sdap->role); + } else { + LOG_E(SDAP, "Failed to add qfi2drb mapping: QFI=%d, DRB=%d\n", qfi, sdap->drb_id); + } } +} - // store QFI to DRB mapping rules - LOG_D(SDAP, "Storing the configured QoS flow to DRB mapping rule\n"); - entity->qfi2drb_map_update(entity, &sdap); +/** @brief Remove QoS flows from a DRB */ +static void nr_sdap_rm_qos_flows_from_drb(nr_sdap_entity_t *entity, const sdap_config_t *sdap) +{ + for (int i = 0; i < sdap->mappedQFIs2ReleaseCount; i++) { + uint8_t qfi = sdap->mappedQFIs2Release[i]; + LOG_D(SDAP, "Removing QFI from DRB mapping rules: QFI=%d for DRB=%d\n", qfi, sdap->drb_id); + entity->qfi2drb_map_delete(entity, qfi); + } +} + +/** @brief Update QFI to DRB mapping rules + * @param qfi the QoS Flow index, used as unique index of the qfi2drb mapping table + * @param drb the DRB ID to be mapped */ +static void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, const sdap_config_t *sdap) +{ + if (!entity->is_gnb) { // UE control PDU configuration + nr_sdap_ue_control_pdu_config(entity, entity->ue_id, sdap); + } + nr_sdap_add_qos_flows_to_drb(entity, sdap); + nr_sdap_rm_qos_flows_from_drb(entity, sdap); } /** @@ -480,31 +485,11 @@ static void nr_sdap_ue_qfi2drb_config(nr_sdap_entity_t *entity, const ue_id_t ue * @note there is one SDAP entity per PDU session * * @param is_gnb, indicates whether it is for gNB or UE - * @param has_sdap_rx, indicates whether it is a receiving SDAP entity - * @param has_sdap_tx, indicates whether it is a transmitting SDAP entity * @param ue_id, UE ID - * @param pdusession_id, PDU session ID - * @param is_defaultDRB, indicates whether the entity has a default DRB - * @param mapped_qfi_2_add, list of QoS flows to add/update - * @param mappedQFIs2AddCount, number of QoS flows to add/update - */ -nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap) + * @param sdap, SDAP configuration */ +static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap) { - /* check whether the SDAP entity already exists and - update QFI to DRB mapping rules in that case */ - nr_sdap_entity_t *sdap_entity = nr_sdap_get_entity(ue_id, sdap.pdusession_id); - if (sdap_entity) { - LOG_E(SDAP, "SDAP Entity for UE already exists with RNTI/UE ID: %lu and PDU SESSION ID: %d\n", ue_id, sdap.pdusession_id); - if (!is_gnb) { - nr_sdap_ue_qfi2drb_config(sdap_entity, ue_id, sdap); - } else { - // store QFI to DRB mapping rules - sdap_entity->qfi2drb_map_update(sdap_entity, &sdap); - } - return sdap_entity; - } - - sdap_entity = calloc_or_fail(1, sizeof(*sdap_entity)); + nr_sdap_entity_t *sdap_entity = calloc_or_fail(1, sizeof(*sdap_entity)); // SDAP entity ids sdap_entity->ue_id = ue_id; @@ -533,8 +518,8 @@ nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, cons LOG_I(SDAP, "Default DRB for the created SDAP entity: DRB %d \n", sdap_entity->default_drb); } - // store QFI to DRB mapping rules - sdap_entity->qfi2drb_map_update(sdap_entity, &sdap); + // Add QoS flows to the DRB (initial configuration) + nr_sdap_add_qos_flows_to_drb(sdap_entity, &sdap); // update SDAP entity list pointers sdap_entity->next_entity = sdap_info.sdap_entity_llist; @@ -545,7 +530,17 @@ nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, cons // PDCP SDUs to/from the TUN interface. start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id); } - return sdap_entity; +} + +/** @brief Add or modify an SDAP entity if it already exists */ +void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap) +{ + nr_sdap_entity_t *sdap_entity = nr_sdap_get_entity(ue_id, sdap.pdusession_id); + if (sdap_entity) { + sdap_entity->qfi2drb_map_update(sdap_entity, &sdap); + } else { + nr_sdap_add_entity(is_gnb, ue_id, sdap); + } } /** @@ -717,7 +712,7 @@ void nr_reconfigure_sdap_entity(NR_SDAP_Config_t *sdap_config, ue_id_t ue_id, in pdusession_id); /* QFI to DRB mapping */ sdap_config_t sdap = nr_sdap_get_config(is_gnb, sdap_config, drb_id); - nr_sdap_ue_qfi2drb_config(sdap_entity, ue_id, sdap); + sdap_entity->qfi2drb_map_update(sdap_entity, &sdap); } void set_qfi(uint8_t qfi, uint8_t pduid, ue_id_t ue_id) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index a5ac2953cf..a206189035 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -164,12 +164,6 @@ int nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, int map_type, uint8_t dl_qfi) */ void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu); -/* - * TS 37.324 4.4 5.1.1 SDAP entity establishment - * Establish an SDAP entity. - */ -nr_sdap_entity_t *new_nr_sdap_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap); - /* Entity Handling Related Functions */ nr_sdap_entity_t *nr_sdap_get_entity(ue_id_t ue_id, int pdusession_id); @@ -177,6 +171,9 @@ sdap_config_t nr_sdap_get_config(const int is_gnb, const NR_SDAP_Config_t *sdap_ void nr_sdap_release_drb(ue_id_t ue_id, int drb_id, int pdusession_id); +/** @brief Add or modify an SDAP entity based on whether it already exists */ +void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap); + /** * @brief Function to delete a single SDAP Entity based on the ue_id and pdusession_id. * @note 1. SDAP entities may have the same ue_id. From 110f5c73049641b9175185b8e301c406d3fd8de0 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 13 Oct 2025 09:32:42 +0200 Subject: [PATCH 061/275] Updates for Feature Set --- doc/FEATURE_SET.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index 4429cad56b..b0f85405be 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -31,7 +31,7 @@ The following features are valid for the gNB and the 5G-NR UE. * Subcarrier spacings: 15 and 30kHz (FR1), 120kHz (FR2) * Bandwidths: 10, 20, 40, 60, 80, 100MHz * Intermediate downlink and uplink frequencies to interface with IF equipment -* Procedures for 2-layer DL and UL MIMO +* Procedures for 4-layer DL and 2-layer UL SU-MIMO * Slot format: 14 OFDM symbols in UL or DL * Highly efficient 3GPP compliant LDPC encoder and decoder (BG1 and BG2 supported) * Highly efficient 3GPP compliant polar encoder and decoder @@ -47,7 +47,7 @@ These modes of operation are supported: * "noS1" mode (DL and UL, gNB, nrUE): - Connection setup stops after RA; RRC configuration is exchanged through files - - Creates TUN interface to PDCP to inject and receive user-place traffic + - Creates TUN interface to SDAP to inject and receive user-place traffic - No connection to the core network * Standalone (SA) mode (gNB, nrUE): - UE can register with the 5G Core Network through the gNB, establish a PDU @@ -73,7 +73,7 @@ These modes of operation are supported: - Single and multiple DMRS symbols - PTRS support - Support for up to 4 TX antennas - - Support for up to 2 layers + - Support for up to 4 layers - Support for 256 QAM * NR-CSIRS Generation of sequence at PHY * NR-PUSCH (including Segmentation, LDPC encoding, rate matching, scrambling, modulation, RB mapping, etc). @@ -104,7 +104,9 @@ These modes of operation are supported: ## gNB MAC - MAC -> PHY configuration using NR FAPI P5 interface -- MAC <-> PHY data interface using FAPI P7 interface for BCH PDU, DCI PDU, PDSCH PDU +- MAC <-> PHY data interface using FAPI P7 interface for DL_TTI.request, + UL_TTI.request, UL_DCI.request, TX_data.request, RX_Data.indication, + CRC.indication, UCI.indication, SRS.indication - Generation of and scheduler procedures for MIB/SIB1 - Scheduler procedures for RA - 4-Step RA @@ -125,7 +127,7 @@ These modes of operation are supported: - regular scheduler with dynamic proportionally-fair allocation - MCS adaptation from HARQ BLER - MAC header generation (including timing advance) -- ACK / NACK handling and HARQ procedures for downlink +- ACK/NACK handling and HARQ procedures for downlink - MAC uplink scheduler - phy-test scheduler (fixed allocation) - regular scheduler with dynamic proportionally-fair allocation @@ -144,6 +146,9 @@ These modes of operation are supported: neighbors include cells operating on different frequencies - DUs must be synchronized with each other for the measurements to be properly performed - Initial support for RedCap + - RedCap SIB1 v17 IEs supported + - Coexistence of RedCap and Normal UEs + - Handling of RedCap capability for small PDCP/RLC SN size - Scheduling of SIBs (2, 19) ## gNB RLC @@ -180,7 +185,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) -- RRC can configure PDCP, RLC, MAC +- 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 - RRCSetupRequest/RRCSetup/RRCSetupComplete @@ -190,8 +195,10 @@ These modes of operation are supported: - Support for MasterCellGroup configuration (from DU) - Interface with NGAP for the interactions with the AMF - Interface with F1AP for CU/DU split deployment option - - Periodic RRC measurements of serving cell (no A/B events) -- Initial support for RedCap + - Interface with E1AP for CU-CP/CU-CP split deployment option + - Periodic RRC measurements of serving/neighbour cells and A2/A3 event + handling +- Initial support for RedCap (see MAC) ## gNB X2AP @@ -226,6 +233,7 @@ These modes of operation are supported: * F1 UE Context modification required * F1 UE Context release req/cmd/complete - F1 gNB CU configuration update + - F1 gNB DU configuration update - F1 Reset (handled at DU only, full reset only) - Interface with RRC - Interface with GTP-u (tunnel creation/handling for F1-U interface) @@ -245,7 +253,8 @@ These modes of operation are supported: - Bearer Context Modification (gNB-CU-CP initiated) - E1 Bearer Context Modification Request - E1 Bearer Context Modification Response -- Interface with RRC and PDCP + - E1 Reset +- Interface with RRC and PDCP/SDAP - One CU-CP can handle multiple CU-UPs ## gNB GTP-U From 66d8ada9827f729e0fa775dc19494b07be14949d Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 13 Oct 2025 10:54:54 +0200 Subject: [PATCH 062/275] Update UE feature set --- doc/FEATURE_SET.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index b0f85405be..1b095931e2 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -307,6 +307,7 @@ These modes of operation are supported: - Support for 256 QAM - Support for up to 2 TX antenna - Support for up to 2 layers + - Support for UCI on PUSCH * NR-PUCCH - Format 0 (2 bits for ACK/NACK and SR) - Format 2 (mainly for CSI feedback) @@ -339,6 +340,8 @@ These modes of operation are supported: * Minimum system information (MSI) - MIB processing - Scheduling of system information block 1 (SIB1) reception +* Other system information + - Scheduling of other system information blocks reception * Random access procedure (needs improvement, there is still not a clear separation between MAC and PHY) - Mapping SSBs to multiple ROs - Scheduling of PRACH @@ -407,8 +410,11 @@ These modes of operation are supported: * Integration of RRC messages and procedures supporting UE 5G SA connection according to 38.331 Rel.16 - RRCSetupRequest/RRCSetup/RRCSetupComplete + - RRCReject + - RRCRelease/ RRC going to IDLE - RRC Uplink/Downlink Information transfer carrying NAS messages transparently - RRC Reconfiguration/Reconfiguration complete + - RRCSetup fallback (after reestablishment) - RRCReestablishmentRequest/RRC Reestablishment/Reestablishment complete - Support for master cell group configuration - Reception of UECapabilityEnquiry, encoding and transmission of UECapability From 57e382ccd1a87d09424895bd4c434b023e120ffa Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 23 Oct 2025 10:37:49 +0200 Subject: [PATCH 063/275] Retroactively add release notes for v2.3.0 I forgot to add this when making the release. Put it retroactively to have things "in order". See also: https://gitlab.eurecom.fr/oai/openairinterface5g/-/tags/v2.3.0 https://gitlab.eurecom.fr/oai/openairinterface5g/-/releases/v2.3.0 --- CHANGELOG.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df218b36d..a9b355d079 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,51 @@ # RELEASE NOTES: # +## [v2.3.0](https://gitlab.eurecom.fr/oai/openairinterface5g/-/tags/v2.3.0) -> July 2025. ## + +General new features and improvements (both RAN and UE): +- Preliminary support for RedCap UEs +- Support for NTN LEO scenarios including SIB19 and channel emulation (see + doc/RUNMODEM.md) +- VRTSIM: new shared memory realtime radio (see develop/radio/vrtsim/README.md) +- Support for TDD pattern 2 +- Support for long PRACH +- Support for SIB2 +- Add unit tests using ctest (see doc/UnitTests.md) + +Stability and bug fixes: +- Improve various procedures (e.g., RRC reestablishment, RRC Reject, ...) +- Add improvements for ImScope +- Bugfixes in the entire stack (e.g., #803, #805, #810, #812, #818, #852, #878, + #884, #890, #895, #901, #907, #932, #939 ...) +- Documentation updates +- And many other small fixes, like memory leaks, crashes, ... + +RAN changes (gNB/CU/CU-CP/CU-UP/DU/DU-high/DU-low): +- Stability and performance improvements when running with 4-layer DL MIMO +- Stability and performance improvements when running with 2-layer UL MIMO +- Interoperability testing with Metanoia Jura and Foxconn RUs +- Support of O-RAN 7.2 M-plane (Start-up procedure including configuration + management, see also doc/ORAN_FHI7.2_Tutorial.md) +- Upgrade OSC FHI (xran) from E to F release +- Refactoring of LDPC interface and performance improvements +- Support for nFAPI in all scenarios for FR1 (see doc/nfapi.md) +- Support at least 32 UEs concurrently +- Improve robustness when running in split modes (F1, E1) +- Add basic support for beam management in FR2 +- Add support for O1 on DU (see also https://gitlab.eurecom.fr/oai/o1-adapter) +- E2 agent: Support for multiple E2SM-RC report service styles + +nrUE changes: +- Refactoring of threading towards better code and performance +- Add or rework various procedures (e.g., RA with C-RNTI, Going to Idle, RA, ...) +- Refactor and extend NAS implementation (e.g., Service Request, Registration + Request, Service Acept, Service Reject, ...) +- Support for continuous frequency offset compensation +- Reduce minimum RX-to-TX time to 3 + +Regression: +- Multiple BWPs do not work reliably on gNB; use tag 2025.w17 + ## [v2.2.0](https://gitlab.eurecom.fr/oai/openairinterface5g/-/tags/v2.2.0) -> November 2024. ## General 5G improvements (both gNB and UE): From 23c115dc74bfaba3ced8b771897137d0bc6b5742 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 23 Oct 2025 15:54:23 +0200 Subject: [PATCH 064/275] Update NAS and mobility in Feature Set --- doc/FEATURE_SET.md | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index 1b095931e2..4551bc1ff2 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -198,6 +198,13 @@ These modes of operation are supported: - Interface with E1AP for CU-CP/CU-CP split deployment option - Periodic RRC measurements of serving/neighbour cells and A2/A3 event handling +- RRC Mobility Management Procedures: + - Inter-DU Handover (F1-based handover within same CU) + - Inter-gNB Handover (N2-based handover between different gNBs) + - Handover Preparation Information generation and processing + - Handover Command generation and processing + - PDCP Status transfer procedures + - Support for handover decision triggers (A3 events, manual triggers) - Initial support for RedCap (see MAC) ## gNB X2AP @@ -216,6 +223,16 @@ These modes of operation are supported: - NGAP UE context release request/complete - NGAP UE radio capability info indication - NGAP PDU session resource setup request/response + - NGAP Mobility Management Procedures: + * NGAP Handover Required + * NGAP Handover Request + * NGAP Handover Request Acknowledge + * NGAP Handover Command + * NGAP Handover Notify + * NGAP Handover Cancel + * NGAP Handover Cancel Acknowledge + * NGAP Uplink RAN Status Transfer + * NGAP Downlink RAN Status Transfer - Interface with RRC ## gNB F1AP @@ -235,6 +252,8 @@ These modes of operation are supported: - F1 gNB CU configuration update - F1 gNB DU configuration update - F1 Reset (handled at DU only, full reset only) + - F1 Mobility Management Procedures: + * F1 Intra-CU Handover (Inter-DU mobility) - Interface with RRC - Interface with GTP-u (tunnel creation/handling for F1-U interface) - One CU(-CP) can handle multiple DUs @@ -421,17 +440,27 @@ These modes of operation are supported: * Interface with PDCP: configuration, DCCH and CCCH message handling * Interface with RLC and MAC for configuration -## UE NAS +## UE 5G NAS -* Transfer of NAS messages between the AMF and the UE supporting the UE registration with the core network and the PDU session establishment according to 24.501 Rel.16 +* Transfer of NAS messages between the AMF and the UE supporting the UE registration with the core network and the PDU session establishment according to 24.501 Rel.16 +* 5GMM (5G Mobility Management) messages: + - Service Request/Accept/Reject (enc/dec library only) - Identity Request/Response - Authentication Request/Response - Security Mode Command/Complete - Registration Request/Accept/Complete + - Deregistration Request (UE originating) + - Uplink NAS Transport +* 5GSM (5G Session Management) messages: - PDU Session Establishment Request/Accept - - NAS configuration and basic interfacing with RRC - +* Security Features: + - NAS message integrity protection and ciphering + - Security context establishment and management +* Integration: + - NAS configuration and interfacing with RRC + - Integration with SDAP for user plane data transfer +For detailed implementation status, encoding/decoding support, and unit test coverage, see [5G NAS Implementation Documentation](5Gnas.md). # OpenAirInterface 4G LTE eNB Feature Set # From a898c18ddcbdae4a3ad6026dc6bc31e5741fdc95 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 24 Oct 2025 10:43:25 +0200 Subject: [PATCH 065/275] Update NTN in Feature Set --- doc/FEATURE_SET.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index 4551bc1ff2..df1cb60af4 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -125,13 +125,14 @@ These modes of operation are supported: - MAC downlink scheduler - phy-test scheduler (fixed allocation and usable also without UE) - regular scheduler with dynamic proportionally-fair allocation - - MCS adaptation from HARQ BLER + - MCS adaptation from HARQ BLER or SSB-SINR report - MAC header generation (including timing advance) - ACK/NACK handling and HARQ procedures for downlink - MAC uplink scheduler - phy-test scheduler (fixed allocation) - regular scheduler with dynamic proportionally-fair allocation - HARQ procedures for uplink + - MCS adaption from HARQ BLER or PUSCH SINR - Scheduler procedures for SRS reception - Periodic SRS reception - Channel rank computation up to 2x2 scenario @@ -149,7 +150,12 @@ 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 SIBs (2, 19) +- Scheduling of other SIBs (2, 19) +- NTN + - Support downlinkHARQ-FeedbackDisabled-r17 + - Support for 32 PDSCH and PUSCH HARQ processes per UE + - Consider ntn-Config-r17.cellSpecificKoffset-r17 in scheduling + - Function-based interface for ntn-Config-r17 updates (used by NTN-LEO RFsimulator) ## gNB RLC @@ -296,7 +302,8 @@ These modes of operation are supported: - non-blind synchronization (information required: carrier frequency, bandwidth, numerology) - option to search SSB inside the bandwidth available * Time tracking based on PBCH DMRS -* Frequency offset estimation based on PSS and SSS +* Initial Frequency offset estimation based on PSS and SSS +* Continuous Frequency offset estimation and compensation based on PBCH DMRS * 15kHz and 30kHz SCS for FR1 and 120 kHz SCS for FR2 * Reception of NR-PSS/NR-SSS * NR-PBCH supports multiple SSBs and flexible periodicity @@ -342,6 +349,10 @@ These modes of operation are supported: - Support for multiple gNB reception with gNBs synced via GPSDO * NR-PRACH - Formats 0,1,2,3, A1-A3, B1-B3 +* NTN + - TA adjustemt based on ntn-Config-r17 information + - Autonomous TA adjustemt between SIB19 receptions based on DL time tracking + - UL Doppler pre-compensation based on continuous DL FO estimation * Highly efficient 3GPP compliant LDPC encoder and decoder (BG1 and BG2 are supported) * Highly efficient 3GPP compliant polar encoder and decoder * Encoder and decoder for short block @@ -397,6 +408,10 @@ These modes of operation are supported: - Periodic and aperiodic SRS transmission * Bandwidth part (BWP) operation - Operation in configured dedicated BWP through RRCSetup or RRCReconfiguration +* NTN + - Support downlinkHARQ-FeedbackDisabled-r17 + - Support for 32 PDSCH and PUSCH HARQ processes + - Consider ntn-Config-r17.cellSpecificKoffset-r17 in scheduling ## UE RLC @@ -437,6 +452,9 @@ These modes of operation are supported: - RRCReestablishmentRequest/RRC Reestablishment/Reestablishment complete - Support for master cell group configuration - Reception of UECapabilityEnquiry, encoding and transmission of UECapability +* NTN according to 38.331 Rel.17 + - Reception of ntn-Config-r17 from SIB19 or reconfigurationWithSync + - Handling of ntn-UlSyncValidityDuration-r17 in SIB19 * Interface with PDCP: configuration, DCCH and CCCH message handling * Interface with RLC and MAC for configuration From 41ce5954612245e4e1e25066beb3195d1dacca83 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 24 Oct 2025 15:11:25 +0200 Subject: [PATCH 066/275] Update supported bandwiths --- doc/FEATURE_SET.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index df1cb60af4..e3876457e6 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -29,7 +29,7 @@ The following features are valid for the gNB and the 5G-NR UE. * Static FDD * Normal CP * Subcarrier spacings: 15 and 30kHz (FR1), 120kHz (FR2) -* Bandwidths: 10, 20, 40, 60, 80, 100MHz +* Bandwidths: 5-100MHz (FR1), 100 and 200MHz (FR2) * Intermediate downlink and uplink frequencies to interface with IF equipment * Procedures for 4-layer DL and 2-layer UL SU-MIMO * Slot format: 14 OFDM symbols in UL or DL From f2389987d4609336ded549da5e688fa0f556e0ce Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 24 Oct 2025 11:33:49 +0200 Subject: [PATCH 067/275] improvements in formatting and LOGs in DMRS common functions at MAC --- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 89 +++++++++++-------- 1 file changed, 54 insertions(+), 35 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 1a5431bdd2..3b26bf0a35 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2246,7 +2246,6 @@ int32_t get_l_prime(uint8_t duration_in_symbols, // For PDSCH Mapping TypeA, ld is duration between first OFDM of the slot and last OFDM symbol of the scheduled PUSCH resources // For TypeB, ld is the duration of the scheduled PUSCH resources uint8_t ld = (mapping_type == typeA) ? (duration_in_symbols + start_symbol) : duration_in_symbols; - uint8_t l0 = (dmrs_typeA_position == NR_MIB__dmrs_TypeA_Position_pos2) ? 2 : 3 ; uint8_t colomn = additional_pos; @@ -2260,20 +2259,27 @@ int32_t get_l_prime(uint8_t duration_in_symbols, row = ld - 3; uint32_t l_prime; + int l0 = (dmrs_typeA_position == NR_MIB__dmrs_TypeA_Position_pos2) ? 2 : 3; + int l0_shift; if (pusch_maxLength == pusch_len1) { l_prime = table_6_4_1_1_3_3_pusch_dmrs_positions_l[row][colomn]; - l0 = 1 << l0; - } - else { + l0_shift = 1 << l0; + } else { l_prime = table_6_4_1_1_3_4_pusch_dmrs_positions_l[row][colomn]; - l0 = 1<= 0, "invalid l_prime < 0\n"); - LOG_D(NR_MAC, "PUSCH - l0:%d, ld:%d,row:%d, column:%d, addpos:%d, maxlen:%d\n", l0, ld, row, colomn, additional_pos, pusch_maxLength); - AssertFatal(l_prime>=0,"invalid l_prime < 0\n"); - - l_prime = (mapping_type == typeA) ? (l_prime | l0) : (l_prime << start_symbol); - LOG_D(MAC, " PUSCH DMRS MASK in HEX:%x\n", l_prime); + l_prime = (mapping_type == typeA) ? (l_prime | l0_shift) : (l_prime << start_symbol); + LOG_D(NR_MAC, + "PUSCH - l0:%d, ld:%d, row:%d, column:%d, addpos:%d, maxlen:%d DMRS MASK in HEX:%x\n", + (mapping_type == typeA) ? l0 : 0, + ld, + row, + colomn, + additional_pos, + pusch_maxLength, + l_prime); return l_prime; } @@ -3163,13 +3169,21 @@ int16_t fill_dmrs_mask(const NR_PDSCH_Config_t *pdsch_Config, int dmrs_AdditionalPosition = 0; NR_DMRS_DownlinkConfig_t *dmrs_config = NULL; - LOG_D(MAC, "NrofSymbols:%d, startSymbol:%d, mappingtype:%d, dmrs_TypeA_Position:%d\n", NrOfSymbols, startSymbol, mappingtype, dmrs_TypeA_Position); + LOG_D(NR_MAC, + "NrofSymbols:%d, startSymbol:%d, mappingtype:%d, dmrs_TypeA_Position:%d\n", + NrOfSymbols, + startSymbol, + mappingtype, + dmrs_TypeA_Position); int l0 = 0; // type B if (mappingtype == typeA) { - if (dmrs_TypeA_Position == NR_ServingCellConfigCommon__dmrs_TypeA_Position_pos2) l0 = 2; - else if (dmrs_TypeA_Position == NR_ServingCellConfigCommon__dmrs_TypeA_Position_pos3) l0 = 3; - else AssertFatal(1==0,"Illegal dmrs_TypeA_Position %d\n",(int)dmrs_TypeA_Position); + if (dmrs_TypeA_Position == NR_ServingCellConfigCommon__dmrs_TypeA_Position_pos2) + l0 = 2; + else if (dmrs_TypeA_Position == NR_ServingCellConfigCommon__dmrs_TypeA_Position_pos3) + l0 = 3; + else + AssertFatal(false, "Illegal dmrs_TypeA_Position %d\n", (int)dmrs_TypeA_Position); } // in case of DCI FORMAT 1_0 or dedicated pdsch config not received additionposition = pos2, len1 should be used // referred to section 5.1.6.2 in 38.214 @@ -3177,14 +3191,16 @@ int16_t fill_dmrs_mask(const NR_PDSCH_Config_t *pdsch_Config, if (pdsch_Config != NULL) { if (mappingtype == typeA) { // Type A - if (dci_format != NR_DL_DCI_FORMAT_1_0 && - pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA && pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->present == NR_SetupRelease_DMRS_DownlinkConfig_PR_setup) + if (dci_format != NR_DL_DCI_FORMAT_1_0 + && pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA + && pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->present == NR_SetupRelease_DMRS_DownlinkConfig_PR_setup) dmrs_config = (NR_DMRS_DownlinkConfig_t *)pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup; } else if (mappingtype == typeB) { - if (pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB && pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->present == NR_SetupRelease_DMRS_DownlinkConfig_PR_setup) + if (pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB + && pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->present == NR_SetupRelease_DMRS_DownlinkConfig_PR_setup) dmrs_config = (NR_DMRS_DownlinkConfig_t *)pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup; } else { - AssertFatal(1==0,"Incorrect Mappingtype\n"); + AssertFatal(false, "Incorrect Mappingtype\n"); } // default values of additionalposition is pos2 @@ -3192,49 +3208,52 @@ int16_t fill_dmrs_mask(const NR_PDSCH_Config_t *pdsch_Config, dmrs_AdditionalPosition = *dmrs_config->dmrs_AdditionalPosition; } - uint8_t ld, row, column; - int32_t l_prime = -1; - // columns 0-3 for TypeA, 4-7 for TypeB - column = (mappingtype == typeA) ? dmrs_AdditionalPosition : (dmrs_AdditionalPosition + 4); + int column = (mappingtype == typeA) ? dmrs_AdditionalPosition : (dmrs_AdditionalPosition + 4); // Section 7.4.1.1.2 in Spec 38.211 // For PDSCH Mapping TypeA, ld is duration between first OFDM of the slot and last OFDM symbol of the scheduled PDSCH resources // For TypeB, ld is the duration of the scheduled PDSCH resources - ld = (mappingtype == typeA) ? (NrOfSymbols + startSymbol) : NrOfSymbols; + int ld = (mappingtype == typeA) ? (NrOfSymbols + startSymbol) : NrOfSymbols; AssertFatal(ld > 2 && ld < 15,"Illegal NrOfSymbols according to Table 5.1.2.1-1 Spec 38.214 %d\n",ld); AssertFatal((NrOfSymbols + startSymbol) < 15,"Illegal S+L according to Table 5.1.2.1-1 Spec 38.214 S:%d L:%d\n",startSymbol, NrOfSymbols); if (mappingtype == typeA) { - // Section 7.4.1.1.2 in Spec 38.211 AssertFatal((l0 == 2) || (l0 == 3 && dmrs_AdditionalPosition != 3),"Wrong config, If dmrs_TypeA_Position POS3, ADD POS cannot be POS3 \n"); - // Table 5.1.2.1-1 in Spec 38.214 AssertFatal(startSymbol <= l0, "Wrong config, Start symbol %d cannot be later than dmrs_TypeA_Position %d \n", startSymbol, l0); - // Section 7.4.1.1.2 in Spec 38.211 AssertFatal(l0 == 2 || (l0 == 3 && (ld != 3 && ld != 4)), "ld 3 or 4 symbols only possible with dmrs_TypeA_Position POS2 \n"); } // number of front loaded symbols + int l_prime, row, l0_shift; if (length == 1) { row = ld - 2; l_prime = table_7_4_1_1_2_3_pdsch_dmrs_positions_l[row][column]; - l0 = 1 << l0; - } - else { + l0_shift = 1 << l0; + } else { row = (ld < 4) ? 0 : (ld - 3); l_prime = table_7_4_1_1_2_4_pdsch_dmrs_positions_l[row][column]; - l0 = 1<= 0, + "ERROR in configuration. Check Time Domain allocation of this Grant. l_prime < 1. row:%d, column:%d\n", + row, + column); - LOG_D(MAC, "l0:%d, ld:%d,row:%d, column:%d, addpos:%d, maxlen:%d\n", l0, ld, row, column, dmrs_AdditionalPosition, length); - AssertFatal(l_prime>=0,"ERROR in configuration.Check Time Domain allocation of this Grant. l_prime < 1. row:%d, column:%d\n", row, column); - - l_prime = (mappingtype == typeA) ? (l_prime | l0) : (l_prime << startSymbol); - LOG_D(MAC, " PDSCH DMRS MASK in HEX:%x\n", l_prime); + l_prime = (mappingtype == typeA) ? (l_prime | l0_shift) : (l_prime << startSymbol); + LOG_D(NR_MAC, + "l0:%d, ld:%d, row:%d, column:%d, addpos:%d, maxlen:%d, DMRS MASK in HEX:%x\n", + (mappingtype == typeA) ? l0 : 0, + ld, + row, + column, + dmrs_AdditionalPosition, + length, + l_prime); return l_prime; } From b67c6396882e08fa0865f4902bdfa60aa58e9982 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 16:47:24 +0100 Subject: [PATCH 068/275] Handle GTP receiver errors On error, exit the GTP thread after printing a diagnostic message. This avoids also an error flagged by address sanitizer for use-after-free. [GTPU] [91] Recvfrom failed (Bad file descriptor) ================================================================= ==285377==ERROR: AddressSanitizer: heap-use-after-free on address 0x7cd7b7fe5590 at pc 0x0000006a9e18 bp 0x7b97ad4e1d50 sp 0x7b97ad4e1d48 READ of size 4 at 0x7cd7b7fe5590 thread T8 ================================================================= ==285377==ERROR: LeakSanitizer: detected memory leaks Direct leak of 160 byte(s) in 1 object(s) allocated from: #0 0x0000006a9e17 in gtpv1uReceiver /home/richie/oai/openair3/ocp-gtpu/gtp_itf.cpp:1346 #1 0x7f97b9a28ee5 in asan_thread_start(void*) (/lib64/libasan.so.8+0x28ee5) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #2 0x7f97b8e7ff53 in start_thread (/lib64/libc.so.6+0x71f53) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317) #3 0x7f97b8f0332b in __clone3 (/lib64/libc.so.6+0xf532b) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317) #0 0x7f97b9ae60cb in memalign (/lib64/libasan.so.8+0xe60cb) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #1 0x000000471039 in newNotifiedFIFO_elt /home/richie/oai/common/utils/threadPool/notified_fifo.h:75 #2 0x000000471039 in tx_func /home/richie/oai/executables/nr-gnb.c:107 #3 0x000000471039 in L1_tx_thread /home/richie/oai/executables/nr-gnb.c:167 #4 0x7f97b9a28ee5 in asan_thread_start(void*) (/lib64/libasan.so.8+0x28ee5) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) 0x7cd7b7fe5590 is located 336 bytes inside of 400-byte region [0x7cd7b7fe5440,0x7cd7b7fe55d0) freed by thread T0 here: SUMMARY: AddressSanitizer: 160 byte(s) leaked in 1 allocation(s). --- openair3/ocp-gtpu/gtp_itf.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index aca2fcbb32..cb73bfd06c 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -1283,7 +1283,7 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st return !GTPNOK; } -static void gtpv1uReceiveHandleMessage(int h) +static bool gtpv1uReceiveHandleMessage(int h) { uint8_t udpData[65536]; int udpDataLen; @@ -1293,19 +1293,19 @@ static void gtpv1uReceiveHandleMessage(int h) if ((udpDataLen = recvfrom(h, udpData, sizeof(udpData), 0, (struct sockaddr *)&addr, &from_len)) < 0) { LOG_E(GTPU, "[%d] Recvfrom failed (%s)\n", h, strerror(errno)); - return; + return false; } else if (udpDataLen == 0) { LOG_W(GTPU, "[%d] Recvfrom returned 0\n", h); - return; + return true; } else { if (udpDataLen < (int)sizeof(Gtpv1uMsgHeaderT)) { LOG_W(GTPU, "[%d] received malformed gtp packet \n", h); - return; + return true; } Gtpv1uMsgHeaderT *msg = (Gtpv1uMsgHeaderT *)udpData; if ((int)(ntohs(msg->msgLength) + sizeof(Gtpv1uMsgHeaderT)) != udpDataLen) { LOG_W(GTPU, "[%d] received malformed gtp packet length\n", h); - return; + return true; } LOG_D(GTPU, "[%d] Received GTP data, msg type: %x\n", h, msg->msgType); switch (msg->msgType) { @@ -1337,13 +1337,13 @@ static void gtpv1uReceiveHandleMessage(int h) break; } } + return true; } static void* gtpv1uReceiver(void *thr) { gtpThread_t *gt = (gtpThread_t *)thr; - while (true) { - gtpv1uReceiveHandleMessage(gt->h); + while (gtpv1uReceiveHandleMessage(gt->h)) { } LOG_W(GTPU, "exiting thread\n"); return NULL; From e40ccbb535ee1b6a81826ba9aabafab9da197021 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 16:49:57 +0100 Subject: [PATCH 069/275] Stop GTP receiver thread on exit Stop the receive thread before closing the socket, which avoids an error message when stopping the GTP softmodem: [GTPU] [91] Recvfrom failed (Bad file descriptor) [GTPU] exiting thread --- openair3/ocp-gtpu/gtp_itf.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index cb73bfd06c..c524129098 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -152,6 +152,7 @@ class gtpEndPoint { } }; +static void gtpv1uReceiverCancel(pthread_t t); class gtpEndPoints { public: pthread_mutex_t gtp_lock = PTHREAD_MUTEX_INITIALIZER; @@ -168,8 +169,10 @@ class gtpEndPoints { ~gtpEndPoints() { // automatically close all sockets on quit - for (const auto &p : instances) + for (const auto &p : instances) { + gtpv1uReceiverCancel(p.second.thrData.t); close(p.first); + } } }; @@ -1349,6 +1352,15 @@ static void* gtpv1uReceiver(void *thr) return NULL; } +static void gtpv1uReceiverCancel(pthread_t t) +{ + int rc; + rc = pthread_cancel(t); + DevAssert(rc == 0); + rc = pthread_join(t, NULL); + DevAssert(rc == 0); +} + #include void *gtpv1uTask(void *args) @@ -1370,6 +1382,8 @@ void *gtpv1uTask(void *args) // DATA TO BE SENT TO UDP case TERMINATE_MESSAGE: + LOG_W(GTPU, "Exiting GTP instance %ld\n", myInstance); + itti_exit_task(); break; case TIMER_HAS_EXPIRED: From 25f3d632f4766c0c1a3ee6cfc52ebf6593f74dc9 Mon Sep 17 00:00:00 2001 From: Rakesh BB Date: Mon, 27 Oct 2025 16:05:31 +0530 Subject: [PATCH 070/275] =?UTF-8?q?[NGAP]=20Fix=20incorrect=20AMF=20Set=20?= =?UTF-8?q?ID=20type=20(uint8=20=E2=86=92=20uint16)=20causing=20AMF=20look?= =?UTF-8?q?up=20failure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Corrected AMF Set ID data type in ngap_gNB_nnsf_select_amf_by_amf_setid() from uint8_t to uint16_t. - AMF Set ID is a 10-bit field as per 3GPP TS 38.413 ยง9.3.3.12. - The incorrect data type caused truncation for AMF Set IDs >255, leading to lookup failures during RRC reestablishment. - Verified successful reestablishment with large AMF Set IDs after the fix. Reason for fix: AMF lookup using AMF Set ID was failing because the 10-bit AMF Set ID was truncated to 8 bits. Closes #1012 --- openair3/NGAP/ngap_gNB_nnsf.c | 2 +- openair3/NGAP/ngap_gNB_nnsf.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openair3/NGAP/ngap_gNB_nnsf.c b/openair3/NGAP/ngap_gNB_nnsf.c index 5fbc3e4d11..e16e790d38 100644 --- a/openair3/NGAP/ngap_gNB_nnsf.c +++ b/openair3/NGAP/ngap_gNB_nnsf.c @@ -156,7 +156,7 @@ ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_plmn_id(ngap_gNB_instance_t *in ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_amf_setid(ngap_gNB_instance_t *instance_p, const ngap_rrc_establishment_cause_t cause, const plmn_id_t selected_plmn_identity, - uint8_t amf_setid) + uint16_t amf_setid) { struct ngap_gNB_amf_data_s *amf_data_p = NULL; diff --git a/openair3/NGAP/ngap_gNB_nnsf.h b/openair3/NGAP/ngap_gNB_nnsf.h index 8a87ecfce2..0b7c7059e3 100644 --- a/openair3/NGAP/ngap_gNB_nnsf.h +++ b/openair3/NGAP/ngap_gNB_nnsf.h @@ -43,7 +43,7 @@ ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_plmn_id(ngap_gNB_instance_t *in ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_amf_setid(ngap_gNB_instance_t *instance_p, const ngap_rrc_establishment_cause_t cause, const plmn_id_t selected_plmn_identity, - uint8_t amf_setid); + uint16_t amf_setid); ngap_gNB_amf_data_t *ngap_gNB_nnsf_select_amf_by_guami(ngap_gNB_instance_t *instance_p, const ngap_rrc_establishment_cause_t cause, From 9b97fd9b62010f09ef19df161f183c92dab127a2 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 23 Oct 2025 19:27:15 +0200 Subject: [PATCH 071/275] Measure tx_func()/rx_func() job duration --- executables/nr-gnb.c | 8 ++++++++ openair1/PHY/defs_gNB.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 505adf825c..2be0ee80ad 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -147,7 +147,9 @@ void *L1_rx_thread(void *arg) if (res == NULL) break; processingData_L1_t *info = (processingData_L1_t *)NotifiedFifoData(res); + start_meas(&gNB->l1_rx_proc); rx_func(info); + stop_meas(&gNB->l1_rx_proc); delNotifiedFIFO_elt(res); } return NULL; @@ -161,7 +163,9 @@ void *L1_tx_thread(void *arg) { if (res == NULL) // stopping condition, happens only when queue is freed break; processingData_L1tx_t *info = (processingData_L1tx_t *)NotifiedFifoData(res); + start_meas(&gNB->l1_tx_proc); tx_func(info); + stop_meas(&gNB->l1_tx_proc); delNotifiedFIFO_elt(res); } return NULL; @@ -226,6 +230,8 @@ static void rx_func(processingData_L1_t *info) static size_t dump_L1_meas_stats(PHY_VARS_gNB *gNB, RU_t *ru, char *output, size_t outputlen) { const char *begin = output; const char *end = output + outputlen; + output += print_meas_log(&gNB->l1_tx_proc, "L1 Tx job", NULL, NULL, output, end - output); + output += print_meas_log(&gNB->l1_rx_proc, "L1 Rx job", NULL, NULL, output, end - output); output += print_meas_log(&gNB->phy_proc_tx, "L1 Tx processing", NULL, NULL, output, end - output); output += print_meas_log(&gNB->dlsch_encoding_stats, "DLSCH encoding", NULL, NULL, output, end - output); output += print_meas_log(&gNB->dlsch_scrambling_stats, "DLSCH scrambling", NULL, NULL, output, end-output); @@ -290,6 +296,8 @@ void *nrL1_stats_thread(void *param) { return NULL; } + reset_meas(&gNB->l1_tx_proc); + reset_meas(&gNB->l1_rx_proc); reset_meas(&gNB->phy_proc_tx); reset_meas(&gNB->dlsch_encoding_stats); reset_meas(&gNB->phy_proc_rx); diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index fb549d4734..aec28272ef 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -494,6 +494,9 @@ typedef struct PHY_VARS_gNB_s { bool enable_analog_das; + time_stats_t l1_tx_proc; + time_stats_t l1_rx_proc; + time_stats_t phy_proc_tx; time_stats_t phy_proc_rx; time_stats_t rx_prach; From 7e8245c4127655dcc30c399a8bd178ff759434c0 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 24 Oct 2025 10:23:49 +0200 Subject: [PATCH 072/275] nrMAC_stats.log thread: correctly lock scheduler for stats --- openair2/LAYER2/NR_MAC_gNB/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index fa66125bf7..2df930f3ff 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -87,7 +87,6 @@ void *nrmac_stats_thread(void *arg) { char *p = output; NR_SCHED_LOCK(&gNB->sched_lock); p += dump_mac_stats(gNB, p, end - p, false); - NR_SCHED_UNLOCK(&gNB->sched_lock); p += snprintf(p, end - p, "\n"); p += print_meas_log(&gNB->gNB_scheduler, "gNB_scheduler", NULL, NULL, p, end - p); p += print_meas_log(&gNB->rx_ulsch_sdu, "rx_ulsch_sdu", NULL, NULL, p, end - p); @@ -97,6 +96,7 @@ void *nrmac_stats_thread(void *arg) { p += print_meas_log(&gNB->rlc_data_req, "rlc_data_req", NULL, NULL, p, end - p); p += print_meas_log(&gNB->nr_srs_ri_computation_timer, "UL-RI computation time", NULL, NULL, p, end - p); p += print_meas_log(&gNB->nr_srs_tpmi_computation_timer, "UL-TPMI computation time", NULL, NULL, p, end - p); + NR_SCHED_UNLOCK(&gNB->sched_lock); fwrite(output, p - output, 1, file); fflush(file); sleep(1); From 07f06f8c06f2f33ed0e5a49a6e610363ebd92c4d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 24 Oct 2025 10:25:47 +0200 Subject: [PATCH 073/275] nrMAC_stats.log: handle file errors, add truncate to reset file truncate resets the file size to 0. Without this, e.g., when disconnecting a UE, the old UE remains at the end and is not overwritten, which is confusing. --- openair2/LAYER2/NR_MAC_gNB/main.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index 2df930f3ff..4b8196fc1b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -97,10 +97,16 @@ void *nrmac_stats_thread(void *arg) { p += print_meas_log(&gNB->nr_srs_ri_computation_timer, "UL-RI computation time", NULL, NULL, p, end - p); p += print_meas_log(&gNB->nr_srs_tpmi_computation_timer, "UL-TPMI computation time", NULL, NULL, p, end - p); NR_SCHED_UNLOCK(&gNB->sched_lock); - fwrite(output, p - output, 1, file); - fflush(file); + size_t len = p - output; + if (fwrite(output, len, 1, file) != 1 || fflush(file) != 0) { + LOG_E(NR_MAC, "error while writing nrMAC_stats.log: %d, %s\n", errno, strerror(errno)); + break; + } sleep(1); - fseek(file,0,SEEK_SET); + if (ftruncate(fileno(file), 0) != 0 || fseek(file, 0, SEEK_SET) != 0) { + LOG_E(NR_MAC, "error while writing nrMAC_stats.log: %d, %s\n", errno, strerror(errno)); + break; + } } fclose(file); return NULL; From 7df8243a2ed8ad02c5047326dca90cf0bb3a9ad5 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 24 Oct 2025 10:55:22 +0200 Subject: [PATCH 074/275] Remove NR MAC's UE-specific lock There is the (global) sched_lock that prevents concurrent access. A dedicated UE lock does not make sense in that case. Worse, this lock does not prevent all concurrent UE accesses (e.g., there are many loops over UEs that are not protected), so it's also wrong. --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 3 +-- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 8 ++------ openair2/LAYER2/NR_MAC_gNB/main.c | 3 --- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 1 - 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 5e9cd47a79..520aa6dfe1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -2115,10 +2115,9 @@ static void nr_fill_rar(uint8_t Mod_idP, NR_UE_info_t *UE, uint8_t *dlsch_buffer * The corresponding function to add is add_new_UE_RA(). */ void nr_release_ra_UE(gNB_MAC_INST *mac, rnti_t rnti) { + NR_SCHED_ENSURE_LOCKED(&mac->sched_lock); NR_UEs_t *UE_info = &mac->UE_info; - NR_SCHED_LOCK(&UE_info->mutex); NR_UE_info_t *UE = remove_UE_from_list(NR_NB_RA_PROC_MAX, UE_info->access_ue_list, rnti); - NR_SCHED_UNLOCK(&UE_info->mutex); if (UE) { delete_nr_ue_data(UE, mac->common_channels, &UE_info->uid_allocator); } else { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 4e55941420..d5cda66b5b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -3033,18 +3033,17 @@ bool transition_ra_connected_nr_ue(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE) * To remove the UE, use mac_remove_nr_ue(). */ bool add_connected_nr_ue(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE) { + NR_SCHED_ENSURE_LOCKED(&nr_mac->sched_lock); + LOG_I(NR_MAC, "Adding new UE context with RNTI 0x%04x\n", UE->rnti); NR_UEs_t *UE_info = &nr_mac->UE_info; dump_nr_list(UE_info->connected_ue_list); AssertFatal(!UE->ra, "UE in connected cannot have RA process\n"); - NR_SCHED_LOCK(&UE_info->mutex); - bool success = add_UE_to_list(MAX_MOBILES_PER_GNB, UE_info->connected_ue_list, UE); if (!success) { LOG_E(NR_MAC,"Try to add UE %04x but the list is full\n", UE->rnti); delete_nr_ue_data(UE, NULL, &UE_info->uid_allocator); - NR_SCHED_UNLOCK(&UE_info->mutex); return false; } @@ -3057,7 +3056,6 @@ bool add_connected_nr_ue(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE) init_bler_stats(&nr_mac->dl_bler, &sched_ctrl->dl_bler_stats, nr_mac->frame); init_bler_stats(&nr_mac->ul_bler, &sched_ctrl->ul_bler_stats, nr_mac->frame); - NR_SCHED_UNLOCK(&UE_info->mutex); dump_nr_list(UE_info->connected_ue_list); return true; } @@ -3110,9 +3108,7 @@ void mac_remove_nr_ue(gNB_MAC_INST *nr_mac, rnti_t rnti) /* already mutex protected */ NR_SCHED_ENSURE_LOCKED(&nr_mac->sched_lock); NR_UEs_t *UE_info = &nr_mac->UE_info; - NR_SCHED_LOCK(&UE_info->mutex); NR_UE_info_t *UE = remove_UE_from_list(MAX_MOBILES_PER_GNB + 1, UE_info->connected_ue_list, rnti); - NR_SCHED_UNLOCK(&UE_info->mutex); if (UE) delete_nr_ue_data(UE, nr_mac->common_channels, &UE_info->uid_allocator); else diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index 4b8196fc1b..ec8fe2010d 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -127,7 +127,6 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset * scheduler to be locked*/ NR_SCHED_ENSURE_LOCKED(&gNB->sched_lock); - NR_SCHED_LOCK(&gNB->UE_info.mutex); UE_iterator(gNB->UE_info.connected_ue_list, UE) { NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; NR_mac_stats_t *stats = &UE->mac_stats; @@ -237,7 +236,6 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset stats->ul.lc_bytes[c->lcid]); } } - NR_SCHED_UNLOCK(&gNB->UE_info.mutex); return output - begin; } @@ -304,7 +302,6 @@ void mac_top_init_gNB(ngran_node_t node_type, pthread_mutex_init(&RC.nrmac[i]->sched_lock, NULL); - pthread_mutex_init(&RC.nrmac[i]->UE_info.mutex, NULL); uid_linear_allocator_init(&RC.nrmac[i]->UE_info.uid_allocator); if (get_softmodem_params()->phy_test) { diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 026d8d563e..a5b21d2bc0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -797,7 +797,6 @@ typedef struct { typedef struct { /// scheduling control info // last element always NULL - pthread_mutex_t mutex; NR_UE_info_t *connected_ue_list[MAX_MOBILES_PER_GNB + 1]; NR_UE_info_t *access_ue_list[NR_NB_RA_PROC_MAX + 1]; // bitmap of CSI-RS already scheduled in current slot From 48faffc131efcdf90e34cca0c81bbe4dbd1f7370 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 24 Oct 2025 14:19:05 +0200 Subject: [PATCH 075/275] dump_mac_stats(): avoid overflow The call to snprintf() as was used is wrong: we use the return value to advance output, but the man page says > The functions snprintf() and vsnprintf() do not write more than size > bytes (including the terminating null byte ('\0')). If the output was > truncated due to this limit, then the return value is the number of > characters (excluding the terminating null byte) which would have been > written to the final string if enough space had been available. > Thus, a return value of size or more means that the output was > truncated. Thus, output could go beyond end, and we get a stack overflow. Instead, encapsulate the call to snprintf() checking this condition, and only advancing output as intended, limiting to 0 if we are at the end of the buffer. This avoids this error: ==964825==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7be799f51988 at pc 0x7fe7d66b0d39 bp 0x7be79b825ef0 sp 0x7be79b8256c0 WRITE of size 3 at 0x7be799f51988 thread T24 #0 0x7fe7d66b0d38 in vsnprintf (/lib64/libasan.so.8+0xb0d38) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #1 0x7fe7d66b2d44 in snprintf (/lib64/libasan.so.8+0xb2d44) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #2 0x0000008fe7c6 in dump_mac_stats /home/richie/w/refactor-dlsch/openair2/LAYER2/NR_MAC_gNB/main.c:183 #3 0x00000092071a in gNB_dlsch_ulsch_scheduler /home/richie/w/refactor-dlsch/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c:200 #4 0x0000008f8f78 in run_scheduler_monolithic /home/richie/w/refactor-dlsch/openair2/NR_PHY_INTERFACE/NR_IF_Module.c:399 --- openair2/LAYER2/NR_MAC_gNB/main.c | 70 ++++++++++++++++++------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index ec8fe2010d..705bca639a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -118,6 +118,19 @@ void clear_mac_stats(gNB_MAC_INST *gNB) { } } +static char *st_append(char *start, const char *end, const char *format, ...) +{ + size_t space = end - start; + va_list args; + va_start(args, format); + size_t written = vsnprintf(start, space, format, args); + va_end(args); + if (written <= space) + return start + written; + else + return (char *)end; +} + size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset_rsrp) { const char *begin = output; @@ -133,39 +146,39 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset const int avg_rsrp = stats->num_rsrp_meas > 0 ? stats->cumul_rsrp / stats->num_rsrp_meas : 0; const int avg_sinrx10 = stats->num_sinr_meas > 0 ? stats->cumul_sinrx10 / stats->num_sinr_meas : 0; - output += snprintf(output, end - output, "UE RNTI %04x CU-UE-ID ", UE->rnti); + output = st_append(output, end, "UE RNTI %04x CU-UE-ID ", UE->rnti); if (du_exists_f1_ue_data(UE->rnti)) { f1_ue_data_t ued = du_get_f1_ue_data(UE->rnti); - output += snprintf(output, end - output, "%d", ued.secondary_ue); + output = st_append(output, end, "%d", ued.secondary_ue); } else { - output += snprintf(output, end - output, "(none)"); + output = st_append(output, end, "(none)"); } bool in_sync = !sched_ctrl->ul_failure; - output += snprintf(output, - end - output, + output = st_append(output, + end, " %s PH %d dB PCMAX %d dBm", in_sync ? "in-sync" : "out-of-sync", sched_ctrl->ph, sched_ctrl->pcmax); if (stats->num_rsrp_meas) - output += snprintf(output, end - output, ", average RSRP %d (%d meas)", avg_rsrp, stats->num_rsrp_meas); + output = st_append(output, end, ", average RSRP %d (%d meas)", avg_rsrp, stats->num_rsrp_meas); if (stats->num_sinr_meas) { - output += snprintf(output, - end - output, + output = st_append(output, + end, ", average SINR %d.%d (%d meas)", avg_sinrx10 / 10, avg_sinrx10 % 10, stats->num_sinr_meas); } - output += snprintf(output, end - output, "\n"); + output = st_append(output, end, "\n"); if(sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.print_report) - output += snprintf(output, - end - output, + output = st_append(output, + end, "UE %04x: CQI %d, RI %d, PMI (%d,%d)\n", UE->rnti, sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb, @@ -174,18 +187,18 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2); if (stats->srs_stats[0] != '\0') { - output += snprintf(output, end - output, "UE %04x: %s\n", UE->rnti, stats->srs_stats); + output = st_append(output, end, "UE %04x: %s\n", UE->rnti, stats->srs_stats); } - output += snprintf(output, - end - output, + output = st_append(output, + end, "UE %04x: dlsch_rounds ", UE->rnti); - output += snprintf(output, end - output, "%"PRIu64, stats->dl.rounds[0]); + output = st_append(output, end, "%"PRIu64, stats->dl.rounds[0]); for (int i = 1; i < gNB->dl_bler.harq_round_max; i++) - output += snprintf(output, end - output, "/%"PRIu64, stats->dl.rounds[i]); + output = st_append(output, end, "/%"PRIu64, stats->dl.rounds[i]); - output += snprintf(output, - end - output, + output = st_append(output, + end, ", dlsch_errors %"PRIu64", pucch0_DTX %d, BLER %.5f MCS (%d) %d CCE fail %d\n", stats->dl.errors, stats->pucch0_DTX, @@ -199,15 +212,15 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset stats->num_sinr_meas = 0; stats->cumul_sinrx10 = 0; } - output += snprintf(output, - end - output, + output = st_append(output, + end, "UE %04x: ulsch_rounds ", UE->rnti); - output += snprintf(output, end - output, "%"PRIu64, stats->ul.rounds[0]); + output = st_append(output, end, "%"PRIu64, stats->ul.rounds[0]); for (int i = 1; i < gNB->ul_bler.harq_round_max; i++) - output += snprintf(output, end - output, "/%"PRIu64, stats->ul.rounds[i]); + output = st_append(output, end, "/%"PRIu64, stats->ul.rounds[i]); - output += snprintf(output, - end - output, + output = st_append(output, + end, ", ulsch_errors %"PRIu64", ulsch_DTX %d, BLER %.5f MCS (%d) %d (Qm %d deltaMCS %d dB) NPRB %d SNR %d.%d dB CCE fail %d\n", stats->ul.errors, stats->ulsch_DTX, @@ -220,15 +233,15 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset sched_ctrl->pusch_snrx10 / 10, sched_ctrl->pusch_snrx10 % 10, sched_ctrl->ul_cce_fail); - output += snprintf(output, - end - output, + output = st_append(output, + end, "UE %04x: MAC: TX %14"PRIu64" RX %14"PRIu64" bytes\n", UE->rnti, stats->dl.total_bytes, stats->ul.total_bytes); for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); i++) { const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i); - output += snprintf(output, - end - output, + output = st_append(output, + end, "UE %04x: LCID %d: TX %14"PRIu64" RX %14"PRIu64" bytes\n", UE->rnti, c->lcid, @@ -236,6 +249,7 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset stats->ul.lc_bytes[c->lcid]); } } + DevAssert(output <= end); return output - begin; } From 6549acdba9c7f0a2a08b757782f278c6b7c27be4 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 28 Oct 2025 18:06:39 +0100 Subject: [PATCH 076/275] fix for CSI payload sizes in PUCCH structure at UE --- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index f8b9f18c11..b43937d362 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -307,8 +307,8 @@ typedef struct { NR_PUCCH_Resource_t *pucch_resource; uint32_t ack_payload; uint8_t sr_payload; - uint32_t csi_part1_payload; - uint32_t csi_part2_payload; + uint64_t csi_part1_payload; + uint64_t csi_part2_payload; int n_sr; int n_csi; int n_harq; From 93cadc9d631415b3e3948fdc93336edea28714eb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 28 Oct 2025 12:01:38 +0100 Subject: [PATCH 077/275] Truncate nrL1_stats.log before writing Without this, the remainder of the file remains in place, and this can be confusing if the total file size reduces. --- executables/nr-gnb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 2be0ee80ad..aa1a496559 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -314,13 +314,16 @@ void *nrL1_stats_thread(void *param) { reset_meas(&gNB->dlsch_precoding_stats); while (!oai_exit) { sleep(1); + if (ftruncate(fileno(fd), 0) != 0 || fseek(fd, 0, SEEK_SET) != 0) { + LOG_E(NR_MAC, "error while writing nrL1_stats.log: %d, %s\n", errno, strerror(errno)); + break; + } dump_nr_I0_stats(fd,gNB); dump_pdsch_stats(fd,gNB); dump_pusch_stats(fd,gNB); dump_L1_meas_stats(gNB, ru, output, L1STATSSTRLEN); fprintf(fd,"%s\n",output); fflush(fd); - fseek(fd,0,SEEK_SET); } fclose(fd); return(NULL); From e57789adb2725756af4e6c49c8f1eb2a2969c621 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 29 Oct 2025 10:52:39 +0100 Subject: [PATCH 078/275] prevent assertion in case of no measurements configured for CSI-RS at UE --- openair1/PHY/NR_UE_TRANSPORT/csi_rx.c | 5 +++++ openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/csi_rx.c b/openair1/PHY/NR_UE_TRANSPORT/csi_rx.c index c28830e4d7..26cad50f12 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/csi_rx.c +++ b/openair1/PHY/NR_UE_TRANSPORT/csi_rx.c @@ -905,6 +905,11 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue, rxdataF); + if (csirs_config_pdu->measurement_bitmap == 0) { + LOG_D(NR_PHY, "No CSI-RS measurements configured\n"); + return; + } + uint32_t noise_power = 0; int16_t log2_re = 0; int16_t log2_maxh = 0; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 3825a49fd2..714d2eacfd 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -1918,7 +1918,6 @@ uint8_t set_csirs_measurement_bitmap(NR_CSI_MeasConfig_t *csi_measconfig, NR_CSI AssertFatal(false, "Unexpected measurement report type %d\n", report_config->reportQuantity.present); } } - AssertFatal(meas_bitmap > 0, "Expected to have at least 1 measurement configured for CSI-RS\n"); return meas_bitmap; } From 5e32eb67f6a909c5f0c96db6e4b37bb7c6203a47 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 29 Oct 2025 11:02:19 +0100 Subject: [PATCH 079/275] remove unnecessary do_CSIRS from CI config files --- ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf | 1 - .../conf_files/gnb.sa.band78.106prb.rfsim.neighbour.conf | 2 -- ci-scripts/conf_files/gnb.sa.band78.51prb.aw2s.ddsuu.conf | 4 ---- .../conf_files/untested/gnb.sa.band78.fr1.51PRB.usrpb210.conf | 3 --- 4 files changed, 10 deletions(-) diff --git a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf index 23475e3838..4daf7c62fd 100644 --- a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf +++ b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf @@ -23,7 +23,6 @@ gNBs = ////////// Physical parameters: min_rxtxtime = 2; - do_CSIRS = 1; do_SRS = 1; servingCellConfigCommon = ( diff --git a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.neighbour.conf b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.neighbour.conf index 6322d1463c..7bfceaf620 100644 --- a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.neighbour.conf +++ b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.neighbour.conf @@ -18,8 +18,6 @@ gNBs = ////////// Physical parameters: - do_CSIRS = 0; - do_SRS = 0; min_rxtxtime = 6; servingCellConfigCommon = ( diff --git a/ci-scripts/conf_files/gnb.sa.band78.51prb.aw2s.ddsuu.conf b/ci-scripts/conf_files/gnb.sa.band78.51prb.aw2s.ddsuu.conf index e9f3089019..69cf8b7330 100644 --- a/ci-scripts/conf_files/gnb.sa.band78.51prb.aw2s.ddsuu.conf +++ b/ci-scripts/conf_files/gnb.sa.band78.51prb.aw2s.ddsuu.conf @@ -21,10 +21,6 @@ gNBs = nr_cellid = 12345678L ////////// Physical parameters: - pdsch_AntennaPorts_XP = 1; - pusch_AntennaPorts = 1; - do_CSIRS = 1; - do_SRS = 0; uess_agg_levels = [2, 2, 2, 0, 0]; servingCellConfigCommon = ( diff --git a/ci-scripts/conf_files/untested/gnb.sa.band78.fr1.51PRB.usrpb210.conf b/ci-scripts/conf_files/untested/gnb.sa.band78.fr1.51PRB.usrpb210.conf index 7242b91190..7bdbe7127c 100644 --- a/ci-scripts/conf_files/untested/gnb.sa.band78.fr1.51PRB.usrpb210.conf +++ b/ci-scripts/conf_files/untested/gnb.sa.band78.fr1.51PRB.usrpb210.conf @@ -17,9 +17,6 @@ gNBs = ////////// Physical parameters: - do_CSIRS = 0; - do_SRS = 0; - servingCellConfigCommon = ( { #spCellConfigCommon From 56b987c1dd388b58f493bdfaf4295c26baf391dd Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 29 Oct 2025 14:27:36 +0100 Subject: [PATCH 080/275] bugfix for computation of alt size of DCI00 --- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 1a5431bdd2..9572b4f602 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2813,11 +2813,13 @@ uint16_t nr_dci_size(const NR_UE_DL_BWP_t *DL_BWP, dci_pdu->harq_pid.nbits = 4; dci_pdu->frequency_domain_assignment.nbits = (uint8_t)ceil(log2((N_RB * (N_RB + 1)) >>1)); // Freq domain assignment -- hopping scenario to be updated size += dci_pdu->frequency_domain_assignment.nbits; - if(alt_size >= size) - size += alt_size - size; // Padding to match 1_0 size - else if (ss_type == NR_SearchSpace__searchSpaceType_PR_common) { - dci_pdu->frequency_domain_assignment.nbits -= (size - alt_size); - size = alt_size; + if (alt_size) { + if(alt_size >= size) + size += alt_size - size; // Padding to match 1_0 size + else if (ss_type == NR_SearchSpace__searchSpaceType_PR_common) { + dci_pdu->frequency_domain_assignment.nbits -= (size - alt_size); + size = alt_size; + } } // UL/SUL indicator assumed to be 0 break; @@ -3127,7 +3129,7 @@ uint16_t nr_dci_size(const NR_UE_DL_BWP_t *DL_BWP, default: AssertFatal(1==0, "Invalid NR DCI format %d\n", format); } - LOG_D(NR_MAC, "DCI size: %d\n", size); + LOG_D(NR_MAC, "DCI format %d size: %d alt_size %d\n", format, size, alt_size); return size; } From 10bda8417637ee0ba11d720903320c537631fc29 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 22 Oct 2025 11:36:47 +0200 Subject: [PATCH 081/275] Limit statistics of UE to configurable threshold The gNB will stop the periodical logging of UEs in MAC Info level after a configurable number of UEs have been printed, by default 8. Set it to 17 in some CI tests to ensure we still get all UEs for the time being, as the CI might depend on this. --- .../conf_files/gnb.sa.band78.106prb.rfsim.yaml | 1 + .../gnb.sa.band78.51prb.aw2s.ddsuu.conf | 1 + doc/MAC/mac-usage.md | 3 +++ openair2/GNB_APP/MACRLC_nr_paramdef.h | 5 +++++ openair2/GNB_APP/gnb_config.c | 2 ++ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 15 ++++++++++++++- openair2/LAYER2/NR_MAC_gNB/main.c | 2 ++ openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 5 +++++ 8 files changed, 33 insertions(+), 1 deletion(-) diff --git a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.yaml b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.yaml index 6240b62104..918bdc9802 100644 --- a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.yaml +++ b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.yaml @@ -146,6 +146,7 @@ MACRLCs: tr_n_preference: local_RRC pusch_TargetSNRx10: 200 pucch_TargetSNRx10: 200 + stats_max_ue: 17 L1s: - num_cc: 1 diff --git a/ci-scripts/conf_files/gnb.sa.band78.51prb.aw2s.ddsuu.conf b/ci-scripts/conf_files/gnb.sa.band78.51prb.aw2s.ddsuu.conf index e9f3089019..95e86caefe 100644 --- a/ci-scripts/conf_files/gnb.sa.band78.51prb.aw2s.ddsuu.conf +++ b/ci-scripts/conf_files/gnb.sa.band78.51prb.aw2s.ddsuu.conf @@ -186,6 +186,7 @@ MACRLCs = ( tr_n_preference = "local_RRC"; pusch_TargetSNRx10 = 200; pucch_TargetSNRx10 = 200; + stats_max_ue = 17; } ); diff --git a/doc/MAC/mac-usage.md b/doc/MAC/mac-usage.md index f4583b7a23..09ef02c560 100644 --- a/doc/MAC/mac-usage.md +++ b/doc/MAC/mac-usage.md @@ -236,6 +236,9 @@ In the `MACRLCs` section of the gNB/DU configuration file: case RSSI reaches the threshold and prevents ADC railing. Unit depends on RSSI reporting config. * `pucch_RSSI_Threshold`: Same as above but for PUCCH +* `stats_max_ue` (default 8): maximum number of UEs to show in periodical + stats; beyond this number, periodical statistics will be disabled (it can + still be seen in `nrMAC_stats.log`. Use `0` to disable periodical stats. In the `gNBs` section of the gNB/DU configuration file: some of the parameters affect RRC configuration (CellGroupConfig) of a UE, and are therefore listed diff --git a/openair2/GNB_APP/MACRLC_nr_paramdef.h b/openair2/GNB_APP/MACRLC_nr_paramdef.h index a6299b10cc..6bd02cf74a 100644 --- a/openair2/GNB_APP/MACRLC_nr_paramdef.h +++ b/openair2/GNB_APP/MACRLC_nr_paramdef.h @@ -79,6 +79,7 @@ #define CONFIG_STRING_MACRLC_BEAM_WEIGHTS_LIST "beam_weights" #define CONFIG_STRING_MACRLC_PUSCH_RSSI_THRESHOLD "pusch_RSSI_Threshold" #define CONFIG_STRING_MACRLC_PUCCH_RSSI_THRESHOLD "pucch_RSSI_Threshold" +#define CONFIG_STRING_MACRLC_STATS_MAX_UE "stats_max_ue" #define HLP_MACRLC_UL_PRBBLACK "SNR threshold to decide whether a PRB will be blacklisted or not" #define HLP_MACRLC_DL_BLER_UP "Upper threshold of BLER to decrease DL MCS" @@ -98,6 +99,7 @@ #define HLP_MACRLC_BEAMS_PERIOD "set of beams that can be simultaneously allocated in a period" #define HLP_MACRLC_PUSCH_RSSI_THRESHOLD "Limits PUSCH TPC commands based on RSSI to prevent ADC railing. Value range [-1280, 0], unit 0.1 dBm/dBFS" #define HLP_MACRLC_PUCCH_RSSI_THRESHOLD "Limits PUCCH TPC commands based on RSSI to prevent ADC railing. Value range [-1280, 0], unit 0.1 dBm/dBFS" +#define HLP_MACRLC_STATS_MAX_UE "Maximum number of UEs before disabling periodical output (0 to disable)" /*-------------------------------------------------------------------------------------------------------------------------------------------------------*/ /* MacRLC configuration parameters */ @@ -149,6 +151,7 @@ 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \ {CONFIG_STRING_MACRLC_PUCCH_RSSI_THRESHOLD, HLP_MACRLC_PUCCH_RSSI_THRESHOLD, \ 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \ + {CONFIG_STRING_MACRLC_STATS_MAX_UE, HLP_MACRLC_STATS_MAX_UE, 0, .iptr=NULL, .defintval=8, TYPE_INT, 0}, \ } // clang-format off @@ -194,6 +197,7 @@ #define MACRLC_BEAMWEIGHTS_IDX 39 #define MACRLC_PUSCH_RSSI_THRES_IDX 40 #define MACRLC_PUCCH_RSSI_THRES_IDX 41 +#define MACRLC_STATS_MAX_UE_IDX 42 #define MACRLCPARAMS_CHECK { \ { .s5 = { NULL } }, \ @@ -238,6 +242,7 @@ { .s5 = { NULL } }, \ { .s2 = { config_check_intrange, {-1280, 0}} }, /* PUSCH RSSI threshold range */ \ { .s2 = { config_check_intrange, {-1280, 0}} }, /* PUCCH RSSI threshold range */ \ + { .s5 = { NULL } }, \ } /*---------------------------------------------------------------------------------------------------------------------------------------------------------*/ diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 45532572cd..bd49df26be 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1580,6 +1580,8 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) AssertFatal(1 == 0, "MACRLC %d: %s unknown southbound midhaul\n", j, *(MacRLC_ParamList.paramarray[j][MACRLC_TRANSPORT_S_PREFERENCE_IDX].strptr)); } RC.nrmac[j]->ulsch_max_frame_inactivity = *(MacRLC_ParamList.paramarray[j][MACRLC_ULSCH_MAX_FRAME_INACTIVITY].uptr); + RC.nrmac[j]->stats_max_ue = *MacRLC_ParamList.paramarray[j][MACRLC_STATS_MAX_UE_IDX].iptr; + RC.nrmac[j]->print_ue_stats = RC.nrmac[j]->stats_max_ue > 0; NR_bler_options_t *dl_bler_options = &RC.nrmac[j]->dl_bler; dl_bler_options->upper = *(MacRLC_ParamList.paramarray[j][MACRLC_DL_BLER_TARGET_UPPER_IDX].dblptr); dl_bler_options->lower = *(MacRLC_ParamList.paramarray[j][MACRLC_DL_BLER_TARGET_LOWER_IDX].dblptr); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 46ad8069da..a102411721 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -195,10 +195,23 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo } bool wait_prach_completed = gNB->num_scheduled_prach_rx >= NUM_PRACH_RX_FOR_NOISE_ESTIMATE; - if ((wait_prach_completed || get_softmodem_params()->phy_test) && (slot == 0) && (frame & 127) == 0) { + if (gNB->print_ue_stats && (wait_prach_completed || get_softmodem_params()->phy_test) && (slot == 0) && (frame & 127) == 0) { char stats_output[32656] = {0}; dump_mac_stats(gNB, stats_output, sizeof(stats_output), true); LOG_I(NR_MAC, "Frame.Slot %d.%d\n%s\n", frame, slot, stats_output); + + // TODO: this should be replaced with a size() operation on connected_ue_list + int num_ue = 0; + UE_iterator(gNB->UE_info.connected_ue_list, it) { + (void) it; // not used + num_ue++; + } + if (gNB->print_ue_stats && num_ue > gNB->stats_max_ue) { + gNB->print_ue_stats = false; + LOG_W(NR_MAC, + "periodical UE stats deactivated after reaching %d UEs, please check nrMAC_stats.log, or increase MACRLCs.[0].stats_max_ue\n", + gNB->stats_max_ue); + } } nr_measgap_scheduling(gNB, frame, slot); diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index 705bca639a..f3f070b0e7 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -314,6 +314,8 @@ 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]->print_ue_stats = true; + pthread_mutex_init(&RC.nrmac[i]->sched_lock, NULL); uid_linear_allocator_init(&RC.nrmac[i]->UE_info.uid_allocator); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index a5b21d2bc0..5bd786df90 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -974,6 +974,11 @@ typedef struct gNB_MAC_INST_s { f1_config_t f1_config; int16_t frame; + /// number of UEs to exceed to disable stats + int stats_max_ue; + /// if stats are currently enabled + bool print_ue_stats; + pthread_mutex_t sched_lock; mac_stats_t mac_stats; From 91b3ca77f650a289b262269fde68a0e1eb750bcc Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 17:16:43 +0100 Subject: [PATCH 082/275] QFI 0 is a valid QFI QFI 0 is a valid QFI: don't check for qfi <= 0, as uint8 cannot be negative. Also, correctly use SDAP_MAP_RULE_EMPTY for a DRB ID (which need to be larger than 0). --- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index f0e791c683..9ff9e39471 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -210,8 +210,8 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, { /* The offset of the SDAP header, it might be 0 if has_sdap_rx is not true in the pdcp entity. */ int offset=0; - int qfi = buf[0] & 0x3F; // QFI is always the first 6 bits in the first octet - if (qfi <= 0 || qfi >= SDAP_MAX_QFI) { + uint8_t qfi = buf[0] & 0x3F; // QFI is always the first 6 bits in the first octet + if (qfi >= SDAP_MAX_QFI) { LOG_E(SDAP, "Invalid QFI %d received in SDAP header\n", qfi); return; } @@ -450,7 +450,7 @@ static void nr_sdap_add_qos_flows_to_drb(nr_sdap_entity_t *entity, const sdap_co for (int i = 0; i < sdap->mappedQFIs2AddCount; i++) { uint8_t qfi = sdap->mappedQFIs2Add[i]; LOG_D(SDAP, "Adding QFI to DRB mapping rules: %d mapped QFIs for DRB %d\n", sdap->mappedQFIs2AddCount, sdap->drb_id); - if (qfi < SDAP_MAX_QFI && qfi > SDAP_MAP_RULE_EMPTY && sdap->drb_id > 0 && sdap->drb_id <= MAX_DRBS_PER_UE) { + if (qfi < SDAP_MAX_QFI && sdap->drb_id > SDAP_MAP_RULE_EMPTY && sdap->drb_id <= MAX_DRBS_PER_UE) { entity->qfi2drb_map_add(entity, qfi, sdap->drb_id, sdap->role); } else { LOG_E(SDAP, "Failed to add qfi2drb mapping: QFI=%d, DRB=%d\n", qfi, sdap->drb_id); From a416db39ba44806ce1750fb75493897df5d9a400 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Mon, 27 Oct 2025 18:05:22 +0100 Subject: [PATCH 083/275] Pass SDAP config by pointer sdap_config_t is more than 1kB large. Avoid excessive copying by passing it by reference. --- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 2 +- openair2/RRC/NR/rrc_gNB_nsa.c | 2 +- openair2/RRC/NR_UE/rrc_UE.c | 2 +- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 16 ++++++++-------- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index ed9cc37c18..4b95963257 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -153,7 +153,7 @@ static void e1_add_bearers(const int ue_id, const NR_DRB_ToAddModList_t *addMod, // get SDAP config sdap_config_t sdap = nr_sdap_get_config(GNB_FLAG_YES, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity); // add SDAP entity - nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, sdap); + nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, &sdap); // add PDCP entity nr_pdcp_add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp); } diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index 996dc3528e..3e3c462145 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -177,7 +177,7 @@ static void rrc_nsa_add_drb(const int ue_id, sdap.drb_id = drb->drb_Identity; sdap.defaultDRB = true; // add SDAP entity (terminated at gNB, since it's EPC) - nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, sdap); + nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, &sdap); // add PDCP entity nr_pdcp_add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp); } diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 6681eed855..15c713cb26 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -619,7 +619,7 @@ static void rrc_ue_add_bearer(const int ue_id, const NR_DRB_ToAddMod_t *drb, con sdap = nr_sdap_get_config(GNB_FLAG_NO, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity); } // add SDAP entity - nr_sdap_addmod_entity(GNB_FLAG_NO, ue_id, sdap); + nr_sdap_addmod_entity(GNB_FLAG_NO, ue_id, &sdap); // add PDCP entity nr_pdcp_add_drb(GNB_FLAG_NO, ue_id, drb->pdcp_Config, &sdap, sp); } diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 9ff9e39471..389213986e 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -487,13 +487,13 @@ static void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, const sdap_conf * @param is_gnb, indicates whether it is for gNB or UE * @param ue_id, UE ID * @param sdap, SDAP configuration */ -static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap) +static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t *sdap) { nr_sdap_entity_t *sdap_entity = calloc_or_fail(1, sizeof(*sdap_entity)); // SDAP entity ids sdap_entity->ue_id = ue_id; - sdap_entity->pdusession_id = sdap.pdusession_id; + sdap_entity->pdusession_id = sdap->pdusession_id; sdap_entity->is_gnb = is_gnb; // rx/tx entities @@ -513,13 +513,13 @@ static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap sdap_entity->pdusession_sock = -1; // set default DRB - if (sdap.defaultDRB) { - sdap_entity->default_drb = sdap.drb_id; + if (sdap->defaultDRB) { + sdap_entity->default_drb = sdap->drb_id; LOG_I(SDAP, "Default DRB for the created SDAP entity: DRB %d \n", sdap_entity->default_drb); } // Add QoS flows to the DRB (initial configuration) - nr_sdap_add_qos_flows_to_drb(sdap_entity, &sdap); + nr_sdap_add_qos_flows_to_drb(sdap_entity, sdap); // update SDAP entity list pointers sdap_entity->next_entity = sdap_info.sdap_entity_llist; @@ -533,11 +533,11 @@ static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap } /** @brief Add or modify an SDAP entity if it already exists */ -void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap) +void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t *sdap) { - nr_sdap_entity_t *sdap_entity = nr_sdap_get_entity(ue_id, sdap.pdusession_id); + nr_sdap_entity_t *sdap_entity = nr_sdap_get_entity(ue_id, sdap->pdusession_id); if (sdap_entity) { - sdap_entity->qfi2drb_map_update(sdap_entity, &sdap); + sdap_entity->qfi2drb_map_update(sdap_entity, sdap); } else { nr_sdap_add_entity(is_gnb, ue_id, sdap); } diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index a206189035..e96836ac79 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -172,7 +172,7 @@ sdap_config_t nr_sdap_get_config(const int is_gnb, const NR_SDAP_Config_t *sdap_ void nr_sdap_release_drb(ue_id_t ue_id, int drb_id, int pdusession_id); /** @brief Add or modify an SDAP entity based on whether it already exists */ -void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap); +void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t *sdap); /** * @brief Function to delete a single SDAP Entity based on the ue_id and pdusession_id. From 459e20fe99e6b4666e8e2f679ca6ccf2d74a2027 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 18:47:19 +0100 Subject: [PATCH 084/275] E1AP: correctly free entire PDU Free memory including the "base pointer". Fix two places in which it was on the stack to harmonize. The only stack variable is in reception of new E1 messages (e1ap_handle_message()), which uses ASN_STRUCT_FREE_CONTENTS_ONLY(). This fixes bugs similar to these logs: Direct leak of 40 byte(s) in 1 object(s) allocated from: #0 0x7f418f4e68a3 in calloc (/lib64/libasan.so.8+0xe68a3) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #1 0x00000049e367 in calloc_or_fail /home/richie/oai/common/utils/utils.h:74 #2 0x00000049e367 in encode_e1_bearer_context_release_command /home/richie/oai/openair2/E1AP/lib/e1ap_bearer_context_management.c:1235 #3 0x000000482951 in e1apCUCP_send_BEARER_CONTEXT_RELEASE_COMMAND /home/richie/oai/openair2/E1AP/e1ap.c:546 #4 0x000000482951 in E1AP_CUCP_task /home/richie/oai/openair2/E1AP/e1ap.c:856 #5 0x7f418f428ee5 in asan_thread_start(void*) (/lib64/libasan.so.8+0x28ee5) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) Direct leak of 40 byte(s) in 1 object(s) allocated from: #0 0x7f418f4e68a3 in calloc (/lib64/libasan.so.8+0xe68a3) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #1 0x00000049380a in calloc_or_fail /home/richie/oai/common/utils/utils.h:74 #2 0x00000049380a in encode_E1_bearer_context_setup_request /home/richie/oai/openair2/E1AP/lib/e1ap_bearer_context_management.c:578 #3 0x00000047f6a2 in e1apCUCP_send_BEARER_CONTEXT_SETUP_REQUEST /home/richie/oai/openair2/E1AP/e1ap.c:378 #4 0x0000004829f8 in E1AP_CUCP_task /home/richie/oai/openair2/E1AP/e1ap.c:846 #5 0x7f418f428ee5 in asan_thread_start(void*) (/lib64/libasan.so.8+0x28ee5) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) Direct leak of 40 byte(s) in 1 object(s) allocated from: #0 0x7f418f4e68a3 in calloc (/lib64/libasan.so.8+0xe68a3) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #1 0x00000046ede7 in calloc_or_fail /home/richie/oai/common/utils/utils.h:74 #2 0x00000046ede7 in encode_e1ap_cuup_setup_response /home/richie/oai/openair2/E1AP/lib/e1ap_interface_management.c:305 #3 0x00000047e412 in e1ap_send_SETUP_RESPONSE /home/richie/oai/openair2/E1AP/e1ap.c:167 #4 0x000000482a68 in E1AP_CUCP_task /home/richie/oai/openair2/E1AP/e1ap.c:836 #5 0x7f418f428ee5 in asan_thread_start(void*) (/lib64/libasan.so.8+0x28ee5) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) --- openair2/E1AP/e1ap.c | 10 +++++----- openair2/E1AP/e1ap_common.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openair2/E1AP/e1ap.c b/openair2/E1AP/e1ap.c index 1eff4ea529..168366f351 100644 --- a/openair2/E1AP/e1ap.c +++ b/openair2/E1AP/e1ap.c @@ -125,8 +125,8 @@ static void e1_task_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *s int e1ap_send_RESET(bool isCu, sctp_assoc_t assoc_id, E1AP_Reset_t *Reset) { AssertFatal(false, "Not implemented yet\n"); - E1AP_E1AP_PDU_t pdu = {0}; - return e1ap_encode_send(isCu, assoc_id, &pdu, 0, __func__); + E1AP_E1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + return e1ap_encode_send(isCu, assoc_id, pdu, 0, __func__); } int e1ap_send_RESET_ACKNOWLEDGE(instance_t instance, E1AP_Reset_t *Reset) @@ -304,9 +304,9 @@ static void fill_CONFIGURATION_UPDATE(E1AP_E1AP_PDU_t *pdu) void e1apCUUP_send_CONFIGURATION_UPDATE(sctp_assoc_t assoc_id) { - E1AP_E1AP_PDU_t pdu = {0}; - fill_CONFIGURATION_UPDATE(&pdu); - e1ap_encode_send(UPtype, assoc_id, &pdu, 0, __func__); + E1AP_E1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + fill_CONFIGURATION_UPDATE(pdu); + e1ap_encode_send(UPtype, assoc_id, pdu, 0, __func__); } int e1apCUCP_send_gNB_DU_CONFIGURATION_FAILURE(void) diff --git a/openair2/E1AP/e1ap_common.c b/openair2/E1AP/e1ap_common.c index 330d29567a..e102c7e151 100644 --- a/openair2/E1AP/e1ap_common.c +++ b/openair2/E1AP/e1ap_common.c @@ -226,7 +226,7 @@ int e1ap_encode_send(E1_t type, sctp_assoc_t assoc_id, E1AP_E1AP_PDU_t *pdu, uin void *buffer = NULL; ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_E1AP_E1AP_PDU, 0, pdu, &buffer); - ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_E1AP_E1AP_PDU, pdu); + ASN_STRUCT_FREE(asn_DEF_E1AP_E1AP_PDU, pdu); if (encoded < 0) { LOG_E(E1AP, "%s: Failed to encode E1AP message\n", func); From eee9c4cf33dbc9475947762539f06e8ad8440fe6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 18:50:45 +0100 Subject: [PATCH 085/275] Fix two direct memory leaks of CU-UP tester Direct leak of 100 byte(s) in 1 object(s) allocated from: #0 0x7f6585ee6f2b in malloc (/lib64/libasan.so.8+0xe6f2b) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #1 0x000000414a0d in malloc_or_fail /home/richie/oai/common/utils/utils.h:86 #2 0x0000004061c9 in main /home/richie/oai/tests/nr-cuup/nr-cuup-load-test.c:544 #3 0x7f6585211574 in __libc_start_call_main (/lib64/libc.so.6+0x3574) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317) #4 0x7f6585211627 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x3627) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317) #5 0x000000413034 in _start (/home/richie/oai/build/tests/nr-cuup/nr-cuup-load-test+0x413034) (BuildId: 8af0132792b03fa12ba95b5623865c9a8a5625a3) Direct leak of 100 byte(s) in 1 object(s) allocated from: #0 0x7f6585ee6f2b in malloc (/lib64/libasan.so.8+0xe6f2b) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #1 0x000000414a0d in malloc_or_fail /home/richie/oai/common/utils/utils.h:86 #2 0x0000004061ff in main /home/richie/oai/tests/nr-cuup/nr-cuup-load-test.c:546 #3 0x7f6585211574 in __libc_start_call_main (/lib64/libc.so.6+0x3574) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317) #4 0x7f6585211627 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x3627) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317) #5 0x000000413034 in _start (/home/richie/oai/build/tests/nr-cuup/nr-cuup-load-test+0x413034) (BuildId: 8af0132792b03fa12ba95b5623865c9a8a5625a3) --- tests/nr-cuup/nr-cuup-load-test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/nr-cuup/nr-cuup-load-test.c b/tests/nr-cuup/nr-cuup-load-test.c index 533b119c9e..8cc5256744 100644 --- a/tests/nr-cuup/nr-cuup-load-test.c +++ b/tests/nr-cuup/nr-cuup-load-test.c @@ -664,6 +664,9 @@ int main(int argc, char *argv[]) success = false; } + free(ng_ip); + free(f1_ip); + if (success) LOG_A(GNB_APP, "test succeeded\n"); else From 155ee5ad42ee054164a07b3f9f46c78ad86c14cc Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 18:51:49 +0100 Subject: [PATCH 086/275] Fix direct memory leak on E1 message reception Direct leak of 8 byte(s) in 1 object(s) allocated from: #0 0x7f7bc7ee68a3 in calloc (/lib64/libasan.so.8+0xe68a3) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #1 0x0000004abfa6 in calloc_or_fail /home/richie/oai/common/utils/utils.h:74 #2 0x0000004abfa6 in decode_e1ap_cuup_setup_request /home/richie/oai/openair2/E1AP/lib/e1ap_interface_management.c:219 #3 0x00000046a9f5 in e1apCUCP_handle_SETUP_REQUEST /home/richie/oai/openair2/E1AP/e1ap.c:187 #4 0x000000470b35 in e1ap_handle_message /home/richie/oai/openair2/E1AP/e1ap.c:109 #5 0x000000470b35 in e1_task_handle_sctp_data_ind /home/richie/oai/openair2/E1AP/e1ap.c:120 #6 0x00000047717b in E1AP_CUCP_task /home/richie/oai/openair2/E1AP/e1ap.c:832 #7 0x7f7bc7e28ee5 in asan_thread_start(void*) (/lib64/libasan.so.8+0x28ee5) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) Direct leak of 8 byte(s) in 1 object(s) allocated from: #0 0x7f7bc7ee68a3 in calloc (/lib64/libasan.so.8+0xe68a3) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) #1 0x0000004ac30c in calloc_or_fail /home/richie/oai/common/utils/utils.h:74 #2 0x0000004ac30c in decode_e1ap_cuup_setup_request /home/richie/oai/openair2/E1AP/lib/e1ap_interface_management.c:194 #3 0x00000046a9f5 in e1apCUCP_handle_SETUP_REQUEST /home/richie/oai/openair2/E1AP/e1ap.c:187 #4 0x000000470b35 in e1ap_handle_message /home/richie/oai/openair2/E1AP/e1ap.c:109 #5 0x000000470b35 in e1_task_handle_sctp_data_ind /home/richie/oai/openair2/E1AP/e1ap.c:120 #6 0x00000047717b in E1AP_CUCP_task /home/richie/oai/openair2/E1AP/e1ap.c:832 #7 0x7f7bc7e28ee5 in asan_thread_start(void*) (/lib64/libasan.so.8+0x28ee5) (BuildId: 10b8ccd49f75c21babf1d7abe51bb63589d8471f) --- tests/nr-cuup/nr-cuup-load-test.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/nr-cuup/nr-cuup-load-test.c b/tests/nr-cuup/nr-cuup-load-test.c index 8cc5256744..0774b26b1b 100644 --- a/tests/nr-cuup/nr-cuup-load-test.c +++ b/tests/nr-cuup/nr-cuup-load-test.c @@ -28,6 +28,7 @@ #include "openair3/SCTP/sctp_eNB_task.h" #include "openair2/E1AP/e1ap.h" #include "openair3/ocp-gtpu/gtp_itf.h" +#include "openair2/E1AP/lib/e1ap_interface_management.h" configmodule_interface_t *uniqCfg; @@ -90,6 +91,7 @@ static void setup_cuup(sctp_assoc_t *assoc_id, e1ap_nssai_t *nssai) DevAssert(req->plmn[0].slice != NULL); *nssai = *req->plmn[0].slice; DevAssert(assoc_id != 0); + free_e1ap_cuup_setup_request(req); itti_free(TASK_GNB_APP, itti_req); // acknowledge the E1 setup request with a response From a2b127f643ba5095fbc1c3b19f249746706542b1 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 18:54:19 +0100 Subject: [PATCH 087/275] CU-UP: fix direct memory leak Note that this strdup has been intentioally created for debugging purposes in core files. See also: 1ab3b8dd8e0a ("add version signature in core files...") --- executables/nr-cuup.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/executables/nr-cuup.c b/executables/nr-cuup.c index 403f78ddb3..0c2c9cdf3f 100644 --- a/executables/nr-cuup.c +++ b/executables/nr-cuup.c @@ -150,7 +150,8 @@ int main(int argc, char **argv) time_manager_start(tick_functions, tick_functions_count, TIME_SOURCE_REALTIME); // strdup to put the sring in the core file for post mortem identification - LOG_I(HW, "Version: %s\n", strdup(OAI_PACKAGE_VERSION)); + char *v = strdup(OAI_PACKAGE_VERSION); + LOG_I(HW, "Version: %s\n", v); set_softmodem_sighandler(); itti_init(TASK_MAX, tasks_info); int rc; @@ -189,6 +190,8 @@ int main(int argc, char **argv) time_manager_finish(); + free(v); + logClean(); printf("Bye.\n"); return 0; From e9bf6344b01372a1fcd36cbb62381d92b0b276cf Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 18:56:42 +0100 Subject: [PATCH 088/275] SCTP: fix a memory leak call freeaddrinfo() before return'ing from the function. --- openair3/SCTP/sctp_eNB_task.c | 1 + 1 file changed, 1 insertion(+) diff --git a/openair3/SCTP/sctp_eNB_task.c b/openair3/SCTP/sctp_eNB_task.c index 82afa451c0..567a166491 100644 --- a/openair3/SCTP/sctp_eNB_task.c +++ b/openair3/SCTP/sctp_eNB_task.c @@ -477,6 +477,7 @@ static void sctp_handle_new_association_req(const instance_t instance, if (errno != EINPROGRESS) { SCTP_ERROR("Connect failed: %s\n", strerror(errno)); sctp_itti_send_association_resp(requestor, instance, -1, req->ulp_cnx_id, SCTP_STATE_UNREACHABLE, 0, 0); + freeaddrinfo(serv); close(sd); return; } else { From eced5afdf72f95948fdf81452528bfcd469cc23d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 28 Oct 2025 08:25:06 +0100 Subject: [PATCH 089/275] Add CU-UP functional test to ctest Use the CU-UP load tester to test the functionality of the CU-UP and its loader tester. The requirements on this load test are intentionally low, as it will be run with other unit tests in parallel, and should still pass. It uses an existing configuration file for the CU-UP. The test script runs the CU-UP, runs the load tester, stops the CU-UP, and returns the load tester return value as test result (which will be 0=success if all packets have been received). --- tests/nr-cuup/CMakeLists.txt | 10 ++++++++++ tests/nr-cuup/nr-cuup-functional-test.sh | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100755 tests/nr-cuup/nr-cuup-functional-test.sh diff --git a/tests/nr-cuup/CMakeLists.txt b/tests/nr-cuup/CMakeLists.txt index 02becb2496..9d48fd89e2 100644 --- a/tests/nr-cuup/CMakeLists.txt +++ b/tests/nr-cuup/CMakeLists.txt @@ -1,4 +1,14 @@ add_executable(nr-cuup-load-test nr-cuup-load-test.c) +add_dependencies(nr-cuup-load-test params_libconfig) target_link_libraries(nr-cuup-load-test PRIVATE GTPV1U ${T_LIB} utils ITTI e1ap SCTP_CLIENT) target_link_libraries(nr-cuup-load-test PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) # ITTI + +if(ENABLE_TESTS) + # we add also nr-cuup as it is required for the test + add_dependencies(tests nr-cuup-load-test nr-cuup) + add_test(NAME nr_cuup_functional_test + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/nr-cuup-functional-test.sh ${CMAKE_CURRENT_SOURCE_DIR}/load-test.conf + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +endif() diff --git a/tests/nr-cuup/nr-cuup-functional-test.sh b/tests/nr-cuup/nr-cuup-functional-test.sh new file mode 100755 index 0000000000..28a9b3779a --- /dev/null +++ b/tests/nr-cuup/nr-cuup-functional-test.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# this script takes one argument, which is the path to the CU-UP configuration +if [ $# -ne 1 ]; then + echo "usage: $0 " + exit 1 +fi + +CONFIG=$1 + +set -x + +# current dir should be CMAKE_BINARY_DIR +./nr-cuup -O ${CONFIG} & +CUUP_PID=$! + +timeout 5s ./tests/nr-cuup/nr-cuup-load-test -t 3 -d 10 -u 10 +RET=$? + +kill ${CUUP_PID} +exit ${RET} From 3e23c796c9779de32d8d59ea34e81b3362eb9394 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Mon, 27 Oct 2025 16:07:04 +0000 Subject: [PATCH 090/275] CI: ensure clean iperf3 server startup by killing existing processes on same port Before starting a new iperf3 server instance in CI tests, explicitly kill any existing iperf3 processes bound to the same port. This prevents conflicts or failures caused by leftover iperf3 servers from previous runs that did not terminate properly or on time. The cleanup is performed using `pkill` before launching the new server. --- ci-scripts/cls_oaicitest.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 2b31f41064..144bc24da6 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -503,6 +503,11 @@ class OaiCiTest(): t = iperf_time * 2.5 cmd_ue.run(f'rm {client_filename}', reportNonZero=False, silent=True) if cn.runIperf3Server(): + # Clean up any existing iperf3 server processes on this port. + ret = cmd_svr.run(f"{cn.getCmdPrefix()} pkill -f '.*iperf3.*{port}'", reportNonZero=False) + # If pkill succeeds, it means there was a leftover iperf3 server. + if ret.returncode == 0: + logging.warning(f'Iperf3 server on port {port} detected and terminated') cmd_svr.run(f'{cn.getCmdPrefix()} timeout -vk3 {t} iperf3 -s -B {svrIP} -p {port} -1 {jsonReport} >> /dev/null &', timeout=t) cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> {client_filename}', timeout=t) dest_filename = archiveArtifact(cmd_ue, ctx, client_filename) From 9be94881a36feec81d32de1988942568d62bc721 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Tue, 28 Oct 2025 08:41:22 +0000 Subject: [PATCH 091/275] CI: correct testcase description in RFSim5G tests --- ci-scripts/xml_files/container_5g_f1_rfsim.xml | 4 ++-- ci-scripts/xml_files/container_5g_rfsim.xml | 4 ++-- ci-scripts/xml_files/container_5g_rfsim_24prb.xml | 4 ++-- ci-scripts/xml_files/container_5g_rfsim_2x2.xml | 4 ++-- ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml | 4 ++-- ci-scripts/xml_files/container_5g_rfsim_multiue.xml | 4 ++-- ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml | 4 ++-- ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ci-scripts/xml_files/container_5g_f1_rfsim.xml b/ci-scripts/xml_files/container_5g_f1_rfsim.xml index d8dc580a04..49a28e4a9f 100644 --- a/ci-scripts/xml_files/container_5g_f1_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_f1_rfsim.xml @@ -138,7 +138,7 @@ Iperf - Iperf (DL/3Mbps/UDP)(30 sec) + Iperf (DL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_ue localhost @@ -150,7 +150,7 @@ Iperf - Iperf (UL/3Mbps/UDP)(30 sec) + Iperf (UL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 rfsim5g_ue localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim.xml b/ci-scripts/xml_files/container_5g_rfsim.xml index e2566bfd25..89cbe35dfa 100644 --- a/ci-scripts/xml_files/container_5g_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_rfsim.xml @@ -196,7 +196,7 @@ Iperf - Iperf (DL/3Mbps/UDP)(30 sec) + Iperf (DL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_ue localhost @@ -208,7 +208,7 @@ Iperf - Iperf (UL/1Mbps/UDP)(30 sec) + Iperf (UL/1Mbps/UDP)(20 sec) -u -b 1M -t 20 rfsim5g_ue localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_24prb.xml b/ci-scripts/xml_files/container_5g_rfsim_24prb.xml index 849c91a7b0..470c5e9539 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_24prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_24prb.xml @@ -121,7 +121,7 @@ Iperf - Iperf (DL/3Mbps/UDP)(30 sec) + Iperf (DL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_ue localhost @@ -133,7 +133,7 @@ Iperf - Iperf (UL/1Mbps/UDP)(30 sec) + Iperf (UL/1Mbps/UDP)(20 sec) -u -b 1M -t 20 rfsim5g_ue localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_2x2.xml b/ci-scripts/xml_files/container_5g_rfsim_2x2.xml index ed0e11807c..636df9e729 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_2x2.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_2x2.xml @@ -97,7 +97,7 @@ Iperf - Iperf (DL/10Mbps/UDP)(30 sec) + Iperf (DL/10Mbps/UDP)(20 sec) -u -b 10M -t 20 -R rfsim5g_ue localhost @@ -109,7 +109,7 @@ Iperf - Iperf (UL/3Mbps/UDP)(30 sec) + Iperf (UL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 rfsim5g_ue localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml b/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml index 4ff5dbedc8..2a180440d0 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml @@ -97,7 +97,7 @@ Iperf - Iperf (DL/30kbps/UDP)(30 sec) + Iperf (DL/30kbps/UDP)(20 sec) -u -b 0.03M -t 20 -R -c 10.0.1.1 rfsim5g_ue localhost @@ -109,7 +109,7 @@ Iperf - Iperf (UL/30kbps/UDP)(30 sec) + Iperf (UL/30kbps/UDP)(20 sec) -u -b 0.03M -t 20 -c 10.0.1.1 rfsim5g_ue localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_multiue.xml b/ci-scripts/xml_files/container_5g_rfsim_multiue.xml index 215f58c2a7..4296fdf970 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_multiue.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_multiue.xml @@ -155,7 +155,7 @@ Iperf - Iperf (DL/3Mbps/UDP)(30 sec) + Iperf (DL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_multiue1 localhost @@ -167,7 +167,7 @@ Iperf - Iperf (UL/1Mbps/UDP)(30 sec) + Iperf (UL/1Mbps/UDP)(20 sec) -u -b 1M -t 20 rfsim5g_multiue1 localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml b/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml index 5da417c284..814f258e0a 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml @@ -120,7 +120,7 @@ Iperf - Iperf (DL/10Mbps/UDP)(30 sec) + Iperf (DL/10Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_ue localhost @@ -132,7 +132,7 @@ Iperf - Iperf (UL/4Mbps/UDP)(30 sec) + Iperf (UL/4Mbps/UDP)(20 sec) -u -b 1M -t 20 rfsim5g_ue localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml index f70c81732e..c7668c5022 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml @@ -117,7 +117,7 @@ Iperf - Iperf (DL/3Mbps/UDP)(30 sec) + Iperf (DL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_ue localhost @@ -129,7 +129,7 @@ Iperf - Iperf (UL/1Mbps/UDP)(30 sec) + Iperf (UL/1Mbps/UDP)(20 sec) -u -b 1M -t 20 rfsim5g_ue localhost From 7157d4bcd185ee9b64f940a2adb74f2ab2e52e85 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Fri, 19 Sep 2025 11:51:29 +0200 Subject: [PATCH 092/275] add trace when the main NR UE thread ends and when NR UE sets oai_exit variable --- executables/nr-ue.c | 2 +- executables/nr-uesoftmodem.c | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index e7132e15cf..5721b45f8d 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -1091,7 +1091,7 @@ void *UE_thread(void *arg) stream_status = STREAM_STATUS_SYNCED; tx_wait_for_dlsch[slot] = 0; } - + LOG_W(NR_PHY, "UE main thread is ending\n"); return NULL; } diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index fbac6402f5..ca6c48dbef 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -139,10 +139,7 @@ int create_tasks_nrue(uint32_t ue_nb) { void exit_function(const char *file, const char *function, const int line, const char *s, const int assert) { int CC_id; - - if (s != NULL) { - printf("%s:%d %s() Exiting OAI softmodem: %s\n",file,line, function, s); - } + LOG_W(NR_PHY, "called by: %s:%d %s() Exiting OAI softmodem: %s\n", file, line, function, s ? s : "no msg"); oai_exit = 1; @@ -496,9 +493,8 @@ int main(int argc, char **argv) printf("Entering ITTI signals handler\n"); printf("TYPE TO TERMINATE\n"); itti_wait_tasks_end(trigger_deregistration); - printf("Returned from ITTI signal handler\n"); - oai_exit=1; - printf("oai_exit=%d\n",oai_exit); + LOG_W(NR_PHY, "Returned from ITTI signal handler\n"); + oai_exit = 1; if (ouput_vcd) vcd_signal_dumper_close(); From e54738c7e4567c27c41a7b0cbb4ef1f70d95cebc Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 19 Aug 2025 12:12:52 +0200 Subject: [PATCH 093/275] flush command line printf, it is called before log module initialisation --- common/config/config_load_configmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/config/config_load_configmodule.c b/common/config/config_load_configmodule.c index cfe05042ce..f5dfe6137f 100644 --- a/common/config/config_load_configmodule.c +++ b/common/config/config_load_configmodule.c @@ -240,6 +240,7 @@ configmodule_interface_t *load_configmodule(int argc, for (int i=0; i Date: Thu, 14 Aug 2025 12:16:20 +0200 Subject: [PATCH 094/275] Remove prach_ifft from premanent allocation Allocate on the stack when necessary. --- openair1/PHY/INIT/nr_init.c | 7 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 220 +++++++++++---------------- openair1/PHY/defs_gNB.h | 2 - 3 files changed, 87 insertions(+), 142 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 778903be91..23334c8626 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -190,8 +190,7 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) common_vars->debugBuff_sample_offset = 0; // PRACH - prach_vars->rxsigF = (int16_t **)malloc16_clear(Prx*sizeof(int16_t*)); - prach_vars->prach_ifft = (int32_t *)malloc16_clear(1024*2*sizeof(int32_t)); + prach_vars->rxsigF = (int16_t **)malloc16_clear(Prx * sizeof(int16_t *)); init_prach_list(gNB); @@ -266,10 +265,6 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) free_and_zero(common_vars->debugBuff); - NR_gNB_PRACH* prach_vars = &gNB->prach_vars; - free_and_zero(prach_vars->rxsigF); - free_and_zero(prach_vars->prach_ifft); - for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id]; for (int i = 0; i < n_buf; i++) { diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 76d9b1185f..f278d4c845 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -452,81 +452,67 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, { AssertFatal(gNB != NULL, "Can only be called from gNB\n"); nfapi_nr_prach_config_t *cfg = &gNB->gNB_config.prach_config; - NR_DL_FRAME_PARMS *fp; + c16_t **rxsigF = (c16_t **)gNB->prach_vars.rxsigF; + uint16_t preamble_index0 = 0; + uint16_t numshift = 0; + int first_nonzero_root_idx = 0; + bool new_dft = false; + int log2_ifft_size = 10; - uint16_t rootSequenceIndex; - int numrootSequenceIndex; - uint8_t restricted_set; - uint8_t n_ra_prb=0xFF; - int nb_rx; + NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - int16_t **rxsigF = gNB->prach_vars.rxsigF; + const int nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value; + const int rootSequenceIndex = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].prach_root_sequence_index.value; + const int numrootSequenceIndex = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].num_root_sequences.value; + const int NCS = prach_pdu->num_cs; // cfg->num_prach_fd_occasions_list[0].prach_zero_corr_conf.value; + const int prach_sequence_length = cfg->prach_sequence_length.value; + const int msg1_frequencystart = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].k1.value; + const int restricted_set = cfg->restricted_set_config.value; + const int prach_fmt = prach_pdu->prach_format; + const int N_ZC = prach_sequence_length == 0 ? 839 : 139; - uint8_t preamble_index; - uint16_t NCS=99,NCS2; - uint16_t preamble_offset=0,preamble_offset_old; - int16_t preamble_shift=0; - uint32_t preamble_shift2; - uint16_t preamble_index0=0,n_shift_ra=0,n_shift_ra_bar; - uint16_t d_start=0; - uint16_t numshift=0; - const uint16_t *prach_root_sequence_map; - uint8_t not_found; - uint16_t u; - int16_t *Xu=0; - uint16_t first_nonzero_root_idx=0; - uint8_t new_dft=0; - int32_t lev; - int16_t levdB; - int log2_ifft_size=10; - int16_t prach_ifft_tmp[2048*2] __attribute__((aligned(32))); - int32_t *prach_ifft=(int32_t*)NULL; - - fp = &gNB->frame_parms; + LOG_D(PHY, + "L1 PRACH RX: rooSequenceIndex %d, numRootSeqeuences %d, NCS %d, N_ZC %d, format %d \n", + rootSequenceIndex, + numrootSequenceIndex, + NCS, + N_ZC, + prach_fmt); - nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value; - rootSequenceIndex = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].prach_root_sequence_index.value; - numrootSequenceIndex = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].num_root_sequences.value; - NCS = prach_pdu->num_cs;//cfg->num_prach_fd_occasions_list[0].prach_zero_corr_conf.value; - int prach_sequence_length = cfg->prach_sequence_length.value; - int msg1_frequencystart = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].k1.value; - // int num_unused_root_sequences = cfg->num_prach_fd_occasions_list[0].num_unused_root_sequences.value; - // cfg->num_prach_fd_occasions_list[0].unused_root_sequences_list - - restricted_set = cfg->restricted_set_config.value; - - uint8_t prach_fmt = prach_pdu->prach_format; - uint16_t N_ZC = (prach_sequence_length==0)?839:139; - - LOG_D(PHY,"L1 PRACH RX: rooSequenceIndex %d, numRootSeqeuences %d, NCS %d, N_ZC %d, format %d \n",rootSequenceIndex,numrootSequenceIndex,NCS,N_ZC,prach_fmt); - - prach_ifft = gNB->prach_vars.prach_ifft; if (LOG_DEBUGFLAG(DEBUG_PRACH)) { - if ((frame&1023) < 20) LOG_D(PHY,"PRACH (gNB) : running rx_prach for slot %d, msg1_frequencystart %d, rootSequenceIndex %d\n", slot, msg1_frequencystart, rootSequenceIndex); + if ((frame & 1023) < 20) + LOG_D(PHY, + "PRACH (gNB) : running rx_prach for slot %d, msg1_frequencystart %d, rootSequenceIndex %d\n", + slot, + msg1_frequencystart, + rootSequenceIndex); } start_meas(&gNB->rx_prach); - prach_root_sequence_map = (cfg->prach_sequence_length.value==0) ? prach_root_sequence_map_0_3 : prach_root_sequence_map_abc; + const uint16_t *prach_root_sequence_map = + cfg->prach_sequence_length.value == 0 ? prach_root_sequence_map_0_3 : prach_root_sequence_map_abc; // PDP is oversampled, e.g. 1024 sample instead of 839 // Adapt the NCS (zero-correlation zones) with oversampling factor e.g. 1024/839 - NCS2 = (N_ZC==839) ? ((NCS<<10)/839) : ((NCS<<8)/139); + int NCS2 = N_ZC == 839 ? (NCS << 10) / 839 : (NCS << 8) / 139; - if (NCS2==0) NCS2 = N_ZC; + if (NCS2 == 0) + NCS2 = N_ZC; + int preamble_offset = 0, preamble_offset_old = 99; - preamble_offset_old = 99; - - *max_preamble_energy = 0; *max_preamble_delay = 0; *max_preamble = 0; - int16_t prachF[2 * 1024]; - for (preamble_index=0 ; preamble_index<64 ; preamble_index++) { + int16_t preamble_shift = 0; + const int dft_sz = N_ZC == 839 ? 1024 : 256; + int32_t prach_ifft[dft_sz] __attribute__((aligned(32))); + for (int preamble_index = 0; preamble_index < 64; preamble_index++) { if (LOG_DEBUGFLAG(DEBUG_PRACH)) { - int en = dB_fixed(signal_energy((int32_t*)&rxsigF[0][0],(N_ZC==839) ? 840: 140)); - if (en>60) LOG_D(PHY,"frame %d, slot %d : Trying preamble %d \n",frame,slot,preamble_index); + int en = dB_fixed(signal_energy((int32_t *)rxsigF[0], N_ZC == 839 ? 840 : 140)); + if (en > 60) + LOG_D(PHY, "frame %d, slot %d : Trying preamble %d \n", frame, slot, preamble_index); } if (restricted_set == 0) { // This is the relative offset in the root sequence table (5.7.2-4 from 36.211) for the given preamble index @@ -534,48 +520,43 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, if (preamble_offset != preamble_offset_old) { preamble_offset_old = preamble_offset; - new_dft = 1; + new_dft = true; // This is the \nu corresponding to the preamble index preamble_shift = 0; - } - - else { - preamble_shift -= NCS; + } else { + preamble_shift -= NCS; if (preamble_shift < 0) - preamble_shift+=N_ZC; + preamble_shift += N_ZC; } } else { // This is the high-speed case - new_dft = 0; + new_dft = false; uint16_t nr_du[NR_PRACH_SEQ_LEN_L - 1]; nr_fill_du(N_ZC, prach_root_sequence_map, nr_du); // set preamble_offset to initial rootSequenceIndex and look if we need more root sequences for this // preamble index and find the corresponding cyclic shift // Check if all shifts for that root have been processed + int n_shift_ra = 0, n_shift_ra_bar, d_start = 0; if (preamble_index0 == numshift) { - not_found = 1; - new_dft = 1; + bool not_found = true; + new_dft = true; preamble_index0 -= numshift; - //(preamble_offset==0 && numshift==0) ? (preamble_offset) : (preamble_offset++); - - while (not_found == 1) { + while (not_found) { // current root depending on rootSequenceIndex int index = (rootSequenceIndex + preamble_offset) % N_ZC; - - u = prach_root_sequence_map[index]; - + int u = nr_du[prach_root_sequence_map[index]]; uint16_t n_group_ra = 0; - - if ((nr_du[u] < (N_ZC / 3)) && (nr_du[u] >= NCS) ) { - n_shift_ra = nr_du[u] / NCS; - d_start = (nr_du[u] << 1) + (n_shift_ra * NCS); + + if (u < (N_ZC / 3) && u >= NCS) { + n_shift_ra = u / NCS; + d_start = (u << 1) + (n_shift_ra * NCS); n_group_ra = N_ZC / d_start; - n_shift_ra_bar = max(0, (N_ZC-(nr_du[u] << 1) - (n_group_ra * d_start)) / N_ZC); - } else if ((nr_du[u] >= (N_ZC / 3)) && (nr_du[u] <= ((N_ZC - NCS) >> 1))) { - n_shift_ra = (N_ZC - (nr_du[u] << 1)) / NCS; - d_start = N_ZC - (nr_du[u] << 1) + (n_shift_ra * NCS); - n_group_ra = nr_du[u] / d_start; - n_shift_ra_bar = min(n_shift_ra, max(0, (nr_du[u]- (n_group_ra * d_start)) / NCS)); + n_shift_ra_bar = max(0, (N_ZC - (u << 1) - (n_group_ra * d_start)) / N_ZC); + } else if (u >= (N_ZC / 3) && u <= ((N_ZC - NCS) >> 1)) { + n_shift_ra = (N_ZC - (u << 1)) / NCS; + d_start = N_ZC - (u << 1) + (n_shift_ra * NCS); + n_group_ra = u / d_start; + n_shift_ra_bar = min(n_shift_ra, max(0, (u - (n_group_ra * d_start)) / NCS)); } else { n_shift_ra = 0; n_shift_ra_bar = 0; @@ -584,12 +565,13 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, // This is the number of cyclic shifts for the current root u numshift = (n_shift_ra * n_group_ra) + n_shift_ra_bar; // skip to next root and recompute parameters if numshift==0 - (numshift>0) ? (not_found = 0) : (preamble_offset++); + numshift > 0 ? not_found = false : preamble_offset++; } } if (n_shift_ra>0) - preamble_shift = -((d_start * (preamble_index0/n_shift_ra)) + ((preamble_index0%n_shift_ra)*NCS)); // minus because the channel is h(t -\tau + Cv) + preamble_shift = -(d_start * (preamble_index0 / n_shift_ra) + + (preamble_index0 % n_shift_ra) * NCS); // minus because the channel is h(t -\tau + Cv) else preamble_shift = 0; @@ -604,7 +586,7 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, // Compute DFT of RX signal (conjugate input, results in conjugate output) for each new rootSequenceIndex if (LOG_DEBUGFLAG(DEBUG_PRACH)) { - int en = dB_fixed(signal_energy((int32_t*)&rxsigF[0][0],840)); + int en = dB_fixed(signal_energy((int32_t *)rxsigF[0], 840)); if (en>60) LOG_D(PHY, "frame %d, slot %d : preamble index %d, NCS %d, N_ZC/NCS %d: offset %d, preamble shift %d , en %d)\n", @@ -620,42 +602,30 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, LOG_D(PHY,"PRACH RX preamble_index %d, preamble_offset %d\n",preamble_index,preamble_offset); + if (new_dft) { + new_dft = false; - if (new_dft == 1) { - new_dft = 0; - - Xu = (int16_t*)gNB->X_u[preamble_offset-first_nonzero_root_idx]; - + c16_t *Xu = (c16_t *)gNB->X_u[preamble_offset - first_nonzero_root_idx]; LOG_D(PHY,"PRACH RX new dft preamble_offset-first_nonzero_root_idx %d\n",preamble_offset-first_nonzero_root_idx); - memset(prach_ifft,0,((N_ZC==839) ? 2048 : 256)*sizeof(int32_t)); - memset(prachF, 0, sizeof(int16_t) * 2 * 1024); + memset(prach_ifft, 0, sizeof(prach_ifft)); if (LOG_DUMPFLAG(DEBUG_PRACH)) { LOG_M("prach_rxF0.m","prach_rxF0",rxsigF[0],N_ZC,1,1); LOG_M("prach_rxF1.m","prach_rxF1",rxsigF[1],6144,1,1); } - + c16_t prachF[dft_sz] __attribute__((aligned(32))); for (int aa = 0; aa < nb_rx; aa++) { - // Do componentwise product with Xu* on each antenna - - for (int offset = 0; offset < (N_ZC << 1); offset += 2) { - prachF[offset] = (int16_t)(((int32_t)Xu[offset]*rxsigF[aa][offset] + (int32_t)Xu[offset+1]*rxsigF[aa][offset+1])>>15); - prachF[offset+1] = (int16_t)(((int32_t)Xu[offset]*rxsigF[aa][offset+1] - (int32_t)Xu[offset+1]*rxsigF[aa][offset])>>15); + // Do componentwise product with Xu* on each antenna + for (int offset = 0; offset < N_ZC; offset++) { + prachF[offset] = c16MulConjShift(Xu[offset], rxsigF[aa][offset], 15); } - + memset(prachF + N_ZC, 0, sizeof(*prachF) * (dft_sz - N_ZC)); // Now do IFFT of size 1024 (N_ZC=839) or 256 (N_ZC=139) - if (N_ZC == 839) { - idft(IDFT_1024, prachF, prach_ifft_tmp, 1); - // compute energy and accumulate over receive antennas - for (int i = 0; i < 1024; i++) - prach_ifft[i] += (int32_t)prach_ifft_tmp[i<<1]*(int32_t)prach_ifft_tmp[i<<1] + (int32_t)prach_ifft_tmp[1+(i<<1)]*(int32_t)prach_ifft_tmp[1+(i<<1)]; - } else { - idft(IDFT_256, prachF, prach_ifft_tmp, 1); - log2_ifft_size = 8; - // compute energy and accumulate over receive antennas and repetitions for BR - for (int i = 0; i < 256; i++) - prach_ifft[i] += (int32_t)prach_ifft_tmp[i<<1]*(int32_t)prach_ifft_tmp[(i<<1)] + (int32_t)prach_ifft_tmp[1+(i<<1)]*(int32_t)prach_ifft_tmp[1+(i<<1)]; - } + c16_t prach_ifft_tmp[dft_sz] __attribute__((aligned(32))); + idft(get_idft(dft_sz), (int16_t *)prachF, (int16_t *)prach_ifft_tmp, 1); + // compute energy and accumulate over receive antennas + for (int i = 0; i < dft_sz; i++) + prach_ifft[i] += squaredMod(prach_ifft_tmp[i]); if (LOG_DUMPFLAG(DEBUG_PRACH)) { if (aa == 0) @@ -678,14 +648,14 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, } } // new dft - - // check energy in nth time shift, for - preamble_shift2 = ((preamble_shift==0) ? 0 : ((preamble_shift<*max_preamble_energy) { LOG_D(PHY,"preamble_index %d, delay %d en %d dB > %d dB\n",preamble_index,i,levdB,*max_preamble_energy); *max_preamble_energy = levdB; @@ -693,8 +663,7 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, *max_preamble = preamble_index; } } - }// preamble_index - + } // preamble_index // The conversion from *max_preamble_delay from TA value is done here. // It is normalized to the 30.72 Ms/s, considering the numerology, N_RB and the sampling rate @@ -721,23 +690,6 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, } else *TA = *TA/2; - if (LOG_DUMPFLAG(DEBUG_PRACH)) { - //int en = dB_fixed(signal_energy((int32_t*)&rxsigF[0][0],840)); - // if (en>60) { - int k = (12 * n_ra_prb) - 6 * fp->N_RB_UL; - if (k < 0) - k += fp->ofdm_symbol_size; - - k*=12; - k+=13; - k*=2; - - LOG_M("rxsigF.m","prach_rxF", &rxsigF[0][0], 12288, 1, 1); - LOG_M("prach_rxF_comp0.m","prach_rxF_comp0", prachF, 1024, 1, 1); - LOG_M("Xu.m","xu", Xu, N_ZC, 1, 1); - LOG_M("prach_ifft0.m","prach_t0", prach_ifft, 1024, 1, 1); - // } - } stop_meas(&gNB->rx_prach); } diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index ebf459402e..9575e33c7b 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -151,8 +151,6 @@ typedef struct { /// second index: rx antenna [0..63] (hard coded) \note Hard coded array size indexed by \c nb_antennas_rx. /// third index: frequency-domain sample [0..ofdm_symbol_size*12[ int16_t **rxsigF; - /// \brief local buffer to compute prach_ifft - int32_t *prach_ifft; gNB_PRACH_list_t list[NUMBER_OF_NR_PRACH_MAX]; } NR_gNB_PRACH; From f11361343cf6a8e98f0aaa31065ecdcc58d6df28 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 16 Sep 2025 11:55:57 +0200 Subject: [PATCH 095/275] add management of nb tx antennas=0 in rfsimulator code trace the error of 0 antennas, and avoid infinite loop of calls to read 0 bytes --- radio/rfsimulator/simulator.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 13a414fed6..94234a755f 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -688,6 +688,8 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, if (b->conn_sock >= 0) { samplesBlockHeader_t header = {nsamps, nbAnt, timestamp}; + if (!nbAnt) + LOG_E(HW, "rfsimulator sending 0 tx antennas\n"); fullwrite(b->conn_sock, &header, sizeof(header), t); if (nbAnt == 1) { fullwrite(b->conn_sock, samplesVoid[0], sampleToByte(nsamps, nbAnt), t); @@ -796,6 +798,8 @@ static void process_recv_header(rfsimulator_state_t *t, buffer_t *b, bool first_ // We have a transmission hole to fill, like TDD // we create no signal samples up to the beginning of this reception int nbAnt = b->th.nbAnt; + if (!nbAnt) + LOG_E(HW, "rfsimulator receive 0 rx antennas\n"); if (b->th.timestamp - b->lastReceivedTS < CirSize) { // case we wrap at circular buffer end for (uint64_t index = b->lastReceivedTS; index < b->th.timestamp; index++) { @@ -822,6 +826,8 @@ static void process_recv_header(rfsimulator_state_t *t, buffer_t *b, bool first_ b->transferPtr = (char *)&b->circularBuf[(b->lastReceivedTS * b->th.nbAnt) % CirSize]; // we now need to read the samples b->remainToTransfer = sampleToByte(b->th.size, b->th.nbAnt); + if (!b->remainToTransfer) + b->headerMode = true; // We got a header with 0 antennas, no I/Q to read return; } From 7d90d9b23e60355ec104ab15a03334549c3d84fb Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 29 Oct 2025 15:35:04 +0100 Subject: [PATCH 096/275] log: make input parameters const --- common/utils/LOG/log.c | 8 +++++++- common/utils/LOG/log.h | 10 ++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/common/utils/LOG/log.c b/common/utils/LOG/log.c index 1fe2280c15..91c97ac1ac 100644 --- a/common/utils/LOG/log.c +++ b/common/utils/LOG/log.c @@ -145,7 +145,13 @@ static const char *const log_level_highlight_end[] = {LOG_RESET, LOG_RESET, LOG_RESET, LOG_RESET, LOG_RESET, LOG_RESET}; /*!< \brief Optional end-format strings for highlighting */ static void log_output_memory(log_component_t *c, const char *file, const char *func, int line, int comp, int level, const char* format,va_list args); -int write_file_matlab(const char *fname, const char *vname, void *data, int length, int dec, unsigned int format, int multiVec) +int write_file_matlab(const char *fname, + const char *vname, + const void *data, + int length, + int dec, + unsigned int format, + int multiVec) { FILE *fp=NULL; int i; diff --git a/common/utils/LOG/log.h b/common/utils/LOG/log.h index 1f7474253c..a8c7becfdf 100644 --- a/common/utils/LOG/log.h +++ b/common/utils/LOG/log.h @@ -316,8 +316,14 @@ void close_log_mem(void); #define MATLAB_CSHORT_BRACKET1 13 #define MATLAB_CSHORT_BRACKET2 14 #define MATLAB_CSHORT_BRACKET3 15 - -int32_t write_file_matlab(const char *fname, const char *vname, void *data, int length, int dec, unsigned int format, int multiVec); + +int32_t write_file_matlab(const char *fname, + const char *vname, + const void *data, + int length, + int dec, + unsigned int format, + int multiVec); #define write_output(a, b, c, d, e, f) write_file_matlab(a, b, c, d, e, f, 0) /*----------------macro definitions for reading log configuration from the config module */ From a07c03e65f260f2053c52d279de4b364d71d6f16 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 29 Oct 2025 12:14:31 +0100 Subject: [PATCH 097/275] MAC: use pointers, rewrite FAPI PDU alloc --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 5e9cd47a79..88c9c875ea 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -60,7 +60,7 @@ static int16_t ssb_index_from_prach(module_id_t module_idP, gNB_MAC_INST *gNB = RC.nrmac[module_idP]; NR_COMMON_channels_t *cc = &gNB->common_channels[0]; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; - nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0]; + nfapi_nr_config_request_scf_t *cfg = &gNB->config[0]; NR_RACH_ConfigCommon_t *rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; uint8_t config_index = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex; uint8_t fdm = cfg->prach_config.num_prach_fd_occasions.value; @@ -351,10 +351,10 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP) msgacc = initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup; int slots_frame = gNB->frame_structure.numb_slots_frame; int index = ul_buffer_index(frameP, slotP, slots_frame, gNB->UL_tti_req_ahead_size); - nfapi_nr_ul_tti_request_t *UL_tti_req = &RC.nrmac[module_idP]->UL_tti_req_ahead[0][index]; - nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0]; + nfapi_nr_ul_tti_request_t *UL_tti_req = &gNB->UL_tti_req_ahead[0][index]; + nfapi_nr_config_request_scf_t *cfg = &gNB->config[0]; - if (is_ul_slot(slotP, &RC.nrmac[module_idP]->frame_structure)) { + if (is_ul_slot(slotP, &gNB->frame_structure)) { const NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &rach_ConfigCommon->rach_ConfigGeneric; uint8_t config_index = rach_ConfigGeneric->prach_ConfigurationIndex; int slot_index = 0; @@ -390,16 +390,15 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP) uint32_t N_t_slot = cc->prach_info.N_t_slot; uint32_t start_symb = cc->prach_info.start_symbol; for (int fdm_index = 0; fdm_index < fdm; fdm_index++) { // one structure per frequency domain occasion - AssertFatal(UL_tti_req->n_pdus < sizeof(UL_tti_req->pdus_list) / sizeof(UL_tti_req->pdus_list[0]), - "Invalid UL_tti_req->n_pdus %d\n", - UL_tti_req->n_pdus); - - UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE; - UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_prach_pdu_t); - nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[UL_tti_req->n_pdus].prach_pdu; - memset(prach_pdu, 0, sizeof(nfapi_nr_prach_pdu_t)); + AssertFatal(UL_tti_req->n_pdus < sizeofArray(UL_tti_req->pdus_list), "Invalid UL_tti_req->n_pdus %d\n", UL_tti_req->n_pdus); + nfapi_nr_ul_tti_request_number_of_pdus_t *newpdu = UL_tti_req->pdus_list + UL_tti_req->n_pdus; + *newpdu = (nfapi_nr_ul_tti_request_number_of_pdus_t){ + .pdu_type = NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE, + .pdu_size = sizeof(nfapi_nr_prach_pdu_t), + }; UL_tti_req->n_pdus += 1; int num_td_occ = 0; + nfapi_nr_prach_pdu_t *prach_pdu = &newpdu->prach_pdu; for (int td_index = 0; td_index < N_t_slot; td_index++) { uint32_t config_period = cc->prach_info.x; prach_occasion_id = (((frameP % (cc->max_association_period * config_period))/config_period) * cc->total_prach_occasions_per_config_period) + @@ -1123,7 +1122,7 @@ static void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, slot_ const uint16_t mask = SL_to_bitmap(ra->msg3_startsymb, ra->msg3_nbSymb); int slots_frame = mac->frame_structure.numb_slots_frame; int buffer_index = ul_buffer_index(ra->Msg3_frame, ra->Msg3_slot, slots_frame, mac->vrb_map_UL_size); - uint16_t *vrb_map_UL = &RC.nrmac[module_idP]->common_channels[CC_id].vrb_map_UL[ra->Msg3_beam.idx][buffer_index * MAX_BWP_SIZE]; + uint16_t *vrb_map_UL = &mac->common_channels[CC_id].vrb_map_UL[ra->Msg3_beam.idx][buffer_index * MAX_BWP_SIZE]; for (int i = 0; i < ra->msg3_nb_rb; ++i) { AssertFatal(!(vrb_map_UL[i + ra->msg3_first_rb + ra->msg3_bwp_start] & mask), "RB %d in %4d.%2d is already taken, cannot allocate Msg3!\n", @@ -1135,7 +1134,7 @@ static void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, slot_ LOG_D(NR_MAC, "UE %04x: %d.%d RA is active, Msg3 in (%d,%d)\n", UE->rnti, frameP, slotP, ra->Msg3_frame, ra->Msg3_slot); buffer_index = ul_buffer_index(ra->Msg3_frame, ra->Msg3_slot, slots_frame, mac->UL_tti_req_ahead_size); - nfapi_nr_ul_tti_request_t *future_ul_tti_req = &RC.nrmac[module_idP]->UL_tti_req_ahead[CC_id][buffer_index]; + nfapi_nr_ul_tti_request_t *future_ul_tti_req = &mac->UL_tti_req_ahead[CC_id][buffer_index]; AssertFatal(future_ul_tti_req->SFN == ra->Msg3_frame && future_ul_tti_req->Slot == ra->Msg3_slot, "future UL_tti_req's frame.slot %d.%d does not match PUSCH %d.%d\n", From f5c1d18638f6cbf850415f2b36b97e77eb9dd0ff Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Thu, 18 Sep 2025 16:20:08 +0200 Subject: [PATCH 098/275] Fix bug in "freeing" prach occasions scheduler after decoding first occasion Release PRACH only after treating the entire PRACH slot/work. Also, release only once, not on every decoding occasion. --- openair1/SCHED_NR/nr_prach_procedures.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index c56a37ca1d..7dfe05dccd 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -87,8 +87,6 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac &max_preamble[0], &max_preamble_energy[0], &max_preamble_delay[0]); - LOG_D(NR_PHY,"Freeing PRACH entry %d\n",prach_id); - free_nr_prach_entry(gNB,prach_id); LOG_D(NR_PHY,"[RAPROC] Frame %d, slot %d, occasion %d (prachStartSymbol %d) : Most likely preamble %d, energy %d.%d dB delay %d (prach_energy counter %d)\n", frame,slot,prach_oc,prachStartSymbol, max_preamble[0], @@ -142,6 +140,8 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac gNB->prach_energy_counter++; } //if prach_id>0 rach_ind->slot = prach_start_slot; + LOG_D(NR_PHY,"Freeing PRACH entry %d\n",prach_id); + free_nr_prach_entry(gNB,prach_id); } // for NUMBER_OF_NR_PRACH_OCCASION_MAX VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0); } From a4d3eb9fadf0abf02102c11a445cec190d7ab80c Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Thu, 14 Aug 2025 10:10:16 +0200 Subject: [PATCH 099/275] replace fix array index by pointers for prach lists in gNB --- executables/nr-ru.c | 9 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 78 ++++----- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 10 +- openair1/SCHED/nfapi_nr_dummy.c | 16 +- openair1/SCHED_NR/fapi_nr_l1.c | 6 +- openair1/SCHED_NR/nr_prach_procedures.c | 149 +++++++++--------- 6 files changed, 137 insertions(+), 131 deletions(-) diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 20deff5725..bf701f8abc 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1208,8 +1208,8 @@ void *ru_thread(void *param) proc->tti_rx * gNB->frame_parms.samples_per_slot_wCP); // Do PRACH RU processing - int prach_id = find_nr_prach_ru(ru, proc->frame_rx, proc->tti_rx, SEARCH_EXIST); - if (prach_id >= 0) { + RU_PRACH_list_t *p = find_nr_prach_ru(ru, proc->frame_rx, proc->tti_rx, SEARCH_EXIST); + if (p) { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 1 ); T(T_GNB_PHY_PRACH_INPUT_SIGNAL, @@ -1218,12 +1218,11 @@ void *ru_thread(void *param) T_INT(0), T_BUFFER(&ru->common.rxdata[0][fp->get_samples_slot_timestamp(proc->tti_rx - 1, fp, 0) /*-ru->N_TA_offset*/], (fp->get_samples_per_slot(proc->tti_rx - 1, fp) + fp->get_samples_per_slot(proc->tti_rx, fp)) * 4)); - RU_PRACH_list_t *p = ru->prach_list + prach_id; int N_dur = get_nr_prach_duration(p->fmt); for (int prach_oc = 0; prach_oc < p->num_prach_ocas; prach_oc++) { int prachStartSymbol = p->prachStartSymbol + prach_oc * N_dur; - int beam_id = ru->prach_list[prach_id].beam ? ru->prach_list[prach_id].beam[prach_oc] : 0; + int beam_id = p->beam ? p->beam[prach_oc] : 0; //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. rx_nr_prach_ru(ru, p->fmt, // could also use format @@ -1235,7 +1234,7 @@ void *ru_thread(void *param) proc->frame_rx, proc->tti_rx); } - free_nr_ru_prach_entry(ru,prach_id); + free_nr_ru_prach_entry(ru, p); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 0); } // end if (prach_id >= 0) } // end if (ru->feprx) diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index f278d4c845..12c045e551 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -46,34 +46,38 @@ void init_prach_list(PHY_VARS_gNB *gNB) } } -void free_nr_prach_entry(PHY_VARS_gNB *gNB, int prach_id) +void free_nr_prach_entry(PHY_VARS_gNB *gNB, gNB_PRACH_list_t *p) { - gNB->prach_vars.list[prach_id].frame = -1; - gNB->prach_vars.list[prach_id].slot = -1; - gNB->prach_vars.list[prach_id].num_slots = -1; - free_and_zero(gNB->prach_vars.list[prach_id].beam_nb); + for (uint16_t i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++) { + if (p == gNB->prach_vars.list + i) { + p->frame = -1; + p->slot = -1; + p->num_slots = -1; + free_and_zero(p->beam_nb); + break; + } + } } -int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame, int slot, find_type_t type) { - +gNB_PRACH_list_t *find_nr_prach(PHY_VARS_gNB *gNB, int frame, int slot, find_type_t type) +{ AssertFatal(gNB!=NULL,"gNB is null\n"); for (uint16_t i=0; iprach_vars.list + i; LOG_D(PHY, "searching for PRACH in %d.%d prach_index %d=> %d.%d\n", frame, slot, i, p->frame, p->slot); if ((type == SEARCH_EXIST_OR_FREE) && (p->frame == -1) && (p->slot == -1)) { - return i; + return p; } else if ((type == SEARCH_EXIST) && (p->frame == frame) && (p->slot + p->num_slots - 1 == slot)) { - return i; + return p; } } - return -1; + return NULL; } -int nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) +gNB_PRACH_list_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) { - int prach_id = find_nr_prach(gNB, SFN, Slot, SEARCH_EXIST_OR_FREE); - AssertFatal(((prach_id >= 0) && (prach_id < NUMBER_OF_NR_PRACH_MAX)), "illegal or no prach_id found!!! prach_id %d\n", prach_id); - gNB_PRACH_list_t *prach = &gNB->prach_vars.list[prach_id]; + gNB_PRACH_list_t *prach = find_nr_prach(gNB, SFN, Slot, SEARCH_EXIST_OR_FREE); + AssertFatal(prach, "prach list full!!!\n"); prach->frame = SFN; prach->slot = Slot; const int format = prach_pdu->prach_format; @@ -94,9 +98,8 @@ int nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *pr bitmap); } } - LOG_D(NR_PHY,"Copying prach pdu %d bytes to index %d\n", (int)sizeof(*prach_pdu), prach_id); - memcpy(&prach->pdu, prach_pdu, sizeof(*prach_pdu)); - return prach_id; + prach->pdu = *prach_pdu; + return prach; } void init_prach_ru_list(RU_t *ru) @@ -110,7 +113,7 @@ void init_prach_ru_list(RU_t *ru) pthread_mutex_init(&ru->prach_list_mutex, NULL); } -int16_t find_nr_prach_ru(RU_t *ru,int frame,int slot, find_type_t type) +RU_PRACH_list_t *find_nr_prach_ru(RU_t *ru, int frame, int slot, find_type_t type) { AssertFatal(ru != NULL, "ru is null\n"); pthread_mutex_lock(&ru->prach_list_mutex); @@ -119,41 +122,44 @@ int16_t find_nr_prach_ru(RU_t *ru,int frame,int slot, find_type_t type) LOG_D(PHY, "searching for PRACH in %d.%d : prach_index %d=> %d.%d\n", frame, slot, i, p->frame, p->slot); if ((type == SEARCH_EXIST_OR_FREE) && (p->frame == -1) && (p->slot == -1)) { pthread_mutex_unlock(&ru->prach_list_mutex); - return i; + return p; } else if ((type == SEARCH_EXIST) && (p->frame == frame) && (p->slot + p->num_slots - 1 == slot)) { pthread_mutex_unlock(&ru->prach_list_mutex); - return i; + return p; } } pthread_mutex_unlock(&ru->prach_list_mutex); - return -1; + return NULL; } void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beam_id) { - int prach_id = find_nr_prach_ru(ru, SFN, Slot, SEARCH_EXIST_OR_FREE); - AssertFatal((prach_id >= 0) && (prach_id < NUMBER_OF_NR_PRACH_MAX), - "illegal or no prach_id found!!! prach_id %d\n", - prach_id); + RU_PRACH_list_t *prach = find_nr_prach_ru(ru, SFN, Slot, SEARCH_EXIST_OR_FREE); + AssertFatal(prach, "ru prach list full\n"); pthread_mutex_lock(&ru->prach_list_mutex); - ru->prach_list[prach_id].frame = SFN; - ru->prach_list[prach_id].slot = Slot; + prach->frame = SFN; + prach->slot = Slot; const int fmt = prach_pdu->prach_format; - ru->prach_list[prach_id].num_slots = (fmt < 4) ? get_long_prach_dur(fmt, ru->nr_frame_parms->numerology_index) : 1; - ru->prach_list[prach_id].fmt = fmt; - ru->prach_list[prach_id].numRA = prach_pdu->num_ra; - ru->prach_list[prach_id].beam = beam_id; - ru->prach_list[prach_id].prachStartSymbol = prach_pdu->prach_start_symbol; - ru->prach_list[prach_id].num_prach_ocas = prach_pdu->num_prach_ocas; + prach->num_slots = (fmt < 4) ? get_long_prach_dur(fmt, ru->nr_frame_parms->numerology_index) : 1; + prach->fmt = fmt; + prach->numRA = prach_pdu->num_ra; + prach->beam = beam_id; + prach->prachStartSymbol = prach_pdu->prach_start_symbol; + prach->num_prach_ocas = prach_pdu->num_prach_ocas; pthread_mutex_unlock(&ru->prach_list_mutex); } -void free_nr_ru_prach_entry(RU_t *ru, int prach_id) +void free_nr_ru_prach_entry(RU_t *ru, RU_PRACH_list_t *p) { pthread_mutex_lock(&ru->prach_list_mutex); - ru->prach_list[prach_id].frame = -1; - ru->prach_list[prach_id].slot = -1; + for (int i = 0; i < NUMBER_OF_NR_RU_PRACH_MAX; i++) { + if (p == ru->prach_list + i) { + p->frame = -1; + p->slot = -1; + break; + } + } pthread_mutex_unlock(&ru->prach_list_mutex); } diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index a7769dc84d..8547a1415d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -200,7 +200,7 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, int slot, nfapi_nr_pusch_pdu_t *ulsch_pdu); -int nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); +gNB_PRACH_list_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); void rx_nr_prach(PHY_VARS_gNB *gNB, nfapi_nr_prach_pdu_t *prach_pdu, @@ -223,8 +223,8 @@ void rx_nr_prach_ru(RU_t *ru, void nr_fill_prach_ru(RU_t *ru,int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beam_nb); -int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame,int slot, find_type_t type); -int16_t find_nr_prach_ru(RU_t *ru,int frame,int slot, find_type_t type); +gNB_PRACH_list_t *find_nr_prach(PHY_VARS_gNB *gNB, int frame, int slot, find_type_t type); +RU_PRACH_list_t *find_nr_prach_ru(RU_t *ru, int frame, int slot, find_type_t type); void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, @@ -247,10 +247,10 @@ int nr_get_srs_signal(PHY_VARS_gNB *gNB, void init_prach_list(PHY_VARS_gNB *gNB); void init_prach_ru_list(RU_t *ru); -void free_nr_ru_prach_entry(RU_t *ru, int prach_id); +void free_nr_ru_prach_entry(RU_t *ru, RU_PRACH_list_t *p); int get_nr_prach_duration(uint8_t prach_format); -void free_nr_prach_entry(PHY_VARS_gNB *gNB, int prach_id); +void free_nr_prach_entry(PHY_VARS_gNB *gNB, gNB_PRACH_list_t *); void nr_decode_pucch1(c16_t **rxdataF, pucch_GroupHopping_t pucch_GroupHopping, diff --git a/openair1/SCHED/nfapi_nr_dummy.c b/openair1/SCHED/nfapi_nr_dummy.c index 9c9ecea443..4e4dea190d 100644 --- a/openair1/SCHED/nfapi_nr_dummy.c +++ b/openair1/SCHED/nfapi_nr_dummy.c @@ -37,15 +37,13 @@ void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pucch_pdu_t *pucch_pdu){} -void nr_fill_prach(PHY_VARS_gNB *gNB, - int SFN, - int Slot, - nfapi_nr_prach_pdu_t *prach_pdu){} -void nr_fill_prach_ru(RU_t *ru, - int SFN, - int Slot, - nfapi_nr_prach_pdu_t *prach_pdu){} - +gNB_PRACH_list_t nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) +{ +} +RU_PRACH_list_t nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) +{ +} + void nr_phy_config_request(NR_PHY_Config_t *gNB){} void install_nr_schedule_handlers(NR_IF_Module_t *if_inst){} diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 74a2178982..8b88b0195a 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -154,9 +154,9 @@ void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti UL_tti_req->SFN, UL_tti_req->Slot); nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; - int id = nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); - if (gNB->RU_list[0]->if_south == LOCAL_RF || gNB->RU_list[0]->if_south == REMOTE_IF5) - nr_fill_prach_ru(gNB->RU_list[0], UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu, gNB->prach_vars.list[id].beam_nb); + gNB_PRACH_list_t *prach = nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); + if (prach && (gNB->RU_list[0]->if_south == LOCAL_RF || gNB->RU_list[0]->if_south == REMOTE_IF5)) + nr_fill_prach_ru(gNB->RU_list[0], UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu, prach->beam_nb); break; case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: LOG_D(NR_PHY, diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index 7dfe05dccd..9e581e1a00 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -63,85 +63,88 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac ru=gNB->RU_list[0]; - int prach_id = find_nr_prach(gNB, frame, slot, SEARCH_EXIST); - if (prach_id >= 0) { - LOG_D(NR_PHY,"%d.%d Got prach entry id %d\n",frame,slot,prach_id); - nfapi_nr_prach_pdu_t *prach_pdu = &gNB->prach_vars.list[prach_id].pdu; - const int prach_start_slot = gNB->prach_vars.list[prach_id].slot; - uint8_t prachStartSymbol; - int N_dur = get_nr_prach_duration(prach_pdu->prach_format); + gNB_PRACH_list_t *prach_id = find_nr_prach(gNB, frame, slot, SEARCH_EXIST); + if (!prach_id) { + return; + } - for(int prach_oc = 0; prach_oc < prach_pdu->num_prach_ocas; prach_oc++) { - for (ru_aa=0,aa=0;ru_aanb_rx;ru_aa++,aa++) { - gNB->prach_vars.rxsigF[aa] = ru->prach_rxsigF[prach_oc][ru_aa]; - } + LOG_D(NR_PHY, "%d.%d Got prach entry\n", frame, slot); + nfapi_nr_prach_pdu_t *prach_pdu = &prach_id->pdu; + const int prach_start_slot = prach_id->slot; + uint8_t prachStartSymbol; + int N_dur = get_nr_prach_duration(prach_pdu->prach_format); - prachStartSymbol = prach_pdu->prach_start_symbol + prach_oc * N_dur; - //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. + for (int prach_oc = 0; prach_oc < prach_pdu->num_prach_ocas; prach_oc++) { + for (ru_aa = 0, aa = 0; ru_aa < ru->nb_rx; ru_aa++, aa++) { + gNB->prach_vars.rxsigF[aa] = ru->prach_rxsigF[prach_oc][ru_aa]; + } - rx_nr_prach(gNB, - prach_pdu, - prach_oc, - frame, - slot, - &max_preamble[0], - &max_preamble_energy[0], - &max_preamble_delay[0]); - LOG_D(NR_PHY,"[RAPROC] Frame %d, slot %d, occasion %d (prachStartSymbol %d) : Most likely preamble %d, energy %d.%d dB delay %d (prach_energy counter %d)\n", - frame,slot,prach_oc,prachStartSymbol, - max_preamble[0], - max_preamble_energy[0]/10, - max_preamble_energy[0]%10, - max_preamble_delay[0], - gNB->prach_energy_counter); + prachStartSymbol = prach_pdu->prach_start_symbol + prach_oc * N_dur; + // comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is + // given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots + // where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. - if ((gNB->prach_energy_counter == NUM_PRACH_RX_FOR_NOISE_ESTIMATE) - && (max_preamble_energy[0] > gNB->measurements.prach_I0 + gNB->prach_thres) - && (rach_ind->number_of_pdus < MAX_NUM_NR_RX_RACH_PDUS)) { - LOG_A(NR_PHY, - "[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, - max_preamble[0], - max_preamble_energy[0] / 10, - max_preamble_energy[0] % 10, - gNB->measurements.prach_I0, - gNB->prach_thres, - max_preamble_delay[0], - prachStartSymbol, - prach_pdu->num_ra); + rx_nr_prach(gNB, prach_pdu, prach_oc, frame, slot, &max_preamble[0], &max_preamble_energy[0], &max_preamble_delay[0]); + LOG_D(NR_PHY, + "[RAPROC] Frame %d, slot %d, occasion %d (prachStartSymbol %d) : Most likely preamble %d, energy %d.%d dB delay %d " + "(prach_energy counter %d)\n", + frame, + slot, + prach_oc, + prachStartSymbol, + max_preamble[0], + max_preamble_energy[0] / 10, + max_preamble_energy[0] % 10, + max_preamble_delay[0], + gNB->prach_energy_counter); - T(T_ENB_PHY_INITIATE_RA_PROCEDURE, - T_INT(gNB->Mod_id), - T_INT(frame), - T_INT(slot), - T_INT(max_preamble[0]), - T_INT(max_preamble_energy[0]), - T_INT(max_preamble_delay[0])); + if ((gNB->prach_energy_counter == NUM_PRACH_RX_FOR_NOISE_ESTIMATE) + && (max_preamble_energy[0] > gNB->measurements.prach_I0 + gNB->prach_thres) + && (rach_ind->number_of_pdus < MAX_NUM_NR_RX_RACH_PDUS)) { + LOG_A(NR_PHY, + "[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, + max_preamble[0], + max_preamble_energy[0] / 10, + max_preamble_energy[0] % 10, + gNB->measurements.prach_I0, + gNB->prach_thres, + max_preamble_delay[0], + prachStartSymbol, + prach_pdu->num_ra); - nfapi_nr_prach_indication_pdu_t *ind = rach_ind->pdu_list + rach_ind->number_of_pdus; - ind->phy_cell_id = gNB->gNB_config.cell_config.phy_cell_id.value; - ind->symbol_index = prachStartSymbol; - ind->slot_index = slot; - ind->freq_index = prach_pdu->num_ra; - ind->avg_rssi = (max_preamble_energy[0] < 631) ? (128 + (max_preamble_energy[0] / 5)) : 254; - ind->avg_snr = 0xff; // invalid for now + T(T_ENB_PHY_INITIATE_RA_PROCEDURE, + T_INT(gNB->Mod_id), + T_INT(frame), + T_INT(slot), + T_INT(max_preamble[0]), + T_INT(max_preamble_energy[0]), + T_INT(max_preamble_delay[0])); - ind->num_preamble = 1; - ind->preamble_list[0].preamble_index = max_preamble[0]; - ind->preamble_list[0].timing_advance = max_preamble_delay[0]; - ind->preamble_list[0].preamble_pwr = 0xffffffff; - rach_ind->number_of_pdus++; - } - gNB->measurements.prach_I0 = ((gNB->measurements.prach_I0*900)>>10) + ((max_preamble_energy[0]*124)>>10); - if (frame==0) LOG_I(PHY,"prach_I0 = %d.%d dB\n",gNB->measurements.prach_I0/10,gNB->measurements.prach_I0%10); - 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,"Freeing PRACH entry %d\n",prach_id); - free_nr_prach_entry(gNB,prach_id); - } // for NUMBER_OF_NR_PRACH_OCCASION_MAX + nfapi_nr_prach_indication_pdu_t *ind = rach_ind->pdu_list + rach_ind->number_of_pdus; + ind->phy_cell_id = gNB->gNB_config.cell_config.phy_cell_id.value; + ind->symbol_index = prachStartSymbol; + ind->slot_index = slot; + ind->freq_index = prach_pdu->num_ra; + ind->avg_rssi = (max_preamble_energy[0] < 631) ? (128 + (max_preamble_energy[0] / 5)) : 254; + ind->avg_snr = 0xff; // invalid for now + + ind->num_preamble = 1; + ind->preamble_list[0].preamble_index = max_preamble[0]; + ind->preamble_list[0].timing_advance = max_preamble_delay[0]; + ind->preamble_list[0].preamble_pwr = 0xffffffff; + rach_ind->number_of_pdus++; + } + gNB->measurements.prach_I0 = ((gNB->measurements.prach_I0 * 900) >> 10) + ((max_preamble_energy[0] * 124) >> 10); + if (frame == 0) + LOG_I(PHY, "prach_I0 = %d.%d dB\n", gNB->measurements.prach_I0 / 10, gNB->measurements.prach_I0 % 10); + 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, "Freeing PRACH entry\n"); + free_nr_prach_entry(gNB, prach_id); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0); } From 88cecd707be45000c53b39ec5c74cf090bda1d48 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Thu, 14 Aug 2025 10:53:34 +0200 Subject: [PATCH 100/275] regroup similar lists for "ru" and "gNB" in a common definition Merge the identical sizes. Use correct naming: it is a element of a list, not a list --- executables/nr-ru.c | 14 ++--- openair1/PHY/NR_TRANSPORT/nr_prach.c | 54 ++++++++++--------- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 10 ++-- openair1/PHY/defs_RU.h | 14 +---- openair1/PHY/defs_common.h | 25 +++++++++ openair1/PHY/defs_gNB.h | 14 +---- openair1/SCHED_NR/fapi_nr_l1.c | 4 +- openair1/SCHED_NR/nr_prach_procedures.c | 2 +- 8 files changed, 70 insertions(+), 67 deletions(-) diff --git a/executables/nr-ru.c b/executables/nr-ru.c index bf701f8abc..0e507e5260 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1208,7 +1208,7 @@ void *ru_thread(void *param) proc->tti_rx * gNB->frame_parms.samples_per_slot_wCP); // Do PRACH RU processing - RU_PRACH_list_t *p = find_nr_prach_ru(ru, proc->frame_rx, proc->tti_rx, SEARCH_EXIST); + prach_item_t *p = find_nr_prach_ru(ru, proc->frame_rx, proc->tti_rx, SEARCH_EXIST); if (p) { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 1 ); @@ -1218,15 +1218,15 @@ void *ru_thread(void *param) T_INT(0), T_BUFFER(&ru->common.rxdata[0][fp->get_samples_slot_timestamp(proc->tti_rx - 1, fp, 0) /*-ru->N_TA_offset*/], (fp->get_samples_per_slot(proc->tti_rx - 1, fp) + fp->get_samples_per_slot(proc->tti_rx, fp)) * 4)); - int N_dur = get_nr_prach_duration(p->fmt); + int N_dur = get_nr_prach_duration(p->lower.fmt); - for (int prach_oc = 0; prach_oc < p->num_prach_ocas; prach_oc++) { - int prachStartSymbol = p->prachStartSymbol + prach_oc * N_dur; - int beam_id = p->beam ? p->beam[prach_oc] : 0; + for (int prach_oc = 0; prach_oc < p->lower.num_prach_ocas; prach_oc++) { + int prachStartSymbol = p->lower.prachStartSymbol + prach_oc * N_dur; + int beam_id = p->lower.beam ? p->lower.beam[prach_oc] : 0; //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. rx_nr_prach_ru(ru, - p->fmt, // could also use format - p->numRA, + p->lower.fmt, // could also use format + p->lower.numRA, beam_id, prachStartSymbol, p->slot, diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 12c045e551..7329465f02 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -46,24 +46,24 @@ void init_prach_list(PHY_VARS_gNB *gNB) } } -void free_nr_prach_entry(PHY_VARS_gNB *gNB, gNB_PRACH_list_t *p) +void free_nr_prach_entry(PHY_VARS_gNB *gNB, prach_item_t *p) { for (uint16_t i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++) { if (p == gNB->prach_vars.list + i) { p->frame = -1; p->slot = -1; p->num_slots = -1; - free_and_zero(p->beam_nb); + free_and_zero(p->upper.beam_nb); break; } } } -gNB_PRACH_list_t *find_nr_prach(PHY_VARS_gNB *gNB, int frame, int slot, find_type_t type) +prach_item_t *find_nr_prach(PHY_VARS_gNB *gNB, int frame, int slot, find_type_t type) { AssertFatal(gNB!=NULL,"gNB is null\n"); for (uint16_t i=0; iprach_vars.list + i; + prach_item_t *p = gNB->prach_vars.list + i; LOG_D(PHY, "searching for PRACH in %d.%d prach_index %d=> %d.%d\n", frame, slot, i, p->frame, p->slot); if ((type == SEARCH_EXIST_OR_FREE) && (p->frame == -1) && (p->slot == -1)) { return p; @@ -74,28 +74,29 @@ gNB_PRACH_list_t *find_nr_prach(PHY_VARS_gNB *gNB, int frame, int slot, find_typ return NULL; } -gNB_PRACH_list_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) +prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) { - gNB_PRACH_list_t *prach = find_nr_prach(gNB, SFN, Slot, SEARCH_EXIST_OR_FREE); + prach_item_t *prach = find_nr_prach(gNB, SFN, Slot, SEARCH_EXIST_OR_FREE); AssertFatal(prach, "prach list full!!!\n"); prach->frame = SFN; prach->slot = Slot; + prach->type=prach_upper; const int format = prach_pdu->prach_format; prach->num_slots = (format < 4) ? get_long_prach_dur(format, gNB->frame_parms.numerology_index) : 1; if (gNB->common_vars.beam_id) { int n_symb = get_nr_prach_duration(prach_pdu->prach_format); - prach->beam_nb = calloc(prach_pdu->beamforming.dig_bf_interface, sizeof(*prach->beam_nb)); + prach->upper.beam_nb = calloc(prach_pdu->beamforming.dig_bf_interface, sizeof(*prach->upper.beam_nb)); for (int i = 0; i < prach_pdu->beamforming.dig_bf_interface; i++) { 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->beam_nb[i] = beam_index_allocation(gNB->enable_analog_das, - fapi_beam_idx, - &gNB->gNB_config.analog_beamforming_ve, - &gNB->common_vars, - Slot, - NR_NUMBER_OF_SYMBOLS_PER_SLOT, - bitmap); + prach->upper.beam_nb[i] = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, + &gNB->gNB_config.analog_beamforming_ve, + &gNB->common_vars, + Slot, + NR_NUMBER_OF_SYMBOLS_PER_SLOT, + bitmap); } } prach->pdu = *prach_pdu; @@ -105,7 +106,7 @@ gNB_PRACH_list_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_p void init_prach_ru_list(RU_t *ru) { AssertFatal(ru != NULL, "ru is null\n"); - for (int i = 0; i < NUMBER_OF_NR_RU_PRACH_MAX; i++) { + for (int i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++) { ru->prach_list[i].frame = -1; ru->prach_list[i].slot = -1; ru->prach_list[i].num_slots = -1; @@ -113,12 +114,12 @@ void init_prach_ru_list(RU_t *ru) pthread_mutex_init(&ru->prach_list_mutex, NULL); } -RU_PRACH_list_t *find_nr_prach_ru(RU_t *ru, int frame, int slot, find_type_t type) +prach_item_t *find_nr_prach_ru(RU_t *ru, int frame, int slot, find_type_t type) { AssertFatal(ru != NULL, "ru is null\n"); pthread_mutex_lock(&ru->prach_list_mutex); - for (int i = 0; i < NUMBER_OF_NR_RU_PRACH_MAX; i++) { - RU_PRACH_list_t *p = ru->prach_list + i; + for (int i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++) { + prach_item_t *p = ru->prach_list + i; LOG_D(PHY, "searching for PRACH in %d.%d : prach_index %d=> %d.%d\n", frame, slot, i, p->frame, p->slot); if ((type == SEARCH_EXIST_OR_FREE) && (p->frame == -1) && (p->slot == -1)) { pthread_mutex_unlock(&ru->prach_list_mutex); @@ -134,7 +135,7 @@ RU_PRACH_list_t *find_nr_prach_ru(RU_t *ru, int frame, int slot, find_type_t typ void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beam_id) { - RU_PRACH_list_t *prach = find_nr_prach_ru(ru, SFN, Slot, SEARCH_EXIST_OR_FREE); + prach_item_t *prach = find_nr_prach_ru(ru, SFN, Slot, SEARCH_EXIST_OR_FREE); AssertFatal(prach, "ru prach list full\n"); pthread_mutex_lock(&ru->prach_list_mutex); @@ -142,18 +143,19 @@ void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_p prach->slot = Slot; const int fmt = prach_pdu->prach_format; prach->num_slots = (fmt < 4) ? get_long_prach_dur(fmt, ru->nr_frame_parms->numerology_index) : 1; - prach->fmt = fmt; - prach->numRA = prach_pdu->num_ra; - prach->beam = beam_id; - prach->prachStartSymbol = prach_pdu->prach_start_symbol; - prach->num_prach_ocas = prach_pdu->num_prach_ocas; + prach->type=prach_lower; + prach->lower.fmt = fmt; + prach->lower.numRA = prach_pdu->num_ra; + prach->lower.beam = beam_id; + prach->lower.prachStartSymbol = prach_pdu->prach_start_symbol; + prach->lower.num_prach_ocas = prach_pdu->num_prach_ocas; pthread_mutex_unlock(&ru->prach_list_mutex); } -void free_nr_ru_prach_entry(RU_t *ru, RU_PRACH_list_t *p) +void free_nr_ru_prach_entry(RU_t *ru, prach_item_t *p) { pthread_mutex_lock(&ru->prach_list_mutex); - for (int i = 0; i < NUMBER_OF_NR_RU_PRACH_MAX; i++) { + for (int i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++) { if (p == ru->prach_list + i) { p->frame = -1; p->slot = -1; diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 8547a1415d..20568e8020 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -200,7 +200,7 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, int slot, nfapi_nr_pusch_pdu_t *ulsch_pdu); -gNB_PRACH_list_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); +prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); void rx_nr_prach(PHY_VARS_gNB *gNB, nfapi_nr_prach_pdu_t *prach_pdu, @@ -223,8 +223,8 @@ void rx_nr_prach_ru(RU_t *ru, void nr_fill_prach_ru(RU_t *ru,int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beam_nb); -gNB_PRACH_list_t *find_nr_prach(PHY_VARS_gNB *gNB, int frame, int slot, find_type_t type); -RU_PRACH_list_t *find_nr_prach_ru(RU_t *ru, int frame, int slot, find_type_t type); +prach_item_t *find_nr_prach(PHY_VARS_gNB *gNB, int frame, int slot, find_type_t type); +prach_item_t *find_nr_prach_ru(RU_t *ru, int frame, int slot, find_type_t type); void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, @@ -247,10 +247,10 @@ int nr_get_srs_signal(PHY_VARS_gNB *gNB, void init_prach_list(PHY_VARS_gNB *gNB); void init_prach_ru_list(RU_t *ru); -void free_nr_ru_prach_entry(RU_t *ru, RU_PRACH_list_t *p); +void free_nr_ru_prach_entry(RU_t *ru, prach_item_t *p); int get_nr_prach_duration(uint8_t prach_format); -void free_nr_prach_entry(PHY_VARS_gNB *gNB, gNB_PRACH_list_t *); +void free_nr_prach_entry(PHY_VARS_gNB *gNB, prach_item_t *); void nr_decode_pucch1(c16_t **rxdataF, pucch_GroupHopping_t pucch_GroupHopping, diff --git a/openair1/PHY/defs_RU.h b/openair1/PHY/defs_RU.h index e72465c4a9..8116dfbc4b 100644 --- a/openair1/PHY/defs_RU.h +++ b/openair1/PHY/defs_RU.h @@ -190,18 +190,6 @@ typedef struct { task_ans_t *ans; } feptx_cmd_t; -typedef struct { - int frame; - int slot; - int fmt; - int numRA; - int prachStartSymbol; - int num_prach_ocas; - int num_slots; - int *beam; -} RU_PRACH_list_t; - -#define NUMBER_OF_NR_RU_PRACH_MAX 8 #define NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX 12 #define RU_RX_SLOT_DEPTH 4 typedef struct RU_proc_t_s { @@ -605,7 +593,7 @@ typedef struct RU_t_s { /// beamforming weight vectors int32_t **beam_weights[NUMBER_OF_eNB_MAX+1][15]; /// prach commands - RU_PRACH_list_t prach_list[NUMBER_OF_NR_RU_PRACH_MAX]; + prach_item_t prach_list[NUMBER_OF_NR_PRACH_MAX]; /// mutex for prach_list access pthread_mutex_t prach_list_mutex; /// received frequency-domain signal for PRACH (IF4p5 RRU) diff --git a/openair1/PHY/defs_common.h b/openair1/PHY/defs_common.h index b6619efc6e..085fbc15a4 100644 --- a/openair1/PHY/defs_common.h +++ b/openair1/PHY/defs_common.h @@ -998,6 +998,31 @@ typedef struct { bool failed; } decode_abort_t; +// TODO will be removed in a late commit +#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h" +#define NUMBER_OF_NR_PRACH_MAX 8 +typedef struct { + int frame; + int slot; + int num_slots; // prach duration in slots + enum { prach_upper, prach_lower } type; + union { + struct { + // identifier for concurrent beams + int *beam_nb; + nfapi_nr_prach_pdu_t pdu; + } upper; + struct { + int fmt; + int numRA; + int prachStartSymbol; + int num_prach_ocas; + int *beam; + } lower; + }; + nfapi_nr_prach_pdu_t pdu; +} prach_item_t; + static inline void init_abort(decode_abort_t *ab) { int ret = pthread_mutex_init(&ab->mutex_failure, NULL); diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 9575e33c7b..f62d19ba3a 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -134,24 +134,12 @@ typedef struct { nfapi_nr_dl_tti_ssb_pdu ssb_pdu; } NR_gNB_SSB_t; -typedef struct { - int frame; - int slot; - // identifier for concurrent beams - // prach duration in slots - int num_slots; - int *beam_nb; - nfapi_nr_prach_pdu_t pdu; -} gNB_PRACH_list_t; - -#define NUMBER_OF_NR_PRACH_MAX 8 - typedef struct { /// \brief ?. /// second index: rx antenna [0..63] (hard coded) \note Hard coded array size indexed by \c nb_antennas_rx. /// third index: frequency-domain sample [0..ofdm_symbol_size*12[ int16_t **rxsigF; - gNB_PRACH_list_t list[NUMBER_OF_NR_PRACH_MAX]; + prach_item_t list[NUMBER_OF_NR_PRACH_MAX]; } NR_gNB_PRACH; typedef struct { diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 8b88b0195a..fa5167adff 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -154,9 +154,9 @@ void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti UL_tti_req->SFN, UL_tti_req->Slot); nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; - gNB_PRACH_list_t *prach = nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); + prach_item_t *prach = nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); if (prach && (gNB->RU_list[0]->if_south == LOCAL_RF || gNB->RU_list[0]->if_south == REMOTE_IF5)) - nr_fill_prach_ru(gNB->RU_list[0], UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu, prach->beam_nb); + nr_fill_prach_ru(gNB->RU_list[0], UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu, prach->upper.beam_nb); break; case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: LOG_D(NR_PHY, diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index 9e581e1a00..cc38665757 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -63,7 +63,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac ru=gNB->RU_list[0]; - gNB_PRACH_list_t *prach_id = find_nr_prach(gNB, frame, slot, SEARCH_EXIST); + prach_item_t *prach_id = find_nr_prach(gNB, frame, slot, SEARCH_EXIST); if (!prach_id) { return; } From 4dd340b6bb52df20bc7e48f3bc8a6384e800e18f Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Thu, 14 Aug 2025 11:39:36 +0200 Subject: [PATCH 101/275] merge almost duplicated code for ru prach list and gNB prach list (gNB is not the proper name, it is CU+DU, gNB is RU+CU+DU) --- executables/nr-ru.c | 4 +- openair1/PHY/INIT/nr_init.c | 2 +- openair1/PHY/INIT/nr_init_ru.c | 4 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 143 +++++++----------- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 10 +- openair1/PHY/defs_RU.h | 5 +- openair1/PHY/defs_common.h | 11 +- openair1/PHY/defs_gNB.h | 2 +- openair1/SCHED/nfapi_nr_dummy.c | 5 +- openair1/SCHED_NR/nr_prach_procedures.c | 4 +- openair1/SIMULATION/NR_PHY/prachsim.c | 12 +- 11 files changed, 79 insertions(+), 123 deletions(-) diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 0e507e5260..1c2e1acaf1 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1208,7 +1208,7 @@ 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_ru(ru, proc->frame_rx, proc->tti_rx, SEARCH_EXIST); + prach_item_t *p = find_nr_prach(&ru->prach_list, proc->frame_rx, proc->tti_rx, SEARCH_EXIST); if (p) { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 1 ); @@ -1234,7 +1234,7 @@ void *ru_thread(void *param) proc->frame_rx, proc->tti_rx); } - free_nr_ru_prach_entry(ru, p); + free_nr_prach_entry(&ru->prach_list, p); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 0); } // end if (prach_id >= 0) } // end if (ru->feprx) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 23334c8626..75145c78a4 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -192,7 +192,7 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) // PRACH prach_vars->rxsigF = (int16_t **)malloc16_clear(Prx * sizeof(int16_t *)); - init_prach_list(gNB); + init_prach_list(&gNB->prach_vars.list, prach_upper); 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; diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index ca753938a6..6ad085eb4c 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -27,8 +27,6 @@ #include #include "openair1/PHY/defs_RU.h" -void init_prach_ru_list(RU_t *ru); - void nr_phy_init_RU(RU_t *ru) { NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; @@ -132,7 +130,7 @@ void nr_phy_init_RU(RU_t *ru) LOG_D(PHY, "[INIT] %s() ru->num_gNB:%d \n", __FUNCTION__, ru->num_gNB); } // !=IF5 - init_prach_ru_list(ru); + init_prach_list(&ru->prach_list, prach_lower); } void nr_phy_free_RU(RU_t *ru) diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 7329465f02..9fab6254ca 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -36,51 +36,57 @@ #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include "openair1/PHY/NR_TRANSPORT/nr_prach.h" -void init_prach_list(PHY_VARS_gNB *gNB) +void init_prach_list(prach_list_t *l, prach_type_t t) { - AssertFatal(gNB != NULL, "gNB is null\n"); - for (int i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++){ - gNB->prach_vars.list[i].frame = -1; - gNB->prach_vars.list[i].slot = -1; - gNB->prach_vars.list[i].num_slots = -1; - } + pthread_mutex_init(&l->prach_list_mutex, NULL); + for (prach_item_t *p = l->list; p < l->list + NUMBER_OF_NR_PRACH_MAX; p++) + *p = (prach_item_t){.frame = -1, .slot = -1, .type = t}; } -void free_nr_prach_entry(PHY_VARS_gNB *gNB, prach_item_t *p) +void free_nr_prach_entry(prach_list_t *l, prach_item_t *p) { - for (uint16_t i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++) { - if (p == gNB->prach_vars.list + i) { - p->frame = -1; - p->slot = -1; - p->num_slots = -1; - free_and_zero(p->upper.beam_nb); + pthread_mutex_lock(&l->prach_list_mutex); + if (p->type == prach_upper) + free_and_zero(p->upper.beam_nb); + *p = (prach_item_t){.frame = -1, .slot = -1, .num_slots = -1}; + pthread_mutex_unlock(&l->prach_list_mutex); +} + +prach_item_t *find_nr_prach(prach_list_t *l, int frame, int slot, find_type_t type) +{ + pthread_mutex_lock(&l->prach_list_mutex); + prach_item_t *p = l->list; + prach_item_t *end = p + NUMBER_OF_NR_PRACH_MAX; + for (; p < end; p++) + if (p->frame == frame && (p->slot + p->num_slots - 1) == slot) break; + if (p == end) { + if (type == SEARCH_EXIST) { + pthread_mutex_unlock(&l->prach_list_mutex); + return NULL; } + for (p = l->list; p < end; p++) + if (p->frame == -1 && p->slot == -1) + break; } -} - -prach_item_t *find_nr_prach(PHY_VARS_gNB *gNB, int frame, int slot, find_type_t type) -{ - AssertFatal(gNB!=NULL,"gNB is null\n"); - for (uint16_t i=0; iprach_vars.list + i; - LOG_D(PHY, "searching for PRACH in %d.%d prach_index %d=> %d.%d\n", frame, slot, i, p->frame, p->slot); - if ((type == SEARCH_EXIST_OR_FREE) && (p->frame == -1) && (p->slot == -1)) { - return p; - } else if ((type == SEARCH_EXIST) && (p->frame == frame) && (p->slot + p->num_slots - 1 == slot)) { - return p; - } + if (p == end) { + pthread_mutex_unlock(&l->prach_list_mutex); + return NULL; } - return NULL; + // mark it used, it will be filled later + p->frame = frame; + pthread_mutex_unlock(&l->prach_list_mutex); + return p; } prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) { - prach_item_t *prach = find_nr_prach(gNB, SFN, Slot, SEARCH_EXIST_OR_FREE); - AssertFatal(prach, "prach list full!!!\n"); - prach->frame = SFN; - prach->slot = Slot; - prach->type=prach_upper; + prach_item_t *prach = find_nr_prach(&gNB->prach_vars.list, SFN, Slot, SEARCH_EXIST_OR_FREE); + if (!prach) { + LOG_W(PHY, "no free space for a new detected rach, discarding\n"); + return NULL; + } + *prach = (prach_item_t){.frame = SFN, .slot = Slot, .type = prach_upper}; const int format = prach_pdu->prach_format; prach->num_slots = (format < 4) ? get_long_prach_dur(format, gNB->frame_parms.numerology_index) : 1; if (gNB->common_vars.beam_id) { @@ -103,66 +109,23 @@ prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach return prach; } -void init_prach_ru_list(RU_t *ru) -{ - AssertFatal(ru != NULL, "ru is null\n"); - for (int i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++) { - ru->prach_list[i].frame = -1; - ru->prach_list[i].slot = -1; - ru->prach_list[i].num_slots = -1; - } - pthread_mutex_init(&ru->prach_list_mutex, NULL); -} - -prach_item_t *find_nr_prach_ru(RU_t *ru, int frame, int slot, find_type_t type) -{ - AssertFatal(ru != NULL, "ru is null\n"); - pthread_mutex_lock(&ru->prach_list_mutex); - for (int i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++) { - prach_item_t *p = ru->prach_list + i; - LOG_D(PHY, "searching for PRACH in %d.%d : prach_index %d=> %d.%d\n", frame, slot, i, p->frame, p->slot); - if ((type == SEARCH_EXIST_OR_FREE) && (p->frame == -1) && (p->slot == -1)) { - pthread_mutex_unlock(&ru->prach_list_mutex); - return p; - } else if ((type == SEARCH_EXIST) && (p->frame == frame) && (p->slot + p->num_slots - 1 == slot)) { - pthread_mutex_unlock(&ru->prach_list_mutex); - return p; - } - } - pthread_mutex_unlock(&ru->prach_list_mutex); - return NULL; -} - void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beam_id) { - prach_item_t *prach = find_nr_prach_ru(ru, SFN, Slot, SEARCH_EXIST_OR_FREE); - AssertFatal(prach, "ru prach list full\n"); - - pthread_mutex_lock(&ru->prach_list_mutex); - prach->frame = SFN; - prach->slot = Slot; - const int fmt = prach_pdu->prach_format; - prach->num_slots = (fmt < 4) ? get_long_prach_dur(fmt, ru->nr_frame_parms->numerology_index) : 1; - prach->type=prach_lower; - prach->lower.fmt = fmt; - prach->lower.numRA = prach_pdu->num_ra; - prach->lower.beam = beam_id; - prach->lower.prachStartSymbol = prach_pdu->prach_start_symbol; - prach->lower.num_prach_ocas = prach_pdu->num_prach_ocas; - pthread_mutex_unlock(&ru->prach_list_mutex); -} - -void free_nr_ru_prach_entry(RU_t *ru, prach_item_t *p) -{ - pthread_mutex_lock(&ru->prach_list_mutex); - for (int i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++) { - if (p == ru->prach_list + i) { - p->frame = -1; - p->slot = -1; - break; - } + prach_item_t *prach = find_nr_prach(&ru->prach_list, SFN, Slot, SEARCH_EXIST_OR_FREE); + if (!prach) { + LOG_E(PHY, "no prach memory found\n"); + return; } - pthread_mutex_unlock(&ru->prach_list_mutex); + const int fmt = prach_pdu->prach_format; + *prach = (prach_item_t){.frame = SFN, + .slot = Slot, + .type = prach_lower, + .num_slots = (fmt < 4) ? get_long_prach_dur(fmt, ru->nr_frame_parms->numerology_index) : 1, + .lower = {.fmt = fmt, + .numRA = prach_pdu->num_ra, + .beam = beam_id, + .prachStartSymbol = prach_pdu->prach_start_symbol, + .num_prach_ocas = prach_pdu->num_prach_ocas}}; } void rx_nr_prach_ru(RU_t *ru, diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 20568e8020..f43e4cdcdb 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -222,10 +222,7 @@ void rx_nr_prach_ru(RU_t *ru, int subframe); void nr_fill_prach_ru(RU_t *ru,int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beam_nb); - -prach_item_t *find_nr_prach(PHY_VARS_gNB *gNB, int frame, int slot, find_type_t type); -prach_item_t *find_nr_prach_ru(RU_t *ru, int frame, int slot, find_type_t type); - +prach_item_t *find_nr_prach(prach_list_t *, int frame, int slot, find_type_t type); void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, @@ -245,12 +242,9 @@ 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 init_prach_list(PHY_VARS_gNB *gNB); -void init_prach_ru_list(RU_t *ru); -void free_nr_ru_prach_entry(RU_t *ru, prach_item_t *p); int get_nr_prach_duration(uint8_t prach_format); -void free_nr_prach_entry(PHY_VARS_gNB *gNB, prach_item_t *); +void free_nr_prach_entry(prach_list_t *, prach_item_t *); void nr_decode_pucch1(c16_t **rxdataF, pucch_GroupHopping_t pucch_GroupHopping, diff --git a/openair1/PHY/defs_RU.h b/openair1/PHY/defs_RU.h index 8116dfbc4b..3d85c96af8 100644 --- a/openair1/PHY/defs_RU.h +++ b/openair1/PHY/defs_RU.h @@ -592,10 +592,7 @@ typedef struct RU_t_s { int32_t *bw_list[NUMBER_OF_eNB_MAX+1]; /// beamforming weight vectors int32_t **beam_weights[NUMBER_OF_eNB_MAX+1][15]; - /// prach commands - prach_item_t prach_list[NUMBER_OF_NR_PRACH_MAX]; - /// mutex for prach_list access - pthread_mutex_t prach_list_mutex; + prach_list_t prach_list; /// received frequency-domain signal for PRACH (IF4p5 RRU) int16_t **prach_rxsigF[NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX]; /// received frequency-domain signal for PRACH BR (IF4p5 RRU) diff --git a/openair1/PHY/defs_common.h b/openair1/PHY/defs_common.h index 085fbc15a4..79f1763842 100644 --- a/openair1/PHY/defs_common.h +++ b/openair1/PHY/defs_common.h @@ -1001,11 +1001,12 @@ typedef struct { // TODO will be removed in a late commit #include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h" #define NUMBER_OF_NR_PRACH_MAX 8 +typedef enum { prach_upper, prach_lower } prach_type_t; typedef struct { int frame; int slot; int num_slots; // prach duration in slots - enum { prach_upper, prach_lower } type; + prach_type_t type; union { struct { // identifier for concurrent beams @@ -1023,6 +1024,14 @@ typedef struct { nfapi_nr_prach_pdu_t pdu; } 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 *, prach_type_t); + static inline void init_abort(decode_abort_t *ab) { int ret = pthread_mutex_init(&ab->mutex_failure, NULL); diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index f62d19ba3a..da3b4bcb01 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -139,7 +139,7 @@ typedef struct { /// second index: rx antenna [0..63] (hard coded) \note Hard coded array size indexed by \c nb_antennas_rx. /// third index: frequency-domain sample [0..ofdm_symbol_size*12[ int16_t **rxsigF; - prach_item_t list[NUMBER_OF_NR_PRACH_MAX]; + prach_list_t list; } NR_gNB_PRACH; typedef struct { diff --git a/openair1/SCHED/nfapi_nr_dummy.c b/openair1/SCHED/nfapi_nr_dummy.c index 4e4dea190d..87c1ea576f 100644 --- a/openair1/SCHED/nfapi_nr_dummy.c +++ b/openair1/SCHED/nfapi_nr_dummy.c @@ -37,10 +37,7 @@ void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pucch_pdu_t *pucch_pdu){} -gNB_PRACH_list_t nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) -{ -} -RU_PRACH_list_t nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) +void nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) { } diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index cc38665757..b649398456 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -63,7 +63,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac ru=gNB->RU_list[0]; - prach_item_t *prach_id = find_nr_prach(gNB, frame, slot, SEARCH_EXIST); + prach_item_t *prach_id = find_nr_prach(&gNB->prach_vars.list, frame, slot, SEARCH_EXIST); if (!prach_id) { return; } @@ -145,6 +145,6 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac } // if prach_id>0 rach_ind->slot = prach_start_slot; LOG_D(NR_PHY, "Freeing PRACH entry\n"); - free_nr_prach_entry(gNB, prach_id); + free_nr_prach_entry(&gNB->prach_vars.list, prach_id); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0); } diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index 35e242cad3..3eda15fc6f 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -151,9 +151,7 @@ int main(int argc, char **argv){ NR_DL_FRAME_PARMS *frame_parms; nfapi_nr_prach_config_t *prach_config; - nfapi_nr_prach_pdu_t *prach_pdu; fapi_nr_prach_config_t *ue_prach_config; - fapi_nr_ul_config_prach_pdu *ue_prach_pdu; channel_desc_t *UE2gNB; SCM_t channel_model = Rayleigh1; @@ -417,7 +415,6 @@ int main(int argc, char **argv){ ru = RC.ru[0]; frame_parms = &gNB->frame_parms; prach_config = &gNB->gNB_config.prach_config; - prach_pdu = &gNB->prach_vars.list[0].pdu; frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH) s_re = malloc(2*sizeof(double*)); @@ -561,9 +558,6 @@ int main(int argc, char **argv){ prach_config->restricted_set_config.value = restrictedSetConfig; prach_config->prach_sequence_length.value = prach_sequence_length; prach_config->prach_sub_c_spacing.value = mu; - 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; memcpy((void*)&ru->config,(void*)&RC.gNB[0]->gNB_config,sizeof(ru->config)); RC.nb_nr_L1_inst=1; @@ -573,6 +567,10 @@ int main(int argc, char **argv){ phy_init_nr_gNB(gNB); nr_phy_init_RU(ru); + nfapi_nr_prach_pdu_t *prach_pdu = &gNB->prach_vars.list.list[0].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; // Configure UE UE = malloc(sizeof(PHY_VARS_NR_UE)); @@ -588,7 +586,7 @@ int main(int argc, char **argv){ exit(-1); } - ue_prach_pdu = &UE->prach_vars[0]->prach_pdu; + fapi_nr_ul_config_prach_pdu *ue_prach_pdu = &UE->prach_vars[0]->prach_pdu; ue_prach_config = &UE->nrUE_config.prach_config; txdata = UE->common_vars.txData; From cffb07d798c3b3d466d269cd6d0ecd0d2d2296d0 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Thu, 14 Aug 2025 15:17:13 +0200 Subject: [PATCH 102/275] remove permanent prach rxsigF in gNB and simplify the data structure --- executables/nr-gnb.c | 3 +- openair1/PHY/INIT/nr_init.c | 8 ++--- openair1/PHY/INIT/nr_init_ru.c | 8 ++--- openair1/PHY/NR_TRANSPORT/nr_prach.c | 28 ++++++++------- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 3 +- openair1/PHY/defs_RU.h | 2 +- openair1/PHY/defs_gNB.h | 10 +----- openair1/SCHED_NR/nr_prach_procedures.c | 17 ++++----- openair1/SIMULATION/NR_PHY/prachsim.c | 36 ++++++++++--------- 9 files changed, 51 insertions(+), 64 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 16ad366a0e..0e4fba9812 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -389,8 +389,7 @@ void init_eNB_afterRU(void) AssertFatal(gNB->RU_list[ru_id]->common.rxdataF != NULL, "RU %d : common.rxdataF is NULL\n", gNB->RU_list[ru_id]->idx); AssertFatal(gNB->RU_list[ru_id]->prach_rxsigF != NULL, "RU %d : prach_rxsigF is NULL\n", gNB->RU_list[ru_id]->idx); for (int i = 0; i < gNB->RU_list[ru_id]->nb_rx; aa++, i++) { - LOG_I(PHY,"Attaching RU %d antenna %d to gNB antenna %d\n", gNB->RU_list[ru_id]->idx, i, aa); - gNB->prach_vars.rxsigF[aa] = gNB->RU_list[ru_id]->prach_rxsigF[0][i]; + LOG_I(PHY, "Attaching RU %d antenna %d to gNB antenna %d\n", gNB->RU_list[ru_id]->idx, i, aa); for (int b = 0; b < gNB->RU_list[ru_id]->num_beams_period; b++) { int idx = i + b * gNB->RU_list[ru_id]->nb_rx; gNB->common_vars.rxdataF[b][aa] = (c16_t *)gNB->RU_list[ru_id]->common.rxdataF[idx]; diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 75145c78a4..88c539ccd3 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -104,9 +104,7 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) // shortcuts NR_DL_FRAME_PARMS *const fp = &gNB->frame_parms; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; - NR_gNB_COMMON *const common_vars = &gNB->common_vars; - NR_gNB_PRACH *const prach_vars = &gNB->prach_vars; - + NR_gNB_COMMON *const common_vars = &gNB->common_vars; common_vars->analog_bf = cfg->analog_beamforming_ve.analog_bf_vendor_ext.value; LOG_I(PHY, "L1 configured with%s analog beamforming\n", common_vars->analog_bf ? "" : "out"); if (common_vars->analog_bf) { @@ -190,9 +188,7 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) common_vars->debugBuff_sample_offset = 0; // PRACH - prach_vars->rxsigF = (int16_t **)malloc16_clear(Prx * sizeof(int16_t *)); - - init_prach_list(&gNB->prach_vars.list, prach_upper); + init_prach_list(&gNB->prach_list, prach_upper); 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; diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index 6ad085eb4c..d485e4977d 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -116,12 +116,12 @@ void nr_phy_init_RU(RU_t *ru) // AssertFatal(ru->nb_rx <= sizeof(ru->prach_rxsigF) / sizeof(ru->prach_rxsigF[0]), // "nb_antennas_rx too large"); for (int j = 0; j < NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX; j++) { - ru->prach_rxsigF[j] = (int16_t**)malloc(nb_rx_streams * sizeof(int16_t*)); - + ru->prach_rxsigF[j] = malloc(nb_rx_streams * sizeof(*ru->prach_rxsigF)); + for (int i = 0; i < nb_rx_streams; i++) { // largest size for PRACH FFT is 4x98304 (16*24576) - ru->prach_rxsigF[j][i] = (int16_t*)malloc16_clear(4 * 98304 * 2 * sizeof(int16_t)); - LOG_D(PHY,"[INIT] prach_vars->rxsigF[%d] = %p\n", i, ru->prach_rxsigF[j][i]); + ru->prach_rxsigF[j][i] = malloc16_clear(4 * 98304 * 2 * sizeof(**ru->prach_rxsigF)); + LOG_D(PHY, "[INIT] prach_vars->rxsigF[%d] = %p\n", i, ru->prach_rxsigF[j][i]); } } diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 9fab6254ca..371160437a 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -81,7 +81,7 @@ prach_item_t *find_nr_prach(prach_list_t *l, int frame, int slot, find_type_t ty prach_item_t *nr_fill_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_vars.list, SFN, Slot, SEARCH_EXIST_OR_FREE); + prach_item_t *prach = find_nr_prach(&gNB->prach_list, SFN, Slot, SEARCH_EXIST_OR_FREE); if (!prach) { LOG_W(PHY, "no free space for a new detected rach, discarding\n"); return NULL; @@ -171,7 +171,7 @@ void rx_nr_prach_ru(RU_t *ru, prachStartSymbol, prachOccasion); - int16_t **rxsigF = ru->prach_rxsigF[prachOccasion]; + c16_t **rxsigF = (c16_t **)ru->prach_rxsigF[prachOccasion]; AssertFatal(ru->if_south == LOCAL_RF || ru->if_south == REMOTE_IF5, "we shouldn't call this if if_south != LOCAL_RF or REMOTE_IF5\n"); @@ -392,23 +392,25 @@ void rx_nr_prach_ru(RU_t *ru, // do DFT int16_t *prach2 = prach[aa] + (2*Ncp); // times 2 for complex samples for (int i = 0; i < reps; i++) - dft(dftsize, prach2 + 2*dftlen*i, rxsigF[aa] + 2*dftlen*i, 1); + dft(dftsize, prach2 + 2 * dftlen * i, (int16_t *)rxsigF[aa] + 2 * dftlen * i, 1); //LOG_M("ru_rxsigF_tmp.m","rxsFtmp", rxsigF[aa], dftlen*2*reps, 1, 1); //Coherent combining of PRACH repetitions (assumes channel does not change, to be revisted for "long" PRACH) LOG_D(PHY,"Doing PRACH combining of %d reptitions N_ZC %d\n",reps,N_ZC); - int16_t rxsigF_tmp[N_ZC<<1]; - // if (k+N_ZC > dftlen) { // PRACH signal is split around DC - int16_t *rxsigF2=rxsigF[aa]; - int k2=k<<1; + c16_t rxsigF_tmp[N_ZC]; + // if (k+N_ZC > dftlen) { // PRACH signal is split around DC + c16_t *rxsigF2 = rxsigF[aa]; + int k2 = k; - for (int j=0;jgNB_config.prach_config; - c16_t **rxsigF = (c16_t **)gNB->prach_vars.rxsigF; uint16_t preamble_index0 = 0; uint16_t numshift = 0; int first_nonzero_root_idx = 0; diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index f43e4cdcdb..db391dacc0 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -209,7 +209,8 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, int subframe, uint16_t *max_preamble, uint16_t *max_preamble_energy, - uint16_t *max_preamble_delay); + uint16_t *max_preamble_delay, + c16_t **rxsigF); void rx_nr_prach_ru(RU_t *ru, int prach_fmt, diff --git a/openair1/PHY/defs_RU.h b/openair1/PHY/defs_RU.h index 3d85c96af8..b94d0b1c56 100644 --- a/openair1/PHY/defs_RU.h +++ b/openair1/PHY/defs_RU.h @@ -593,7 +593,7 @@ typedef struct RU_t_s { /// beamforming weight vectors int32_t **beam_weights[NUMBER_OF_eNB_MAX+1][15]; prach_list_t prach_list; - /// received frequency-domain signal for PRACH (IF4p5 RRU) + /// received frequency-domain signal for PRACH (IF4p5 RRU) int16_t **prach_rxsigF[NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX]; /// received frequency-domain signal for PRACH BR (IF4p5 RRU) int16_t **prach_rxsigF_br[4]; diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index da3b4bcb01..ad08cc1df1 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -134,14 +134,6 @@ typedef struct { nfapi_nr_dl_tti_ssb_pdu ssb_pdu; } NR_gNB_SSB_t; -typedef struct { - /// \brief ?. - /// second index: rx antenna [0..63] (hard coded) \note Hard coded array size indexed by \c nb_antennas_rx. - /// third index: frequency-domain sample [0..ofdm_symbol_size*12[ - int16_t **rxsigF; - prach_list_t list; -} NR_gNB_PRACH; - typedef struct { uint8_t NumPRSResources; prs_config_t prs_cfg[NR_MAX_PRS_RESOURCES_PER_SET]; @@ -416,7 +408,7 @@ typedef struct PHY_VARS_gNB_s { int max_nb_pusch; NR_gNB_COMMON common_vars; - NR_gNB_PRACH prach_vars; + prach_list_t prach_list; NR_gNB_PRS prs_vars; NR_gNB_PUSCH *pusch_vars; NR_gNB_PUCCH_t *pucch; diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index b649398456..74a98dc7f7 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -52,10 +52,6 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac uint16_t max_preamble[4]={0},max_preamble_energy[4]={0},max_preamble_delay[4]={0}; RU_t *ru; - int aa=0; - int ru_aa; - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,1); rach_ind->sfn = frame; rach_ind->slot = slot; @@ -63,7 +59,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac ru=gNB->RU_list[0]; - prach_item_t *prach_id = find_nr_prach(&gNB->prach_vars.list, frame, slot, SEARCH_EXIST); + prach_item_t *prach_id = find_nr_prach(&gNB->prach_list, frame, slot, SEARCH_EXIST); if (!prach_id) { return; } @@ -75,16 +71,15 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac 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++) { - for (ru_aa = 0, aa = 0; ru_aa < ru->nb_rx; ru_aa++, aa++) { - gNB->prach_vars.rxsigF[aa] = ru->prach_rxsigF[prach_oc][ru_aa]; - } - prachStartSymbol = prach_pdu->prach_start_symbol + prach_oc * N_dur; // comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is // given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots // where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. - rx_nr_prach(gNB, prach_pdu, prach_oc, frame, slot, &max_preamble[0], &max_preamble_energy[0], &max_preamble_delay[0]); + c16_t *rxsigF[ru->nb_rx]; + for (int i = 0; i < ru->nb_rx; ++i) + rxsigF[i] = (c16_t *)ru->prach_rxsigF[prach_oc][i]; + rx_nr_prach(gNB, prach_pdu, prach_oc, frame, slot, &max_preamble[0], &max_preamble_energy[0], &max_preamble_delay[0], rxsigF); LOG_D(NR_PHY, "[RAPROC] Frame %d, slot %d, occasion %d (prachStartSymbol %d) : Most likely preamble %d, energy %d.%d dB delay %d " "(prach_energy counter %d)\n", @@ -145,6 +140,6 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac } // if prach_id>0 rach_ind->slot = prach_start_slot; LOG_D(NR_PHY, "Freeing PRACH entry\n"); - free_nr_prach_entry(&gNB->prach_vars.list, prach_id); + free_nr_prach_entry(&gNB->prach_list, prach_id); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0); } diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index 3eda15fc6f..9f2e06762c 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -567,7 +567,7 @@ int main(int argc, char **argv){ phy_init_nr_gNB(gNB); nr_phy_init_RU(ru); - nfapi_nr_prach_pdu_t *prach_pdu = &gNB->prach_vars.list.list[0].pdu; + nfapi_nr_prach_pdu_t *prach_pdu = &gNB->prach_list.list[0].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; @@ -769,25 +769,27 @@ int main(int argc, char **argv){ nr_slot_fep_ul(frame_parms, ru->common.rxdata[aa], ru->common.rxdataF[aa], l, slot, ru->N_TA_offset); } } + rx_nr_prach_ru(ru, prach_format, numRA, 0, prachStartSymbol, slot, prachOccasion, frame, slot); + c16_t *rxsigF[ru->nb_rx]; + for (int i = 0; i < ru->nb_rx; ++i) + rxsigF[i] = (c16_t *)ru->prach_rxsigF[prachOccasion][i]; + if (n_frames == 1) + printf("ncs %d,num_seq %d\n", + prach_pdu->num_cs, + prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value); + rx_nr_prach(gNB, prach_pdu, prachOccasion, frame, subframe, &preamble_rx, &preamble_energy, &preamble_delay, rxsigF); - rx_nr_prach_ru(ru, prach_format, numRA, 0, prachStartSymbol, slot, prachOccasion, frame, slot); + // printf(" preamble_energy %d preamble_rx %d preamble_tx %d \n", preamble_energy, preamble_rx, preamble_tx); - for (int i = 0; i < ru->nb_rx; ++i) - gNB->prach_vars.rxsigF[i] = ru->prach_rxsigF[prachOccasion][i]; - if (n_frames == 1) printf("ncs %d,num_seq %d\n",prach_pdu->num_cs, prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value); - rx_nr_prach(gNB, prach_pdu, prachOccasion, frame, subframe, &preamble_rx, &preamble_energy, &preamble_delay); + if (preamble_rx != preamble_tx) + prach_errors++; + else + delay_avg += (double)preamble_delay; - // printf(" preamble_energy %d preamble_rx %d preamble_tx %d \n", preamble_energy, preamble_rx, preamble_tx); + N_ZC = (prach_sequence_length) ? 139 : 839; - if (preamble_rx != preamble_tx) - prach_errors++; - else - delay_avg += (double)preamble_delay; - - N_ZC = (prach_sequence_length) ? 139 : 839; - - if (n_frames==1) { - printf("preamble %d (tx %d) : energy %d, delay %d\n",preamble_rx,preamble_tx,preamble_energy,preamble_delay); + if (n_frames == 1) { + printf("preamble %d (tx %d) : energy %d, delay %d\n", preamble_rx, preamble_tx, preamble_energy, preamble_delay); #ifdef NR_PRACH_DEBUG LOG_M("prach0.m","prach0", &txdata[0][prach_start], frame_parms->samples_per_subframe, 1, 1); LOG_M("rxsig0.m","rxs0", &ru->common.rxdata[0][subframe*frame_parms->samples_per_subframe], frame_parms->samples_per_subframe, 1, 1); @@ -797,7 +799,7 @@ int main(int argc, char **argv){ LOG_M("prach_preamble.m","prachp", &gNB->X_u[0], N_ZC, 1, 1); LOG_M("ue_prach_preamble.m","prachp", &UE->X_u[0], N_ZC, 1, 1); #endif - } + } } printf("SNR %f dB, UE Speed %f km/h: errors %u/%d (delay %f)\n", SNR, ue_speed, prach_errors, n_frames, delay_avg/(double)(n_frames-prach_errors)); From d005336cb89ee5237a295cc5fd9897a0bf8d2ff6 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Thu, 14 Aug 2025 15:41:12 +0200 Subject: [PATCH 103/275] Simplify PRACH beam handling, remove race Simplify by removing a small malloc and memcpy() the beams. This should also remove the data race between ru and gNB/CU-DU --- executables/nr-ru.c | 2 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 24 ++++++++++++------------ openair1/PHY/defs_common.h | 3 +-- openair1/SCHED_NR/fapi_nr_l1.c | 2 +- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 1c2e1acaf1..f28f2919c4 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1222,7 +1222,7 @@ void *ru_thread(void *param) for (int prach_oc = 0; prach_oc < p->lower.num_prach_ocas; prach_oc++) { int prachStartSymbol = p->lower.prachStartSymbol + prach_oc * N_dur; - int beam_id = p->lower.beam ? p->lower.beam[prach_oc] : 0; + int beam_id = p->beams[prach_oc]; //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. rx_nr_prach_ru(ru, p->lower.fmt, // could also use format diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 371160437a..3c2eb73e77 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -46,8 +46,6 @@ void init_prach_list(prach_list_t *l, prach_type_t t) void free_nr_prach_entry(prach_list_t *l, prach_item_t *p) { pthread_mutex_lock(&l->prach_list_mutex); - if (p->type == prach_upper) - free_and_zero(p->upper.beam_nb); *p = (prach_item_t){.frame = -1, .slot = -1, .num_slots = -1}; pthread_mutex_unlock(&l->prach_list_mutex); } @@ -91,25 +89,27 @@ prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach prach->num_slots = (format < 4) ? get_long_prach_dur(format, gNB->frame_parms.numerology_index) : 1; if (gNB->common_vars.beam_id) { int n_symb = get_nr_prach_duration(prach_pdu->prach_format); - prach->upper.beam_nb = calloc(prach_pdu->beamforming.dig_bf_interface, sizeof(*prach->upper.beam_nb)); + AssertFatal(prach_pdu->beamforming.dig_bf_interface < NFAPI_MAX_NUM_BG_IF, + "impossible beams size %d\n", + prach_pdu->beamforming.dig_bf_interface); for (int i = 0; i < prach_pdu->beamforming.dig_bf_interface; i++) { 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->upper.beam_nb[i] = beam_index_allocation(gNB->enable_analog_das, - fapi_beam_idx, - &gNB->gNB_config.analog_beamforming_ve, - &gNB->common_vars, - Slot, - NR_NUMBER_OF_SYMBOLS_PER_SLOT, - bitmap); + prach->beams[i] = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, + &gNB->gNB_config.analog_beamforming_ve, + &gNB->common_vars, + Slot, + NR_NUMBER_OF_SYMBOLS_PER_SLOT, + bitmap); } } prach->pdu = *prach_pdu; return prach; } -void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beam_id) +void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beams) { prach_item_t *prach = find_nr_prach(&ru->prach_list, SFN, Slot, SEARCH_EXIST_OR_FREE); if (!prach) { @@ -123,9 +123,9 @@ void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_p .num_slots = (fmt < 4) ? get_long_prach_dur(fmt, ru->nr_frame_parms->numerology_index) : 1, .lower = {.fmt = fmt, .numRA = prach_pdu->num_ra, - .beam = beam_id, .prachStartSymbol = prach_pdu->prach_start_symbol, .num_prach_ocas = prach_pdu->num_prach_ocas}}; + memcpy(prach->beams, beams, sizeof(prach->beams)); } void rx_nr_prach_ru(RU_t *ru, diff --git a/openair1/PHY/defs_common.h b/openair1/PHY/defs_common.h index 79f1763842..b9923a1b8b 100644 --- a/openair1/PHY/defs_common.h +++ b/openair1/PHY/defs_common.h @@ -1006,11 +1006,11 @@ typedef struct { int frame; int slot; int num_slots; // prach duration in slots + int beams[NFAPI_MAX_NUM_BG_IF]; prach_type_t type; union { struct { // identifier for concurrent beams - int *beam_nb; nfapi_nr_prach_pdu_t pdu; } upper; struct { @@ -1018,7 +1018,6 @@ typedef struct { int numRA; int prachStartSymbol; int num_prach_ocas; - int *beam; } lower; }; nfapi_nr_prach_pdu_t pdu; diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index fa5167adff..09419d4e84 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -156,7 +156,7 @@ void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; prach_item_t *prach = nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); if (prach && (gNB->RU_list[0]->if_south == LOCAL_RF || gNB->RU_list[0]->if_south == REMOTE_IF5)) - nr_fill_prach_ru(gNB->RU_list[0], UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu, prach->upper.beam_nb); + nr_fill_prach_ru(gNB->RU_list[0], UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu, prach->beams); break; case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: LOG_D(NR_PHY, From c48e0b1a3223c0666b7bd5576487f7673e2e7168 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Mon, 18 Aug 2025 16:57:47 +0200 Subject: [PATCH 104/275] merge variants of prach_item_t as this is the same in gNB and RU --- executables/nr-ru.c | 10 +++---- openair1/PHY/INIT/nr_init.c | 2 +- openair1/PHY/INIT/nr_init_ru.c | 2 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 26 +++++++------------ .../PHY/NR_TRANSPORT/nr_transport_proto.h | 2 +- openair1/PHY/defs_common.h | 16 +----------- openair1/SCHED/nfapi_lte_dummy.c | 5 +--- openair1/SCHED_NR/fapi_nr_l1.c | 2 +- openair1/SIMULATION/NR_PHY/prachsim.c | 7 ++--- 9 files changed, 24 insertions(+), 48 deletions(-) diff --git a/executables/nr-ru.c b/executables/nr-ru.c index f28f2919c4..ae610beacd 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1218,15 +1218,15 @@ void *ru_thread(void *param) T_INT(0), T_BUFFER(&ru->common.rxdata[0][fp->get_samples_slot_timestamp(proc->tti_rx - 1, fp, 0) /*-ru->N_TA_offset*/], (fp->get_samples_per_slot(proc->tti_rx - 1, fp) + fp->get_samples_per_slot(proc->tti_rx, fp)) * 4)); - int N_dur = get_nr_prach_duration(p->lower.fmt); + int N_dur = get_nr_prach_duration(p->pdu.prach_format); - for (int prach_oc = 0; prach_oc < p->lower.num_prach_ocas; prach_oc++) { - int prachStartSymbol = p->lower.prachStartSymbol + prach_oc * N_dur; + for (int prach_oc = 0; prach_oc < p->pdu.num_prach_ocas; prach_oc++) { + int prachStartSymbol = p->pdu.prach_start_symbol + prach_oc * N_dur; int beam_id = p->beams[prach_oc]; //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. rx_nr_prach_ru(ru, - p->lower.fmt, // could also use format - p->lower.numRA, + p->pdu.prach_format, // could also use format + p->pdu.num_ra, beam_id, prachStartSymbol, p->slot, diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 88c539ccd3..7a360e30ff 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -188,7 +188,7 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) common_vars->debugBuff_sample_offset = 0; // PRACH - init_prach_list(&gNB->prach_list, prach_upper); + init_prach_list(&gNB->prach_list); 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; diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index d485e4977d..30ec333c70 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -130,7 +130,7 @@ void nr_phy_init_RU(RU_t *ru) LOG_D(PHY, "[INIT] %s() ru->num_gNB:%d \n", __FUNCTION__, ru->num_gNB); } // !=IF5 - init_prach_list(&ru->prach_list, prach_lower); + init_prach_list(&ru->prach_list); } void nr_phy_free_RU(RU_t *ru) diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 3c2eb73e77..d1e337ce2e 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -36,11 +36,11 @@ #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, prach_type_t t) +void init_prach_list(prach_list_t *l) { pthread_mutex_init(&l->prach_list_mutex, NULL); for (prach_item_t *p = l->list; p < l->list + NUMBER_OF_NR_PRACH_MAX; p++) - *p = (prach_item_t){.frame = -1, .slot = -1, .type = t}; + *p = (prach_item_t){.frame = -1, .slot = -1}; } void free_nr_prach_entry(prach_list_t *l, prach_item_t *p) @@ -84,9 +84,10 @@ prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach LOG_W(PHY, "no free space for a new detected rach, discarding\n"); return NULL; } - *prach = (prach_item_t){.frame = SFN, .slot = Slot, .type = prach_upper}; - const int format = prach_pdu->prach_format; - prach->num_slots = (format < 4) ? get_long_prach_dur(format, gNB->frame_parms.numerology_index) : 1; + const int fmt = prach_pdu->prach_format; + *prach = (prach_item_t){.frame = SFN, + .slot = Slot, + .num_slots = (fmt < 4) ? get_long_prach_dur(fmt, gNB->frame_parms.numerology_index) : 1}; 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, @@ -109,23 +110,14 @@ prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach return prach; } -void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beams) +void nr_fill_prach_ru(RU_t *ru, prach_item_t *gnb_prach) { - prach_item_t *prach = find_nr_prach(&ru->prach_list, SFN, Slot, SEARCH_EXIST_OR_FREE); + prach_item_t *prach = find_nr_prach(&ru->prach_list, gnb_prach->frame, gnb_prach->slot, SEARCH_EXIST_OR_FREE); if (!prach) { LOG_E(PHY, "no prach memory found\n"); return; } - const int fmt = prach_pdu->prach_format; - *prach = (prach_item_t){.frame = SFN, - .slot = Slot, - .type = prach_lower, - .num_slots = (fmt < 4) ? get_long_prach_dur(fmt, ru->nr_frame_parms->numerology_index) : 1, - .lower = {.fmt = fmt, - .numRA = prach_pdu->num_ra, - .prachStartSymbol = prach_pdu->prach_start_symbol, - .num_prach_ocas = prach_pdu->num_prach_ocas}}; - memcpy(prach->beams, beams, sizeof(prach->beams)); + *prach = *gnb_prach; } void rx_nr_prach_ru(RU_t *ru, diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index db391dacc0..1d1ef0a56d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -222,7 +222,7 @@ void rx_nr_prach_ru(RU_t *ru, int frame, int subframe); -void nr_fill_prach_ru(RU_t *ru,int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beam_nb); +void nr_fill_prach_ru(RU_t *ru,prach_item_t * ); prach_item_t *find_nr_prach(prach_list_t *, int frame, int slot, find_type_t type); void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, diff --git a/openair1/PHY/defs_common.h b/openair1/PHY/defs_common.h index b9923a1b8b..b469555354 100644 --- a/openair1/PHY/defs_common.h +++ b/openair1/PHY/defs_common.h @@ -1001,25 +1001,11 @@ typedef struct { // TODO will be removed in a late commit #include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h" #define NUMBER_OF_NR_PRACH_MAX 8 -typedef enum { prach_upper, prach_lower } prach_type_t; typedef struct { int frame; int slot; int num_slots; // prach duration in slots int beams[NFAPI_MAX_NUM_BG_IF]; - prach_type_t type; - union { - struct { - // identifier for concurrent beams - nfapi_nr_prach_pdu_t pdu; - } upper; - struct { - int fmt; - int numRA; - int prachStartSymbol; - int num_prach_ocas; - } lower; - }; nfapi_nr_prach_pdu_t pdu; } prach_item_t; @@ -1029,7 +1015,7 @@ typedef struct { /// mutex for prach_list access pthread_mutex_t prach_list_mutex; } prach_list_t; -void init_prach_list(prach_list_t *, prach_type_t); +void init_prach_list(prach_list_t *); static inline void init_abort(decode_abort_t *ab) { diff --git a/openair1/SCHED/nfapi_lte_dummy.c b/openair1/SCHED/nfapi_lte_dummy.c index d61f0d0548..5d9b5b7a76 100644 --- a/openair1/SCHED/nfapi_lte_dummy.c +++ b/openair1/SCHED/nfapi_lte_dummy.c @@ -45,10 +45,7 @@ void nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu){} -void nr_fill_prach_ru(RU_t *ru, - int SFN, - int Slot, - nfapi_nr_prach_pdu_t *prach_pdu){} +void nr_fill_prach_ru(RU_t *ru,prach_item_t *gnb_prach){} void nr_phy_config_request(NR_PHY_Config_t *gNB){} diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 09419d4e84..6d0a3ee7da 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -156,7 +156,7 @@ void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; prach_item_t *prach = nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); if (prach && (gNB->RU_list[0]->if_south == LOCAL_RF || gNB->RU_list[0]->if_south == REMOTE_IF5)) - nr_fill_prach_ru(gNB->RU_list[0], UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu, prach->beams); + nr_fill_prach_ru(gNB->RU_list[0], prach); break; case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: LOG_D(NR_PHY, diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index 9f2e06762c..9eb9c5c9c5 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -774,9 +774,10 @@ int main(int argc, char **argv){ for (int i = 0; i < ru->nb_rx; ++i) rxsigF[i] = (c16_t *)ru->prach_rxsigF[prachOccasion][i]; if (n_frames == 1) - printf("ncs %d,num_seq %d\n", - prach_pdu->num_cs, - prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value); + LOG_I(PHY, + "ncs %d,num_seq %d\n", + prach_pdu->num_cs, + prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value); rx_nr_prach(gNB, prach_pdu, prachOccasion, frame, subframe, &preamble_rx, &preamble_energy, &preamble_delay, rxsigF); // printf(" preamble_energy %d preamble_rx %d preamble_tx %d \n", preamble_energy, preamble_rx, preamble_tx); From d24e010367a6c1aeea041e1090fada293622aaca Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Thu, 11 Sep 2025 17:30:46 +0200 Subject: [PATCH 105/275] simplify computation of ru->prach_rxsigF[prachOccasion][aa] --- openair1/PHY/NR_TRANSPORT/nr_prach.c | 66 +++++++------------ .../PHY/NR_TRANSPORT/nr_transport_proto.h | 1 - openair1/SCHED_NR/nr_prach_procedures.c | 2 +- openair1/SIMULATION/NR_PHY/prachsim.c | 2 +- 4 files changed, 27 insertions(+), 44 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index d1e337ce2e..40a2900e15 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -133,8 +133,6 @@ void rx_nr_prach_ru(RU_t *ru, AssertFatal(ru != NULL,"ru is null\n"); NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; - int slot2 = slot; - int16_t *prach[ru->nb_rx]; int prach_sequence_length = ru->config.prach_config.prach_sequence_length.value; int msg1_frequencystart = ru->config.prach_config.num_prach_fd_occasions_list[numRA].k1.value; const uint8_t prach_mu = ru->config.prach_config.prach_sub_c_spacing.value; @@ -162,19 +160,8 @@ void rx_nr_prach_ru(RU_t *ru, numRA, prachStartSymbol, prachOccasion); - - c16_t **rxsigF = (c16_t **)ru->prach_rxsigF[prachOccasion]; - AssertFatal(ru->if_south == LOCAL_RF || ru->if_south == REMOTE_IF5, "we shouldn't call this if if_south != LOCAL_RF or REMOTE_IF5\n"); - - for (int aa = 0; aa < ru->nb_rx; aa++) { - if (prach_sequence_length == 0) - slot2 = prachStartSlot; - int idx = aa + beam * ru->nb_rx; - prach[aa] = (int16_t*)&ru->common.rxdata[idx][fp->get_samples_slot_timestamp(slot2, fp, 0) + sample_offset_slot - ru->N_TA_offset]; - } - int reps; int Ncp; int dftlen; @@ -183,11 +170,11 @@ void rx_nr_prach_ru(RU_t *ru, if (prach_sequence_length == 0) { LOG_D(PHY, "PRACH (ru %d) in %d.%d, format %d, msg1_frequencyStart %d\n", - ru->idx, - frame, - slot2, - prachFormat, - msg1_frequencystart); + ru->idx, + frame, + prachStartSlot, + prachFormat, + msg1_frequencystart); switch (prachFormat) { case 0: reps = 1; @@ -371,7 +358,7 @@ void rx_nr_prach_ru(RU_t *ru, int n_ra_prb = msg1_frequencystart; int k = (12*n_ra_prb) - 6*fp->N_RB_UL; - int N_ZC = (prach_sequence_length==0)?839:139; + int N_ZC = (prach_sequence_length == 0) ? 839 : 139; if (k<0) k+=(fp->ofdm_symbol_size); @@ -379,36 +366,34 @@ void rx_nr_prach_ru(RU_t *ru, k+=kbar; for (int aa=0; aanb_rx; aa++) { - AssertFatal(prach[aa]!=NULL,"prach[%d] is null\n",aa); + int slot2 = prach_sequence_length ? slot : prachStartSlot; + int idx = aa + beam * ru->nb_rx; + c16_t *prach = + (c16_t *)&ru->common.rxdata[idx][fp->get_samples_slot_timestamp(slot2, fp, 0) + sample_offset_slot - ru->N_TA_offset]; // do DFT - int16_t *prach2 = prach[aa] + (2*Ncp); // times 2 for complex samples - for (int i = 0; i < reps; i++) - dft(dftsize, prach2 + 2 * dftlen * i, (int16_t *)rxsigF[aa] + 2 * dftlen * i, 1); - - //LOG_M("ru_rxsigF_tmp.m","rxsFtmp", rxsigF[aa], dftlen*2*reps, 1, 1); - - //Coherent combining of PRACH repetitions (assumes channel does not change, to be revisted for "long" PRACH) - LOG_D(PHY,"Doing PRACH combining of %d reptitions N_ZC %d\n",reps,N_ZC); + c16_t *prach2 = prach + Ncp; c16_t rxsigF_tmp[N_ZC]; - // if (k+N_ZC > dftlen) { // PRACH signal is split around DC - c16_t *rxsigF2 = rxsigF[aa]; - int k2 = k; - - for (int j = 0; j < N_ZC; j++, k2++) { - if (k2 == dftlen) - k2 = 0; - rxsigF_tmp[j] = rxsigF2[k2]; - for (int i = 1; i < reps; i++) - rxsigF_tmp[j] = c16add(rxsigF_tmp[j], rxsigF2[k2 + i * dftlen]); + memset(rxsigF_tmp, 0, sizeof(rxsigF_tmp)); + for (int i = 0; i < reps; i++, prach2 += dftlen) { + c16_t tmp[dftlen] __attribute__((aligned(32))); + dft(dftsize, (int16_t *)prach2, (int16_t *)tmp, 1); + // Coherent combining of PRACH repetitions (assumes channel does not change, to be revisted for "long" PRACH) + LOG_D(PHY, "Doing PRACH combining of %d reptitions N_ZC %d\n", reps, N_ZC); + // if (k+N_ZC > dftlen) { // PRACH signal is split around DC + int k2 = k; + for (int j = 0; j < N_ZC; j++, k2++) { + if (k2 == dftlen) + k2 = 0; + rxsigF_tmp[j] = c16add(rxsigF_tmp[j], tmp[k2]); + } } - memcpy(rxsigF2, rxsigF_tmp, N_ZC * sizeof(*rxsigF2)); + memcpy(ru->prach_rxsigF[prachOccasion][aa], rxsigF_tmp, sizeof(rxsigF_tmp)); } } void rx_nr_prach(PHY_VARS_gNB *gNB, nfapi_nr_prach_pdu_t *prach_pdu, - int prachOccasion, int frame, int slot, uint16_t *max_preamble, @@ -658,4 +643,3 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, stop_meas(&gNB->rx_prach); } - diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 1d1ef0a56d..2cbe1fde77 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -204,7 +204,6 @@ prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach void rx_nr_prach(PHY_VARS_gNB *gNB, nfapi_nr_prach_pdu_t *prach_pdu, - int prachOccasion, int frame, int subframe, uint16_t *max_preamble, diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index 74a98dc7f7..632269eeed 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -79,7 +79,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac c16_t *rxsigF[ru->nb_rx]; for (int i = 0; i < ru->nb_rx; ++i) rxsigF[i] = (c16_t *)ru->prach_rxsigF[prach_oc][i]; - rx_nr_prach(gNB, prach_pdu, prach_oc, frame, slot, &max_preamble[0], &max_preamble_energy[0], &max_preamble_delay[0], rxsigF); + rx_nr_prach(gNB, prach_pdu, frame, slot, max_preamble, max_preamble_energy, max_preamble_delay, rxsigF); LOG_D(NR_PHY, "[RAPROC] Frame %d, slot %d, occasion %d (prachStartSymbol %d) : Most likely preamble %d, energy %d.%d dB delay %d " "(prach_energy counter %d)\n", diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index 9eb9c5c9c5..e5f64fff5d 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -778,7 +778,7 @@ int main(int argc, char **argv){ "ncs %d,num_seq %d\n", prach_pdu->num_cs, prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value); - rx_nr_prach(gNB, prach_pdu, prachOccasion, frame, subframe, &preamble_rx, &preamble_energy, &preamble_delay, rxsigF); + rx_nr_prach(gNB, prach_pdu, frame, subframe, &preamble_rx, &preamble_energy, &preamble_delay, rxsigF); // printf(" preamble_energy %d preamble_rx %d preamble_tx %d \n", preamble_energy, preamble_rx, preamble_tx); From 180f83d196f12463042f6759cfe469367861c7a1 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 29 Oct 2025 12:13:25 +0100 Subject: [PATCH 106/275] Refactor L1_nr_prach_procedures --- openair1/SCHED_NR/nr_prach_procedures.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index 632269eeed..db01294da5 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -67,11 +67,10 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac LOG_D(NR_PHY, "%d.%d Got prach entry\n", frame, slot); nfapi_nr_prach_pdu_t *prach_pdu = &prach_id->pdu; const int prach_start_slot = prach_id->slot; - uint8_t prachStartSymbol; 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++) { - prachStartSymbol = prach_pdu->prach_start_symbol + prach_oc * N_dur; + uint prachStartSymbol = prach_pdu->prach_start_symbol + prach_oc * N_dur; // comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is // given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots // where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. @@ -119,17 +118,16 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac T_INT(max_preamble_delay[0])); nfapi_nr_prach_indication_pdu_t *ind = rach_ind->pdu_list + rach_ind->number_of_pdus; - ind->phy_cell_id = gNB->gNB_config.cell_config.phy_cell_id.value; - ind->symbol_index = prachStartSymbol; - ind->slot_index = slot; - ind->freq_index = prach_pdu->num_ra; - ind->avg_rssi = (max_preamble_energy[0] < 631) ? (128 + (max_preamble_energy[0] / 5)) : 254; - ind->avg_snr = 0xff; // invalid for now - - ind->num_preamble = 1; - ind->preamble_list[0].preamble_index = max_preamble[0]; - ind->preamble_list[0].timing_advance = max_preamble_delay[0]; - ind->preamble_list[0].preamble_pwr = 0xffffffff; + *ind = (nfapi_nr_prach_indication_pdu_t){ + .phy_cell_id = gNB->gNB_config.cell_config.phy_cell_id.value, + .symbol_index = prachStartSymbol, + .slot_index = slot, + .freq_index = prach_pdu->num_ra, + .avg_rssi = (max_preamble_energy[0] < 631) ? (128 + (max_preamble_energy[0] / 5)) : 254, + .avg_snr = 0xff, // invalid for now + .num_preamble = 1, + .preamble_list = { + {.preamble_index = max_preamble[0], .timing_advance = max_preamble_delay[0], .preamble_pwr = 0xffffffff}}}; rach_ind->number_of_pdus++; } gNB->measurements.prach_I0 = ((gNB->measurements.prach_I0 * 900) >> 10) + ((max_preamble_energy[0] * 124) >> 10); From 4f39ba8f513519446714f99d3b0d2f6929f9cea5 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 29 Oct 2025 12:10:40 +0100 Subject: [PATCH 107/275] create a trace group for nr rach --- common/utils/LOG/log.h | 5 +++-- common/utils/T/T_messages.txt | 21 +++++++++++++++++++++ executables/nr-ru.c | 2 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 15 +++++++++++---- openair1/SCHED_NR/fapi_nr_l1.c | 1 + openair1/SCHED_NR/nr_prach_procedures.c | 4 ++-- 6 files changed, 39 insertions(+), 9 deletions(-) diff --git a/common/utils/LOG/log.h b/common/utils/LOG/log.h index a8c7becfdf..c873b0d418 100644 --- a/common/utils/LOG/log.h +++ b/common/utils/LOG/log.h @@ -168,8 +168,9 @@ static const char *const flag_name[] = {FOREACH_FLAG(FLAG_TEXT) ""}; COMP_DEF(GNB_APP, log) \ COMP_DEF(NR_RRC, log) \ COMP_DEF(NR_MAC, log) \ - COMP_DEF(NR_MAC_DCI, log) \ - COMP_DEF(NR_PHY_DCI, log) \ + COMP_DEF(NR_MAC_DCI, log) \ + COMP_DEF(NR_PHY_DCI, log) \ + COMP_DEF(NR_PHY_RACH, log) \ COMP_DEF(NR_PHY, log) \ COMP_DEF(LOADER, log) \ COMP_DEF(ASN1, log) \ diff --git a/common/utils/T/T_messages.txt b/common/utils/T/T_messages.txt index 2f17b62ea5..cd81deda2b 100644 --- a/common/utils/T/T_messages.txt +++ b/common/utils/T/T_messages.txt @@ -519,6 +519,27 @@ ID = LEGACY_NR_PHY_TRACE GROUP = ALL:LEGACY_NR_PHY:LEGACY_GROUP_TRACE:LEGACY FORMAT = string,log +ID = LEGACY_NR_PHY_RACH_INFO + DESC = NR_PHY_RACH legacy logs - info level + GROUP = ALL:LEGACY_NR_PHY_RACH:LEGACY_GROUP_INFO:LEGACY + FORMAT = string,log +ID = LEGACY_NR_PHY_RACH_ERROR + DESC = NR_PHY_RACH legacy logs - error level + GROUP = ALL:LEGACY_NR_PHY_RACH:LEGACY_GROUP_ERROR:LEGACY + FORMAT = string,log +ID = LEGACY_NR_PHY_RACH_WARNING + DESC = NR_PHY_RACH legacy logs - warning level + GROUP = ALL:LEGACY_NR_PHY_RACH:LEGACY_GROUP_WARNING:LEGACY + FORMAT = string,log +ID = LEGACY_NR_PHY_RACH_DEBUG + DESC = NR_PHY_RACH legacy logs - debug level + GROUP = ALL:LEGACY_NR_PHY_RACH:LEGACY_GROUP_DEBUG:LEGACY + FORMAT = string,log +ID = LEGACY_NR_PHY_RACH_TRACE + DESC = NR_PHY_RACH legacy logs - trace level + GROUP = ALL:LEGACY_NR_PHY_RACH:LEGACY_GROUP_TRACE:LEGACY + FORMAT = string,log + ID = LEGACY_S1AP_INFO DESC = S1AP legacy logs - info level GROUP = ALL:LEGACY_S1AP:LEGACY_GROUP_INFO:LEGACY diff --git a/executables/nr-ru.c b/executables/nr-ru.c index ae610beacd..60270d9d0f 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1219,7 +1219,7 @@ void *ru_thread(void *param) T_BUFFER(&ru->common.rxdata[0][fp->get_samples_slot_timestamp(proc->tti_rx - 1, fp, 0) /*-ru->N_TA_offset*/], (fp->get_samples_per_slot(proc->tti_rx - 1, fp) + fp->get_samples_per_slot(proc->tti_rx, fp)) * 4)); int N_dur = get_nr_prach_duration(p->pdu.prach_format); - + LOG_D(NR_PHY_RACH, "%d.%d try to decode %d occasions \n", frame, slot, p->pdu.num_prach_ocas); for (int prach_oc = 0; prach_oc < p->pdu.num_prach_ocas; prach_oc++) { int prachStartSymbol = p->pdu.prach_start_symbol + prach_oc * N_dur; int beam_id = p->beams[prach_oc]; diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 40a2900e15..425e20dd84 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -46,6 +46,8 @@ void init_prach_list(prach_list_t *l) void free_nr_prach_entry(prach_list_t *l, prach_item_t *p) { 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}; pthread_mutex_unlock(&l->prach_list_mutex); } @@ -421,7 +423,7 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, const int prach_fmt = prach_pdu->prach_format; const int N_ZC = prach_sequence_length == 0 ? 839 : 139; - LOG_D(PHY, + LOG_D(NR_PHY_RACH, "L1 PRACH RX: rooSequenceIndex %d, numRootSeqeuences %d, NCS %d, N_ZC %d, format %d \n", rootSequenceIndex, numrootSequenceIndex, @@ -550,7 +552,12 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, en); } - LOG_D(PHY,"PRACH RX preamble_index %d, preamble_offset %d\n",preamble_index,preamble_offset); + LOG_D(NR_PHY_RACH, + "PRACH RX preamble_index %d, preamble_offset %d, preamb shift %d new dft %d\n", + preamble_index, + preamble_offset, + preamble_shift, + new_dft); if (new_dft) { new_dft = false; @@ -606,8 +613,8 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, for (int i = 0; i < NCS2; i++) { int lev = prach_ifft[preamble_shift2 + i]; int levdB = dB_fixed_times10(lev); - if (levdB>*max_preamble_energy) { - LOG_D(PHY,"preamble_index %d, delay %d en %d dB > %d dB\n",preamble_index,i,levdB,*max_preamble_energy); + if (levdB > *max_preamble_energy || (levdB == *max_preamble_energy && *max_preamble_delay > i)) { + LOG_D(NR_PHY_RACH, "preamble_index %d, delay %d en %d dB > %d dB\n", preamble_index, i, levdB, *max_preamble_energy); *max_preamble_energy = levdB; *max_preamble_delay = i; // Note: This has to be normalized to the 30.72 Ms/s sampling rate *max_preamble = preamble_index; diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 6d0a3ee7da..ad6f90e6e8 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -154,6 +154,7 @@ void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti UL_tti_req->SFN, UL_tti_req->Slot); nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; + LOG_D(NR_PHY_RACH, "Add prach decoding request for %d.%d\n", UL_tti_req->SFN, UL_tti_req->Slot); prach_item_t *prach = nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); if (prach && (gNB->RU_list[0]->if_south == LOCAL_RF || gNB->RU_list[0]->if_south == REMOTE_IF5)) nr_fill_prach_ru(gNB->RU_list[0], prach); diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index db01294da5..caaa579ad0 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -64,8 +64,8 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac return; } - LOG_D(NR_PHY, "%d.%d Got prach entry\n", frame, slot); 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); @@ -137,7 +137,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac gNB->prach_energy_counter++; } // if prach_id>0 rach_ind->slot = prach_start_slot; - LOG_D(NR_PHY, "Freeing PRACH entry\n"); + LOG_D(NR_PHY_RACH, "Freeing PRACH entry\n"); free_nr_prach_entry(&gNB->prach_list, prach_id); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0); } From d745e6fcd313fa18f98b48b86dd46b24571b9440 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Fri, 19 Sep 2025 15:08:26 +0200 Subject: [PATCH 108/275] Make the decidated structure the PRACH decoding values contextual The function rx_nr_prach become autonomous, it doesn't read anymore global variables So, it can become (later) a threadpool work, that will send a message when it finishes rx_nr_prach_ru() still use some global variables, but it is executed in sequence, inside the ru main loop, that limits the probability of race conditions --- executables/nr-ru.c | 52 +-- openair1/PHY/NR_TRANSPORT/nr_prach.c | 366 +++++++++--------- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 24 +- openair1/PHY/defs_common.h | 11 + openair1/SCHED_NR/fapi_nr_l1.c | 4 + openair1/SCHED_NR/nr_prach_procedures.c | 41 +- openair1/SIMULATION/NR_PHY/prachsim.c | 114 ++++-- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 7 +- 8 files changed, 314 insertions(+), 305 deletions(-) diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 60270d9d0f..9e4e6a5384 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1034,7 +1034,7 @@ void *ru_thread(void *param) RU_t *ru = (RU_t *)param; RU_proc_t *proc = &ru->proc; NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; - PHY_VARS_gNB *gNB = RC.gNB[0]; + PHY_VARS_gNB *gNB = RC.gNB[0]; // this RU main loop handes only one RU int ret; int slot = fp->slots_per_frame-1; int frame = 1023; @@ -1047,7 +1047,7 @@ void *ru_thread(void *param) // set default return value sprintf(threadname,"ru_thread %u",ru->idx); LOG_I(PHY,"Starting RU %d (%s,%s) on cpu %d\n",ru->idx,NB_functions[ru->function],NB_timing[ru->if_timing],sched_getcpu()); - ru->config = RC.gNB[0]->gNB_config; + ru->config = gNB->gNB_config; nr_init_frame_parms(&ru->config, fp); nr_dump_frame_parms(fp); @@ -1179,12 +1179,17 @@ void *ru_thread(void *param) proc->timestamp_tx += fp->get_samples_per_slot(i % fp->slots_per_frame, fp); proc->tti_tx = (proc->tti_rx + ru->sl_ahead) % fp->slots_per_frame; proc->frame_tx = proc->tti_rx > proc->tti_tx ? (proc->frame_rx + 1) & 1023 : proc->frame_rx; - LOG_D(PHY,"AFTER fh_south_in - SFN/SL:%d%d RU->proc[RX:%d.%d TX:%d.%d] RC.gNB[0]:[RX:%d%d TX(SFN):%d]\n", - frame,slot, - proc->frame_rx,proc->tti_rx, - proc->frame_tx,proc->tti_tx, - RC.gNB[0]->proc.frame_rx,RC.gNB[0]->proc.slot_rx, - RC.gNB[0]->proc.frame_tx); + LOG_D(PHY, + "AFTER fh_south_in - SFN/SL:%d%d RU->proc[RX:%d.%d TX:%d.%d] RC.gNB[0]:[RX:%d%d TX(SFN):%d]\n", + frame, + slot, + proc->frame_rx, + proc->tti_rx, + proc->frame_tx, + proc->tti_tx, + gNB->proc.frame_rx, + gNB->proc.slot_rx, + gNB->proc.frame_tx); if (ru->idx != 0) proc->frame_tx = (proc->frame_tx + proc->frame_offset) & 1023; @@ -1199,7 +1204,7 @@ void *ru_thread(void *param) LOG_D(NR_PHY, "Setting %d.%d (%d) to busy\n", proc->frame_rx, proc->tti_rx, proc->tti_rx % RU_RX_SLOT_DEPTH); //LOG_M("rxdata.m","rxs",ru->common.rxdata[0],1228800,1,1); LOG_D(PHY,"RU proc: frame_rx = %d, tti_rx = %d\n", proc->frame_rx, proc->tti_rx); - gNBscopeCopy(RC.gNB[0], + gNBscopeCopy(gNB, gNBRxdataF, ru->common.rxdataF[0], sizeof(c16_t), @@ -1208,33 +1213,12 @@ 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(&ru->prach_list, proc->frame_rx, proc->tti_rx, SEARCH_EXIST); + prach_item_t *p = find_nr_prach(&gNB->prach_list, proc->frame_rx, proc->tti_rx, SEARCH_EXIST); if (p) { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 1 ); - - T(T_GNB_PHY_PRACH_INPUT_SIGNAL, - T_INT(proc->frame_rx), - T_INT(proc->tti_rx), - T_INT(0), - T_BUFFER(&ru->common.rxdata[0][fp->get_samples_slot_timestamp(proc->tti_rx - 1, fp, 0) /*-ru->N_TA_offset*/], - (fp->get_samples_per_slot(proc->tti_rx - 1, fp) + fp->get_samples_per_slot(proc->tti_rx, fp)) * 4)); - int N_dur = get_nr_prach_duration(p->pdu.prach_format); - LOG_D(NR_PHY_RACH, "%d.%d try to decode %d occasions \n", frame, slot, p->pdu.num_prach_ocas); - for (int prach_oc = 0; prach_oc < p->pdu.num_prach_ocas; prach_oc++) { - int prachStartSymbol = p->pdu.prach_start_symbol + prach_oc * N_dur; - int beam_id = p->beams[prach_oc]; - //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. - rx_nr_prach_ru(ru, - p->pdu.prach_format, // could also use format - p->pdu.num_ra, - beam_id, - prachStartSymbol, - p->slot, - prach_oc, - proc->frame_rx, - proc->tti_rx); - } - free_nr_prach_entry(&ru->prach_list, p); + // this fills ru->prach_rxsigF for processing by gNB + p->rxsigF = (c16_t ***)ru->prach_rxsigF; + rx_nr_prach_ru(p, ru->common.rxdata, ru->nr_frame_parms, ru->N_TA_offset); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 0); } // end if (prach_id >= 0) } // end if (ru->feprx) diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 425e20dd84..0bdb538f20 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -87,9 +87,8 @@ prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach return NULL; } const int fmt = prach_pdu->prach_format; - *prach = (prach_item_t){.frame = SFN, - .slot = Slot, - .num_slots = (fmt < 4) ? get_long_prach_dur(fmt, gNB->frame_parms.numerology_index) : 1}; + NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; + *prach = (prach_item_t){.frame = SFN, .slot = Slot, .num_slots = (fmt < 4) ? get_long_prach_dur(fmt, fp->numerology_index) : 1}; 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, @@ -109,6 +108,18 @@ prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach } } 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; } @@ -122,152 +133,140 @@ void nr_fill_prach_ru(RU_t *ru, prach_item_t *gnb_prach) *prach = *gnb_prach; } -void rx_nr_prach_ru(RU_t *ru, - int prachFormat, - int numRA, - int beam, - int prachStartSymbol, - int prachStartSlot, - int prachOccasion, - int frame, - int slot) +static void rx_nr_prach_ru_internal(prach_item_t *p, + int beam_id, + int prachStartSymbol, + int prachOccasion, + int32_t **rxdata, + NR_DL_FRAME_PARMS *fp, + int N_TA_offset) { - AssertFatal(ru != NULL,"ru is null\n"); - - NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; - int prach_sequence_length = ru->config.prach_config.prach_sequence_length.value; - int msg1_frequencystart = ru->config.prach_config.num_prach_fd_occasions_list[numRA].k1.value; - const uint8_t prach_mu = ru->config.prach_config.prach_sub_c_spacing.value; - int sample_offset_slot; + const int sum = fp->ofdm_symbol_size + fp->nb_prefix_samples; + const int sum0 = fp->ofdm_symbol_size + fp->nb_prefix_samples0; if (prachStartSymbol == 0) { sample_offset_slot = 0; } else if (fp->slots_per_subframe == 1) { if (prachStartSymbol <= 7) - sample_offset_slot = (fp->ofdm_symbol_size + fp->nb_prefix_samples) * (prachStartSymbol - 1) + (fp->ofdm_symbol_size + fp->nb_prefix_samples0); + sample_offset_slot = sum * (prachStartSymbol - 1) + sum0; else - sample_offset_slot = (fp->ofdm_symbol_size + fp->nb_prefix_samples) * (prachStartSymbol - 2) + (fp->ofdm_symbol_size + fp->nb_prefix_samples0) * 2; + sample_offset_slot = sum * (prachStartSymbol - 2) + sum0 * 2; } else { - if (!(slot%(fp->slots_per_subframe/2))) - sample_offset_slot = (fp->ofdm_symbol_size + fp->nb_prefix_samples) * (prachStartSymbol - 1) + (fp->ofdm_symbol_size + fp->nb_prefix_samples0); + if (!(p->slot % (fp->slots_per_subframe / 2))) + sample_offset_slot = sum * (prachStartSymbol - 1) + sum0; else - sample_offset_slot = (fp->ofdm_symbol_size + fp->nb_prefix_samples) * prachStartSymbol; + sample_offset_slot = sum * prachStartSymbol; } LOG_D(PHY, "frame %d, slot %d: doing rx_nr_prach_ru for format %d, numRA %d, prachStartSymbol %d, prachOccasion %d\n", - frame, - slot, - prachFormat, - numRA, + p->frame, + p->slot, + p->pdu.prach_format, + p->pdu.num_ra, prachStartSymbol, prachOccasion); - AssertFatal(ru->if_south == LOCAL_RF || ru->if_south == REMOTE_IF5, - "we shouldn't call this if if_south != LOCAL_RF or REMOTE_IF5\n"); int reps; int Ncp; int dftlen; - int mu = fp->numerology_index; + int mu = p->numerology_index; - if (prach_sequence_length == 0) { + if (p->prach_sequence_length == 0) { LOG_D(PHY, - "PRACH (ru %d) in %d.%d, format %d, msg1_frequencyStart %d\n", - ru->idx, - frame, - prachStartSlot, - prachFormat, - msg1_frequencystart); - switch (prachFormat) { - case 0: - reps = 1; - Ncp = 3168; - dftlen = 24576; - break; + "PRACH in %d.%d, format %d, msg1_frequencyStart %d\n", + p->frame, + p->slot, + p->pdu.prach_format, + p->msg1_frequencystart); + switch (p->pdu.prach_format) { + case 0: + reps = 1; + Ncp = 3168; + dftlen = 24576; + break; - case 1: - reps = 2; - Ncp = 21024; - dftlen = 24576; - break; + case 1: + reps = 2; + Ncp = 21024; + dftlen = 24576; + break; - case 2: - reps = 4; - Ncp = 4688; - dftlen = 24576; - break; + case 2: + reps = 4; + Ncp = 4688; + dftlen = 24576; + break; - case 3: - reps = 4; - Ncp = 3168; - dftlen = 6144; - break; + case 3: + reps = 4; + Ncp = 3168; + dftlen = 6144; + break; - default: - AssertFatal(1==0, "Illegal prach format %d for length 839\n", prachFormat); - break; + default: + AssertFatal(1 == 0, "Illegal prach format %d for length 839\n", p->pdu.prach_format); + break; } - } - else { + } else { LOG_D(PHY, - "PRACH (ru %d) in %d.%d, format %s, msg1_frequencyStart %d,startSymbol %d\n", - ru->idx, - frame, - slot, - prachfmt[prachFormat], - msg1_frequencystart, - prachStartSymbol); - switch (prachFormat) { - case 4: //A1 - reps = 2; - Ncp = 288 >> mu; - break; + "PRACH in %d.%d, format %s, msg1_frequencyStart %d,startSymbol %d\n", + p->frame, + p->slot, + prachfmt[p->pdu.prach_format], + p->msg1_frequencystart, + prachStartSymbol); + switch (p->pdu.prach_format) { + case 4: // A1 + reps = 2; + Ncp = 288 >> mu; + break; - case 5: //A2 - reps = 4; - Ncp = 576 >> mu; - break; + case 5: // A2 + reps = 4; + Ncp = 576 >> mu; + break; - case 6: //A3 - reps = 6; - Ncp = 864 >> mu; - break; + case 6: // A3 + reps = 6; + Ncp = 864 >> mu; + break; - case 7: //B1 - reps = 2; - Ncp = 216 >> mu; - break; + case 7: // B1 + reps = 2; + Ncp = 216 >> mu; + break; - /* - // B2 and B3 do not exist in FAPI - case 4: //B2 - reps = 4; - Ncp = 360 >> mu; - break; + /* + // B2 and B3 do not exist in FAPI + case 4: //B2 + reps = 4; + Ncp = 360 >> mu; + break; - case 5: //B3 - reps = 6; - Ncp = 504 >> mu; - break; - */ + case 5: //B3 + reps = 6; + Ncp = 504 >> mu; + break; + */ - case 8: //B4 - reps = 12; - Ncp = 936 >> mu; - break; + case 8: // B4 + reps = 12; + Ncp = 936 >> mu; + break; - case 9: //C0 - reps = 1; - Ncp = 1240 >> mu; - break; + case 9: // C0 + reps = 1; + Ncp = 1240 >> mu; + break; - case 10: //C2 - reps = 4; - Ncp = 2048 >> mu; - break; + case 10: // C2 + reps = 4; + Ncp = 2048 >> mu; + break; - default: - AssertFatal(1==0,"unknown prach format %x\n",prachFormat); - break; + default: + AssertFatal(1 == 0, "unknown prach format %x\n", p->pdu.prach_format); + break; } dftlen = 2048 >> mu; } @@ -275,12 +274,11 @@ void rx_nr_prach_ru(RU_t *ru, //actually what we should be checking here is how often the current prach crosses a 0.5ms boundary. //I am not quite sure for which paramter set this would be the case, //so I will ignore it for now and just check if the prach starts on a 0.5ms boundary - if(fp->numerology_index == 0) { + if (p->numerology_index == 0) { if (prachStartSymbol == 0 || prachStartSymbol == 7) Ncp += 16; - } - else { - if (slot%(fp->slots_per_subframe/2)==0 && prachStartSymbol == 0) + } else { + if (p->slot % (fp->slots_per_subframe / 2) == 0 && prachStartSymbol == 0) Ncp += 16; } @@ -351,27 +349,27 @@ void rx_nr_prach_ru(RU_t *ru, // Do forward transform if (LOG_DEBUGFLAG(DEBUG_PRACH)) { - LOG_D(PHY, "rx_prach: Doing PRACH FFT for nb_rx:%d Ncp:%d dftlen:%d\n", ru->nb_rx, Ncp, dftlen); + LOG_D(PHY, "rx_prach: Doing PRACH FFT for nb_rx:%d Ncp:%d dftlen:%d\n", p->nb_rx, Ncp, dftlen); } - const unsigned int K = get_prach_K(prach_sequence_length, prachFormat, fp->numerology_index, prach_mu); - const uint8_t kbar = get_PRACH_k_bar(prach_mu, fp->numerology_index); + const unsigned int K = get_prach_K(p->prach_sequence_length, p->pdu.prach_format, p->numerology_index, p->mu); + const uint8_t kbar = get_PRACH_k_bar(p->mu, p->numerology_index); - int n_ra_prb = msg1_frequencystart; + int n_ra_prb = p->msg1_frequencystart; int k = (12*n_ra_prb) - 6*fp->N_RB_UL; - int N_ZC = (prach_sequence_length == 0) ? 839 : 139; + int N_ZC = (p->prach_sequence_length == 0) ? 839 : 139; if (k<0) k+=(fp->ofdm_symbol_size); k*=K; k+=kbar; - for (int aa=0; aanb_rx; aa++) { - int slot2 = prach_sequence_length ? slot : prachStartSlot; - int idx = aa + beam * ru->nb_rx; - c16_t *prach = - (c16_t *)&ru->common.rxdata[idx][fp->get_samples_slot_timestamp(slot2, fp, 0) + sample_offset_slot - ru->N_TA_offset]; + for (int aa = 0; aa < p->nb_rx; aa++) { + // Fixme: slot or slot makes no sense ??? + int slot2 = p->prach_sequence_length ? p->slot : p->slot; + int idx = aa + beam_id * p->nb_rx; + c16_t *prach = (c16_t *)&rxdata[idx][fp->get_samples_slot_timestamp(slot2, fp, 0) + sample_offset_slot - N_TA_offset]; // do DFT c16_t *prach2 = prach + Ncp; @@ -390,60 +388,56 @@ void rx_nr_prach_ru(RU_t *ru, rxsigF_tmp[j] = c16add(rxsigF_tmp[j], tmp[k2]); } } - memcpy(ru->prach_rxsigF[prachOccasion][aa], rxsigF_tmp, sizeof(rxsigF_tmp)); + memcpy(p->rxsigF[prachOccasion][aa], rxsigF_tmp, sizeof(rxsigF_tmp)); } } -void rx_nr_prach(PHY_VARS_gNB *gNB, - nfapi_nr_prach_pdu_t *prach_pdu, - int frame, - int slot, - uint16_t *max_preamble, - uint16_t *max_preamble_energy, - uint16_t *max_preamble_delay, - c16_t **rxsigF) +void rx_nr_prach_ru(prach_item_t *p, int32_t **rxdata, NR_DL_FRAME_PARMS *fp, int N_TA_offset) { - AssertFatal(gNB != NULL, "Can only be called from gNB\n"); - nfapi_nr_prach_config_t *cfg = &gNB->gNB_config.prach_config; + int N_dur = get_nr_prach_duration(p->pdu.prach_format); + LOG_D(NR_PHY_RACH, "%d.%d try to decode %d occasions \n", p->frame, p->slot, p->pdu.num_prach_ocas); + for (int prach_oc = 0; prach_oc < p->pdu.num_prach_ocas; prach_oc++) { + int prachStartSymbol = p->pdu.prach_start_symbol + prach_oc * N_dur; + int beam_id = p->beams[prach_oc]; + // comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is + // given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots + // where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. + rx_nr_prach_ru_internal(p, beam_id, prachStartSymbol, prach_oc, rxdata, fp, N_TA_offset); + } +} + +rx_prach_out_t rx_nr_prach(const prach_item_t *in, int occasion) +{ + rx_prach_out_t out = {}; + c16_t **input = in->rxsigF[occasion]; uint16_t preamble_index0 = 0; uint16_t numshift = 0; int first_nonzero_root_idx = 0; bool new_dft = false; int log2_ifft_size = 10; - NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - - const int nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value; - const int rootSequenceIndex = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].prach_root_sequence_index.value; - const int numrootSequenceIndex = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].num_root_sequences.value; - const int NCS = prach_pdu->num_cs; // cfg->num_prach_fd_occasions_list[0].prach_zero_corr_conf.value; - const int prach_sequence_length = cfg->prach_sequence_length.value; - const int msg1_frequencystart = cfg->num_prach_fd_occasions_list[prach_pdu->num_ra].k1.value; - const int restricted_set = cfg->restricted_set_config.value; - const int prach_fmt = prach_pdu->prach_format; - const int N_ZC = prach_sequence_length == 0 ? 839 : 139; + const int nb_rx = in->nb_rx; + const int NCS = in->pdu.num_cs; + const int prach_fmt = in->pdu.prach_format; + const int N_ZC = in->prach_sequence_length == 0 ? 839 : 139; LOG_D(NR_PHY_RACH, "L1 PRACH RX: rooSequenceIndex %d, numRootSeqeuences %d, NCS %d, N_ZC %d, format %d \n", - rootSequenceIndex, - numrootSequenceIndex, + in->rootSequenceIndex, + in->numrootSequenceIndex, NCS, N_ZC, prach_fmt); if (LOG_DEBUGFLAG(DEBUG_PRACH)) { - if ((frame & 1023) < 20) - LOG_D(PHY, - "PRACH (gNB) : running rx_prach for slot %d, msg1_frequencystart %d, rootSequenceIndex %d\n", - slot, - msg1_frequencystart, - rootSequenceIndex); + if ((in->frame & 1023) < 20) + LOG_D(PHY, "PRACH (gNB) : running rx_prach for slot %d, rootSequenceIndex %d\n", in->slot, in->rootSequenceIndex); } - start_meas(&gNB->rx_prach); + start_meas(in->rx_prach); const uint16_t *prach_root_sequence_map = - cfg->prach_sequence_length.value == 0 ? prach_root_sequence_map_0_3 : prach_root_sequence_map_abc; + in->prach_sequence_length == 0 ? prach_root_sequence_map_0_3 : prach_root_sequence_map_abc; // PDP is oversampled, e.g. 1024 sample instead of 839 // Adapt the NCS (zero-correlation zones) with oversampling factor e.g. 1024/839 @@ -454,19 +448,16 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, int preamble_offset = 0, preamble_offset_old = 99; - *max_preamble_energy = 0; - *max_preamble_delay = 0; - *max_preamble = 0; int16_t preamble_shift = 0; const int dft_sz = N_ZC == 839 ? 1024 : 256; int32_t prach_ifft[dft_sz] __attribute__((aligned(32))); for (int preamble_index = 0; preamble_index < 64; preamble_index++) { if (LOG_DEBUGFLAG(DEBUG_PRACH)) { - int en = dB_fixed(signal_energy((int32_t *)rxsigF[0], N_ZC == 839 ? 840 : 140)); + int en = dB_fixed(signal_energy((int32_t *)input[0], N_ZC == 839 ? 840 : 140)); if (en > 60) - LOG_D(PHY, "frame %d, slot %d : Trying preamble %d \n", frame, slot, preamble_index); + LOG_D(PHY, "frame %d, slot %d : Trying preamble %d \n", in->frame, in->slot, preamble_index); } - if (restricted_set == 0) { + if (in->restricted_set == 0) { // This is the relative offset in the root sequence table (5.7.2-4 from 36.211) for the given preamble index preamble_offset = ((NCS==0)? preamble_index : (preamble_index/(N_ZC/NCS))); @@ -495,7 +486,7 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, preamble_index0 -= numshift; while (not_found) { // current root depending on rootSequenceIndex - int index = (rootSequenceIndex + preamble_offset) % N_ZC; + int index = (in->rootSequenceIndex + preamble_offset) % N_ZC; int u = nr_du[prach_root_sequence_map[index]]; uint16_t n_group_ra = 0; @@ -538,12 +529,12 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, // Compute DFT of RX signal (conjugate input, results in conjugate output) for each new rootSequenceIndex if (LOG_DEBUGFLAG(DEBUG_PRACH)) { - int en = dB_fixed(signal_energy((int32_t *)rxsigF[0], 840)); + int en = dB_fixed(signal_energy((int32_t *)input[0], 840)); if (en>60) LOG_D(PHY, "frame %d, slot %d : preamble index %d, NCS %d, N_ZC/NCS %d: offset %d, preamble shift %d , en %d)\n", - frame, - slot, + in->frame, + in->slot, preamble_index, NCS, N_ZC / NCS, @@ -562,19 +553,19 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, if (new_dft) { new_dft = false; - c16_t *Xu = (c16_t *)gNB->X_u[preamble_offset - first_nonzero_root_idx]; + c16_t *Xu = in->Xu[preamble_offset - first_nonzero_root_idx]; LOG_D(PHY,"PRACH RX new dft preamble_offset-first_nonzero_root_idx %d\n",preamble_offset-first_nonzero_root_idx); memset(prach_ifft, 0, sizeof(prach_ifft)); if (LOG_DUMPFLAG(DEBUG_PRACH)) { - LOG_M("prach_rxF0.m","prach_rxF0",rxsigF[0],N_ZC,1,1); - LOG_M("prach_rxF1.m","prach_rxF1",rxsigF[1],6144,1,1); + LOG_M("prach_rxF0.m", "prach_rxF0", input[0], N_ZC, 1, 1); + LOG_M("prach_rxF1.m", "prach_rxF1", input[1], 6144, 1, 1); } c16_t prachF[dft_sz] __attribute__((aligned(32))); for (int aa = 0; aa < nb_rx; aa++) { // Do componentwise product with Xu* on each antenna for (int offset = 0; offset < N_ZC; offset++) { - prachF[offset] = c16MulConjShift(Xu[offset], rxsigF[aa][offset], 15); + prachF[offset] = c16MulConjShift(Xu[offset], input[aa][offset], 15); } memset(prachF + N_ZC, 0, sizeof(*prachF) * (dft_sz - N_ZC)); // Now do IFFT of size 1024 (N_ZC=839) or 256 (N_ZC=139) @@ -613,11 +604,11 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, for (int i = 0; i < NCS2; i++) { int lev = prach_ifft[preamble_shift2 + i]; int levdB = dB_fixed_times10(lev); - if (levdB > *max_preamble_energy || (levdB == *max_preamble_energy && *max_preamble_delay > i)) { - LOG_D(NR_PHY_RACH, "preamble_index %d, delay %d en %d dB > %d dB\n", preamble_index, i, levdB, *max_preamble_energy); - *max_preamble_energy = levdB; - *max_preamble_delay = i; // Note: This has to be normalized to the 30.72 Ms/s sampling rate - *max_preamble = preamble_index; + if (levdB > out.max_preamble_energy || (levdB == out.max_preamble_energy && out.max_preamble_delay > i)) { + LOG_D(NR_PHY_RACH, "preamble_index %d, delay %d en %d dB > %d dB\n", preamble_index, i, levdB, out.max_preamble_energy); + out.max_preamble_energy = levdB; + out.max_preamble_delay = i; // Note: This has to be normalized to the 30.72 Ms/s sampling rate + out.max_preamble = preamble_index; } } } // preamble_index @@ -637,16 +628,15 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, // Format >3: 2048/2^mu samples @ 30.72 Ms/s, 2048/2^mu * 4 samples @ 122.88 Ms/s // By solving: // max_preamble_delay * ( (2048/2^mu*(fs/30.72M)) / 256 ) / fs = TA * 16 * 64 / 2^mu * Tc - uint16_t *TA = max_preamble_delay; - int mu = fp->numerology_index; - if (cfg->prach_sequence_length.value == 0) { + int mu = in->numerology_index; + if (in->prach_sequence_length == 0) { if (prach_fmt == 0 || prach_fmt == 1 || prach_fmt == 2) - *TA = *TA * 3 * (1 << mu) / 2; + out.max_preamble_delay *= 3 * (1 << mu) / 2; else if (prach_fmt == 3) - *TA = *TA * 3 * (1 << mu) / 8; - } - else *TA = *TA/2; - - stop_meas(&gNB->rx_prach); + out.max_preamble_delay *= 3 * (1 << mu) / 8; + } else + out.max_preamble_delay /= 2; + stop_meas(in->rx_prach); + return out; } diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 2cbe1fde77..fd0a36536d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -202,24 +202,14 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); -void rx_nr_prach(PHY_VARS_gNB *gNB, - nfapi_nr_prach_pdu_t *prach_pdu, - int frame, - int subframe, - uint16_t *max_preamble, - uint16_t *max_preamble_energy, - uint16_t *max_preamble_delay, - c16_t **rxsigF); +typedef struct rx_prach_out { + uint16_t max_preamble; + uint16_t max_preamble_energy; + uint16_t max_preamble_delay; +} rx_prach_out_t; +rx_prach_out_t rx_nr_prach(const prach_item_t *, int occasion); -void rx_nr_prach_ru(RU_t *ru, - int prach_fmt, - int numRA, - int beam, - int prachStartSymbol, - int prachStartSlot, - int prachOccasion, - int frame, - int subframe); +void rx_nr_prach_ru(prach_item_t *, int32_t **, NR_DL_FRAME_PARMS *frame_parms, int N_TA_offset); void nr_fill_prach_ru(RU_t *ru,prach_item_t * ); prach_item_t *find_nr_prach(prach_list_t *, int frame, int slot, find_type_t type); diff --git a/openair1/PHY/defs_common.h b/openair1/PHY/defs_common.h index b469555354..efaccf2bf7 100644 --- a/openair1/PHY/defs_common.h +++ b/openair1/PHY/defs_common.h @@ -1007,6 +1007,17 @@ typedef struct { int num_slots; // prach duration in slots int beams[NFAPI_MAX_NUM_BG_IF]; nfapi_nr_prach_pdu_t pdu; + int rootSequenceIndex; + int numrootSequenceIndex; + int msg1_frequencystart; + int mu; + int prach_sequence_length; + int restricted_set; + int numerology_index; + int nb_rx; + c16_t ***rxsigF; + c16_t (*Xu)[839]; + time_stats_t *rx_prach; } prach_item_t; typedef struct { diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index ad6f90e6e8..4ec87a1e5f 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -156,8 +156,12 @@ void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; LOG_D(NR_PHY_RACH, "Add prach decoding request for %d.%d\n", UL_tti_req->SFN, UL_tti_req->Slot); prach_item_t *prach = nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); + if (!prach) + LOG_W(NR_PHY_RACH, "Error in scheduling rach\n"); + /* the present design is not supporting multiple gNB in one RU if (prach && (gNB->RU_list[0]->if_south == LOCAL_RF || gNB->RU_list[0]->if_south == REMOTE_IF5)) nr_fill_prach_ru(gNB->RU_list[0], prach); + */ break; case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: LOG_D(NR_PHY, diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index caaa579ad0..e6cd15b4a9 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -49,16 +49,11 @@ int get_nr_prach_duration(uint8_t prach_format) void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rach_indication_t *rach_ind) { - uint16_t max_preamble[4]={0},max_preamble_energy[4]={0},max_preamble_delay[4]={0}; - - RU_t *ru; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,1); rach_ind->sfn = frame; rach_ind->slot = slot; rach_ind->number_of_pdus = 0; - ru=gNB->RU_list[0]; - prach_item_t *prach_id = find_nr_prach(&gNB->prach_list, frame, slot, SEARCH_EXIST); if (!prach_id) { return; @@ -74,11 +69,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac // comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is // given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots // where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. - - c16_t *rxsigF[ru->nb_rx]; - for (int i = 0; i < ru->nb_rx; ++i) - rxsigF[i] = (c16_t *)ru->prach_rxsigF[prach_oc][i]; - rx_nr_prach(gNB, prach_pdu, frame, slot, max_preamble, max_preamble_energy, max_preamble_delay, rxsigF); + rx_prach_out_t res = rx_nr_prach(prach_id, prach_oc); LOG_D(NR_PHY, "[RAPROC] Frame %d, slot %d, occasion %d (prachStartSymbol %d) : Most likely preamble %d, energy %d.%d dB delay %d " "(prach_energy counter %d)\n", @@ -86,26 +77,26 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac slot, prach_oc, prachStartSymbol, - max_preamble[0], - max_preamble_energy[0] / 10, - max_preamble_energy[0] % 10, - max_preamble_delay[0], + res.max_preamble, + res.max_preamble_energy / 10, + res.max_preamble_energy % 10, + res.max_preamble_delay, gNB->prach_energy_counter); if ((gNB->prach_energy_counter == NUM_PRACH_RX_FOR_NOISE_ESTIMATE) - && (max_preamble_energy[0] > gNB->measurements.prach_I0 + gNB->prach_thres) + && (res.max_preamble_energy > gNB->measurements.prach_I0 + gNB->prach_thres) && (rach_ind->number_of_pdus < MAX_NUM_NR_RX_RACH_PDUS)) { LOG_A(NR_PHY, "[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, - max_preamble[0], - max_preamble_energy[0] / 10, - max_preamble_energy[0] % 10, + res.max_preamble, + res.max_preamble_energy / 10, + res.max_preamble_energy % 10, gNB->measurements.prach_I0, gNB->prach_thres, - max_preamble_delay[0], + res.max_preamble_delay, prachStartSymbol, prach_pdu->num_ra); @@ -113,9 +104,9 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac T_INT(gNB->Mod_id), T_INT(frame), T_INT(slot), - T_INT(max_preamble[0]), - T_INT(max_preamble_energy[0]), - T_INT(max_preamble_delay[0])); + T_INT(res.max_preamble), + T_INT(res.max_preamble_energy), + T_INT(res.max_preamble_delay)); nfapi_nr_prach_indication_pdu_t *ind = rach_ind->pdu_list + rach_ind->number_of_pdus; *ind = (nfapi_nr_prach_indication_pdu_t){ @@ -123,14 +114,14 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac .symbol_index = prachStartSymbol, .slot_index = slot, .freq_index = prach_pdu->num_ra, - .avg_rssi = (max_preamble_energy[0] < 631) ? (128 + (max_preamble_energy[0] / 5)) : 254, + .avg_rssi = (res.max_preamble_energy < 631) ? (128 + (res.max_preamble_energy / 5)) : 254, .avg_snr = 0xff, // invalid for now .num_preamble = 1, .preamble_list = { - {.preamble_index = max_preamble[0], .timing_advance = max_preamble_delay[0], .preamble_pwr = 0xffffffff}}}; + {.preamble_index = res.max_preamble, .timing_advance = res.max_preamble_delay, .preamble_pwr = 0xffffffff}}}; rach_ind->number_of_pdus++; } - gNB->measurements.prach_I0 = ((gNB->measurements.prach_I0 * 900) >> 10) + ((max_preamble_energy[0] * 124) >> 10); + gNB->measurements.prach_I0 = ((gNB->measurements.prach_I0 * 900) >> 10) + ((res.max_preamble_energy * 124) >> 10); if (frame == 0) LOG_I(PHY, "prach_I0 = %d.%d dB\n", gNB->measurements.prach_I0 / 10, gNB->measurements.prach_I0 % 10); if (gNB->prach_energy_counter < NUM_PRACH_RX_FOR_NOISE_ESTIMATE) diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index e5f64fff5d..7d87ac54a7 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -135,10 +135,11 @@ int main(int argc, char **argv){ double **s_re, **s_im, **r_re, **r_im, iqim = 0.0, delay_avg = 0, ue_speed = 0, fs=-1, bw; int i, l, aa, aarx, trial, n_frames = 1, rx_prach_start; //, ntrials=1; c16_t **txdata; - int N_RB_UL = 106, delay = 0, NCS_config = 13, rootSequenceIndex = 1, threequarter_fs = 0, mu = 1, fd_occasion = 0, loglvl = OAILOG_INFO, numRA = 0, prachStartSymbol = 0; + int N_RB_UL = 106, delay = 0, NCS_config = 13, rootSequenceIndex = 1, threequarter_fs = 0, mu = 1, fd_occasion = 0, + loglvl = OAILOG_INFO; uint8_t snr1set = 0, ue_speed1set = 0, transmission_mode = 1, n_tx = 1, n_rx = 1, awgn_flag = 0, msg1_frequencystart = 0, num_prach_fd_occasions = 1, prach_format=0; uint8_t config_index = 98, prach_sequence_length = 1, restrictedSetConfig = 0; - uint16_t Nid_cell = 0, preamble_tx = 0, preamble_delay, format0, format1; + uint16_t Nid_cell = 0, preamble_tx = 0, format0, format1; uint32_t tx_lev = 10000, prach_errors = 0; //,tx_lev_dB; uint64_t SSB_positions = 0x01; uint16_t RA_sfn_index; @@ -714,9 +715,17 @@ int main(int argc, char **argv){ } rx_prach_start = subframe*frame_parms->samples_per_subframe; - if (n_frames==1) printf("slot %d, rx_prach_start %d\n",slot,rx_prach_start); - uint16_t preamble_rx, preamble_energy; + if (n_frames == 1) + printf("slot %d, rx_prach_start %d\n", slot, rx_prach_start); + c16_t **rxsigF[NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX]; + for (int j = 0; j < NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX; j++) { + rxsigF[j] = malloc(ru->nb_rx * sizeof(*rxsigF)); + for (int i = 0; i < ru->nb_rx; i++) { + // largest size for PRACH FFT is 4x98304 (16*24576) + rxsigF[j][i] = malloc16_clear(4 * 98304 * sizeof(**rxsigF)); + } + } for (SNR = snr0; SNR < snr1 && !stop; SNR += .1) { for (ue_speed = ue_speed0; ue_speed < ue_speed1 && !stop; ue_speed += 10) { @@ -727,8 +736,7 @@ int main(int argc, char **argv){ prach_errors=0; for (trial = 0; trial < n_frames && !stop; trial++) { - - if (input_fd==NULL) { + if (input_fd == NULL) { sigma2_dB = 10*log10((double)tx_lev) - SNR - 10*log10(N_RB_UL*12/N_ZC); if (n_frames==1) @@ -755,52 +763,72 @@ int main(int argc, char **argv){ tmp->i = (short)(.167 * (r_im[aa][i] + (iqim * r_re[aa][i]) + sqrt(sigma2 / 2) * gaussdouble(0.0, 1.0))); } } - } else { - n_bytes = fread(&ru->common.rxdata[0][rx_prach_start],sizeof(int32_t),frame_parms->samples_per_subframe,input_fd); - printf("fread %d bytes from file %s\n",n_bytes,input_file); - if (n_bytes!=frame_parms->samples_per_subframe) { - printf("expected %d bytes\n",frame_parms->samples_per_subframe); - exit(-1); - } - } + } else { + n_bytes = fread(&ru->common.rxdata[0][rx_prach_start], sizeof(int32_t), frame_parms->samples_per_subframe, input_fd); + printf("fread %d bytes from file %s\n", n_bytes, input_file); + if (n_bytes != frame_parms->samples_per_subframe) { + printf("expected %d bytes\n", frame_parms->samples_per_subframe); + exit(-1); + } + } - for (l = 0; l < frame_parms->symbols_per_slot; l++) { - for (aa = 0; aa < frame_parms->nb_antennas_rx; aa++) { - nr_slot_fep_ul(frame_parms, ru->common.rxdata[aa], ru->common.rxdataF[aa], l, slot, ru->N_TA_offset); - } - } - rx_nr_prach_ru(ru, prach_format, numRA, 0, prachStartSymbol, slot, prachOccasion, frame, slot); - c16_t *rxsigF[ru->nb_rx]; - for (int i = 0; i < ru->nb_rx; ++i) - rxsigF[i] = (c16_t *)ru->prach_rxsigF[prachOccasion][i]; - if (n_frames == 1) - LOG_I(PHY, - "ncs %d,num_seq %d\n", - prach_pdu->num_cs, - prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value); - rx_nr_prach(gNB, prach_pdu, frame, subframe, &preamble_rx, &preamble_energy, &preamble_delay, rxsigF); + for (l = 0; l < frame_parms->symbols_per_slot; l++) { + for (aa = 0; aa < frame_parms->nb_antennas_rx; aa++) { + nr_slot_fep_ul(frame_parms, ru->common.rxdata[aa], ru->common.rxdataF[aa], l, slot, ru->N_TA_offset); + } + } - // printf(" preamble_energy %d preamble_rx %d preamble_tx %d \n", preamble_energy, preamble_rx, preamble_tx); + 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; + prach_item_t in = {.frame = frame, + .slot = slot, + .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 = gNB->frame_parms.numerology_index, + .nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value, + .rxsigF = rxsigF, + .Xu = gNB->X_u, + .rx_prach = &gNB->rx_prach}; + rx_nr_prach_ru(&in, ru->common.rxdata, ru->nr_frame_parms, ru->N_TA_offset); + if (n_frames == 1) + LOG_I(PHY, + "ncs %d,num_seq %d\n", + 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); - if (preamble_rx != preamble_tx) - prach_errors++; - else - delay_avg += (double)preamble_delay; + // printf(" preamble_energy %d preamble_rx %d preamble_tx %d \n", out.max_preamble_energy, out.max_preamble, + // preamble_tx); - N_ZC = (prach_sequence_length) ? 139 : 839; + if (out.max_preamble != preamble_tx) + prach_errors++; + else + delay_avg += (double)out.max_preamble_delay; - if (n_frames == 1) { - printf("preamble %d (tx %d) : energy %d, delay %d\n", preamble_rx, preamble_tx, preamble_energy, preamble_delay); + N_ZC = (prach_sequence_length) ? 139 : 839; + + if (n_frames == 1) { + printf("preamble %d (tx %d) : energy %d, delay %d\n", + out.max_preamble, + preamble_tx, + out.max_preamble_energy, + out.max_preamble_delay); #ifdef NR_PRACH_DEBUG LOG_M("prach0.m","prach0", &txdata[0][prach_start], frame_parms->samples_per_subframe, 1, 1); LOG_M("rxsig0.m","rxs0", &ru->common.rxdata[0][subframe*frame_parms->samples_per_subframe], frame_parms->samples_per_subframe, 1, 1); LOG_M("ru_rxsig0.m","rxs0", &ru->common.rxdata[0][subframe*frame_parms->samples_per_subframe], frame_parms->samples_per_subframe, 1, 1); LOG_M("ru_rxsigF0.m","rxsF0", ru->common.rxdataF[0], frame_parms->ofdm_symbol_size*frame_parms->symbols_per_slot, 1, 1); - LOG_M("ru_prach_rxsigF0.m","rxsF0", ru->prach_rxsigF[0][0], N_ZC, 1, 1); + LOG_M("ru_prach_rxsigF0.m", "rxsF0", rxsigF[0][0], N_ZC, 1, 1); LOG_M("prach_preamble.m","prachp", &gNB->X_u[0], N_ZC, 1, 1); LOG_M("ue_prach_preamble.m","prachp", &UE->X_u[0], N_ZC, 1, 1); #endif - } + } } printf("SNR %f dB, UE Speed %f km/h: errors %u/%d (delay %f)\n", SNR, ue_speed, prach_errors, n_frames, delay_avg/(double)(n_frames-prach_errors)); @@ -817,7 +845,13 @@ int main(int argc, char **argv){ if (input_fd) break; } //SNR loop - + for (int j = 0; j < NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX; j++) { + for (int i = 0; i < ru->nb_rx; i++) { + // largest size for PRACH FFT is 4x98304 (16*24576) + free(rxsigF[j][i]); + } + free(rxsigF[j]); + } free_channel_desc_scm(UE2gNB); nr_phy_free_RU(ru); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 88c9c875ea..198975f1ae 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1430,7 +1430,12 @@ static void nr_generate_Msg2(module_id_t module_idP, scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.ra_ResponseWindow; const int n_slots_frame = nr_mac->frame_structure.numb_slots_frame; if (!msg2_in_response_window(ra->preamble_frame, ra->preamble_slot, n_slots_frame, rrc_ra_ResponseWindow, frameP, slotP)) { - LOG_E(NR_MAC, "UE RA-RNTI %04x TC-RNTI %04x: exceeded RA window, cannot schedule Msg2\n", ra->RA_rnti, UE->rnti); + LOG_E(NR_MAC, + "sfn: %d.%d UE RA-RNTI %04x TC-RNTI %04x: exceeded RA window, cannot schedule Msg2\n", + frameP, + slotP, + ra->RA_rnti, + UE->rnti); nr_release_ra_UE(nr_mac, UE->rnti); return; } From fff95cc07580d588cb7411d1f5f4fe51fff1d1a1 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 29 Oct 2025 15:20:36 +0100 Subject: [PATCH 109/275] Refactor rx_nr_prach --- openair1/PHY/NR_TRANSPORT/nr_prach.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 0bdb538f20..101bd76463 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -409,7 +409,6 @@ void rx_nr_prach_ru(prach_item_t *p, int32_t **rxdata, NR_DL_FRAME_PARMS *fp, in rx_prach_out_t rx_nr_prach(const prach_item_t *in, int occasion) { rx_prach_out_t out = {}; - c16_t **input = in->rxsigF[occasion]; uint16_t preamble_index0 = 0; uint16_t numshift = 0; int first_nonzero_root_idx = 0; @@ -453,7 +452,7 @@ rx_prach_out_t rx_nr_prach(const prach_item_t *in, int occasion) int32_t prach_ifft[dft_sz] __attribute__((aligned(32))); for (int preamble_index = 0; preamble_index < 64; preamble_index++) { if (LOG_DEBUGFLAG(DEBUG_PRACH)) { - int en = dB_fixed(signal_energy((int32_t *)input[0], N_ZC == 839 ? 840 : 140)); + int en = dB_fixed(signal_energy((int32_t *)in->rxsigF[occasion][0], N_ZC == 839 ? 840 : 140)); if (en > 60) LOG_D(PHY, "frame %d, slot %d : Trying preamble %d \n", in->frame, in->slot, preamble_index); } @@ -527,9 +526,9 @@ rx_prach_out_t rx_nr_prach(const prach_item_t *in, int occasion) first_nonzero_root_idx = preamble_offset; } - // Compute DFT of RX signal (conjugate input, results in conjugate output) for each new rootSequenceIndex + // Compute DFT of RX signal (conjugate in->rxsigF[occasion], results in conjugate output) for each new rootSequenceIndex if (LOG_DEBUGFLAG(DEBUG_PRACH)) { - int en = dB_fixed(signal_energy((int32_t *)input[0], 840)); + int en = dB_fixed(signal_energy((int32_t *)in->rxsigF[occasion][0], 840)); if (en>60) LOG_D(PHY, "frame %d, slot %d : preamble index %d, NCS %d, N_ZC/NCS %d: offset %d, preamble shift %d , en %d)\n", @@ -558,14 +557,14 @@ rx_prach_out_t rx_nr_prach(const prach_item_t *in, int occasion) memset(prach_ifft, 0, sizeof(prach_ifft)); if (LOG_DUMPFLAG(DEBUG_PRACH)) { - LOG_M("prach_rxF0.m", "prach_rxF0", input[0], N_ZC, 1, 1); - LOG_M("prach_rxF1.m", "prach_rxF1", input[1], 6144, 1, 1); + LOG_M("prach_rxF0.m", "prach_rxF0", in->rxsigF[occasion][0], N_ZC, 1, 1); + LOG_M("prach_rxF1.m", "prach_rxF1", in->rxsigF[occasion][1], 6144, 1, 1); } c16_t prachF[dft_sz] __attribute__((aligned(32))); for (int aa = 0; aa < nb_rx; aa++) { // Do componentwise product with Xu* on each antenna for (int offset = 0; offset < N_ZC; offset++) { - prachF[offset] = c16MulConjShift(Xu[offset], input[aa][offset], 15); + prachF[offset] = c16MulConjShift(Xu[offset], in->rxsigF[occasion][aa][offset], 15); } memset(prachF + N_ZC, 0, sizeof(*prachF) * (dft_sz - N_ZC)); // Now do IFFT of size 1024 (N_ZC=839) or 256 (N_ZC=139) From 32889870b3657a2a45e1a828d20231b68b0c9a97 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Thu, 30 Oct 2025 13:37:15 +0100 Subject: [PATCH 110/275] CI: Adjust attenutation for HO setup --- ci-scripts/xml_files/container_sa_f1_ho_b210_quectel.xml | 4 ++-- ci-scripts/xml_files/container_sa_n2_ho_b210_quectel.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci-scripts/xml_files/container_sa_f1_ho_b210_quectel.xml b/ci-scripts/xml_files/container_sa_f1_ho_b210_quectel.xml index 77b2312409..07a5c5f401 100644 --- a/ci-scripts/xml_files/container_sa_f1_ho_b210_quectel.xml +++ b/ci-scripts/xml_files/container_sa_f1_ho_b210_quectel.xml @@ -93,7 +93,7 @@ Custom_Command Set attenuator to 0 for DU0 and 17 for DU1 rocket - python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 17 -d 5 + python3 %%workspace%%/ci-scripts/attenuatorctl.py -s 1,2 0 -s 3,4 25 @@ -168,7 +168,7 @@ Custom_Command Attenuate DU1 by 17dB and remove attenuation on DU0 to trigger Handover rocket - python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 17 -d 5 + python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 25 -d 5 diff --git a/ci-scripts/xml_files/container_sa_n2_ho_b210_quectel.xml b/ci-scripts/xml_files/container_sa_n2_ho_b210_quectel.xml index b67522b220..46707f70b4 100644 --- a/ci-scripts/xml_files/container_sa_n2_ho_b210_quectel.xml +++ b/ci-scripts/xml_files/container_sa_n2_ho_b210_quectel.xml @@ -87,7 +87,7 @@ Custom_Command Set initial attenuation on target gNB rocket - python3 %%workspace%%/ci-scripts/attenuatorctl.py -s 1,2 0 -s 3,4 15 + python3 %%workspace%%/ci-scripts/attenuatorctl.py -s 1,2 0 -s 3,4 25 @@ -154,7 +154,7 @@ Custom_Command Gradually attenuate gNB-PCI0 (ch1-2=0dB) and gNB-PCI1 (ch3-4=15dB) over 5s to trigger N2 Handover back to gNB-PCI0 rocket - python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 15 -d 5 + python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 25 -d 5 From 69f391f2b84aafaf66f4eb5ddebf5947a20b36df Mon Sep 17 00:00:00 2001 From: Raghavendra Dinavahi Date: Thu, 15 May 2025 17:08:59 +0200 Subject: [PATCH 111/275] Fix for the ULDLduplex issue for NTN bands Duplex is assumed to be static but for NTN bands, it is not static. TxRX Duplex is defined in section 5.4.4 of 38.101-5 (for NTN) specs v1809 n256 190 MHz, 165 to 215 MHz n255 -101.5 MHz, -72.5 to -130.5 MHz n254 862 - 885 MHz --- common/utils/nr/nr_common.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index 8e6a16c97d..c3d0c881b3 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -459,10 +459,36 @@ bool compare_relative_ul_channel_bw(int nr_band, int scs, int channel_bandwidth, return rel_bw > limit; } +static bool check_delta_duplex(int index, uint64_t dlfreq_khz, uint64_t ulfreq_khz) +{ + int uldl_min_offset = nr_bandtable[index].dl_min - nr_bandtable[index].ul_min; + int txrx_offset = dlfreq_khz - ulfreq_khz; + LOG_I(NR_PHY, "dlfreq:%ld ulfreq:%ld deltaduplex:%d khz, uldl_min_offset:%d khz\n", + dlfreq_khz, ulfreq_khz, txrx_offset, uldl_min_offset); + if (txrx_offset == uldl_min_offset) + return true; + + int band = nr_bandtable[index].band; + + // TBD.. Delta duplex is also a range for these bands n26, n91-n94, n109. + // TBD .. Add the checks later + + // Refer to section 5.4.4 in spec 38.101-5 + if (band == 256 && txrx_offset >= 165000 && txrx_offset <= 215000) + return true; + if (band == 255 && txrx_offset >= -130500 && txrx_offset <= -72500) + return true; + if (band == 254 && txrx_offset >= 862000 && txrx_offset <= 885000) + return true; + + return false; +} + uint16_t get_band(uint64_t downlink_frequency, int32_t delta_duplex) { const int64_t dl_freq_khz = downlink_frequency / 1000; const int32_t delta_duplex_khz = delta_duplex / 1000; + const int64_t ul_freq_khz = dl_freq_khz + delta_duplex_khz; uint64_t center_freq_diff_khz = UINT64_MAX; // 2^64 uint16_t current_band = 0; @@ -472,9 +498,10 @@ uint16_t get_band(uint64_t downlink_frequency, int32_t delta_duplex) if (dl_freq_khz < nr_bandtable[ind].dl_min || dl_freq_khz > nr_bandtable[ind].dl_max) continue; - int32_t current_offset_khz = nr_bandtable[ind].ul_min - nr_bandtable[ind].dl_min; + if (ul_freq_khz < nr_bandtable[ind].ul_min || ul_freq_khz > nr_bandtable[ind].ul_max) + continue; - if (current_offset_khz != delta_duplex_khz) + if (!check_delta_duplex(ind, dl_freq_khz, ul_freq_khz)) continue; int64_t center_frequency_khz = (nr_bandtable[ind].dl_max + nr_bandtable[ind].dl_min) / 2; From 40fae6136c2a3dca84dd1705b03f798c26541c12 Mon Sep 17 00:00:00 2001 From: Raghavendra Dinavahi Date: Thu, 30 Oct 2025 16:37:39 +0100 Subject: [PATCH 112/275] Added the check for non-NTN bands --- common/utils/nr/nr_common.c | 33 ++++++++++++++++++++------------- common/utils/nr/nr_common.h | 2 +- openair1/PHY/INIT/nr_init.c | 2 +- openair1/PHY/INIT/nr_parms.c | 20 +++++--------------- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index c3d0c881b3..53fc42837b 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -459,7 +459,7 @@ bool compare_relative_ul_channel_bw(int nr_band, int scs, int channel_bandwidth, return rel_bw > limit; } -static bool check_delta_duplex(int index, uint64_t dlfreq_khz, uint64_t ulfreq_khz) +static bool check_delta_duplex(int index, uint64_t dlfreq_khz, uint64_t ulfreq_khz, int64_t dlbw, int64_t ulbw) { int uldl_min_offset = nr_bandtable[index].dl_min - nr_bandtable[index].ul_min; int txrx_offset = dlfreq_khz - ulfreq_khz; @@ -470,9 +470,6 @@ static bool check_delta_duplex(int index, uint64_t dlfreq_khz, uint64_t ulfreq_k int band = nr_bandtable[index].band; - // TBD.. Delta duplex is also a range for these bands n26, n91-n94, n109. - // TBD .. Add the checks later - // Refer to section 5.4.4 in spec 38.101-5 if (band == 256 && txrx_offset >= 165000 && txrx_offset <= 215000) return true; @@ -481,16 +478,31 @@ static bool check_delta_duplex(int index, uint64_t dlfreq_khz, uint64_t ulfreq_k if (band == 254 && txrx_offset >= 862000 && txrx_offset <= 885000) return true; + // Refer to section 5.4.4 in spec 38.101-1 for these bands 24, 91-94, 109 + if (band == 24 && (txrx_offset == -101500 || txrx_offset == -120500)) + return true; + //Delta duplex is also a range for these bands n91-n94, n109. + if ((band >= 91 && band <= 94) || band == 109) { + dlbw = ((dlbw / 1000) + 1) * 1000; + ulbw = ((ulbw / 1000) + 1) * 1000; + int lower_limit = nr_bandtable[index].dl_min - nr_bandtable[index].ul_max + ((dlbw + ulbw) >> 1); + int upper_limit = nr_bandtable[index].dl_max - nr_bandtable[index].ul_min - ((dlbw + ulbw) >> 1); + LOG_I(NR_PHY, "Band %d dlbw:%ld Khz, ulbw: %ld Khz RXTX lower limit:%d khz, upper limit:%d khz\n", + band, dlbw, ulbw, lower_limit, upper_limit); + + if (txrx_offset >= lower_limit && txrx_offset <= upper_limit) + return true; + } + return false; } -uint16_t get_band(uint64_t downlink_frequency, int32_t delta_duplex) +uint16_t get_band(uint64_t downlink_frequency, int32_t delta_duplex, int64_t dlbw, int64_t ulbw) { const int64_t dl_freq_khz = downlink_frequency / 1000; const int32_t delta_duplex_khz = delta_duplex / 1000; const int64_t ul_freq_khz = dl_freq_khz + delta_duplex_khz; - uint64_t center_freq_diff_khz = UINT64_MAX; // 2^64 uint16_t current_band = 0; for (int ind = 0; ind < sizeofArray(nr_bandtable); ind++) { @@ -501,15 +513,10 @@ uint16_t get_band(uint64_t downlink_frequency, int32_t delta_duplex) if (ul_freq_khz < nr_bandtable[ind].ul_min || ul_freq_khz > nr_bandtable[ind].ul_max) continue; - if (!check_delta_duplex(ind, dl_freq_khz, ul_freq_khz)) + if (!check_delta_duplex(ind, dl_freq_khz, ul_freq_khz, dlbw, ulbw)) continue; - int64_t center_frequency_khz = (nr_bandtable[ind].dl_max + nr_bandtable[ind].dl_min) / 2; - - if (labs(dl_freq_khz - center_frequency_khz) < center_freq_diff_khz){ - current_band = nr_bandtable[ind].band; - center_freq_diff_khz = labs(dl_freq_khz - center_frequency_khz); - } + current_band = nr_bandtable[ind].band; } printf("DL frequency %"PRIu64": band %d, UL frequency %"PRIu64"\n", diff --git a/common/utils/nr/nr_common.h b/common/utils/nr/nr_common.h index e0d68a45ad..2e796bac23 100644 --- a/common/utils/nr/nr_common.h +++ b/common/utils/nr/nr_common.h @@ -285,7 +285,7 @@ int get_coreset_num_cces(uint8_t *FreqDomainResource, int duration); int get_nr_table_idx(int nr_bandP, uint8_t scs_index); int32_t get_delta_duplex(int nr_bandP, uint8_t scs_index); frame_type_t get_frame_type(uint16_t nr_bandP, uint8_t scs_index); -uint16_t get_band(uint64_t downlink_frequency, int32_t delta_duplex); +uint16_t get_band(uint64_t downlink_frequency, int32_t delta_duplex, int64_t dlbw, int64_t ulbw); int NRRIV2BW(int locationAndBandwidth,int N_RB); int NRRIV2PRBOFFSET(int locationAndBandwidth,int N_RB); int PRBalloc_to_locationandbandwidth0(int NPRB,int RBstart,int BWPsize); diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 778903be91..edf9a5c6ee 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -375,7 +375,7 @@ void nr_phy_config_request(NR_PHY_Config_t *phy_config) fp->ul_CarrierFreq = ((ul_bw_khz>>1) + gNB_config->carrier_config.uplink_frequency.value)*1000 ; int32_t dlul_offset = fp->ul_CarrierFreq - fp->dl_CarrierFreq; - fp->nr_band = get_band(fp->dl_CarrierFreq, dlul_offset); + fp->nr_band = get_band(fp->dl_CarrierFreq, dlul_offset, dl_bw_khz, ul_bw_khz); LOG_I(PHY, "DL frequency %lu Hz, UL frequency %lu Hz: band %d, uldl offset %d Hz\n", fp->dl_CarrierFreq, fp->ul_CarrierFreq, fp->nr_band, dlul_offset); diff --git a/openair1/PHY/INIT/nr_parms.c b/openair1/PHY/INIT/nr_parms.c index 2ace21eab5..cc31dc173a 100644 --- a/openair1/PHY/INIT/nr_parms.c +++ b/openair1/PHY/INIT/nr_parms.c @@ -379,15 +379,13 @@ int nr_init_frame_parms_ue(NR_DL_FRAME_PARMS *fp, fp->N_RB_SL = config->carrier_config.sl_grid_size[fp->numerology_index]; fp->frame_type = get_frame_type(fp->nr_band, fp->numerology_index); - int32_t uplink_frequency_offset = get_delta_duplex(fp->nr_band, fp->numerology_index); + int64_t uplink_frequency_offset = fp->ul_CarrierFreq - fp->dl_CarrierFreq; uplink_frequency_offset *= 1000; - LOG_I(PHY, "Initializing frame parms: DL frequency %lu Hz, UL frequency %lu Hz: band %d, uldl offset %d Hz\n", fp->dl_CarrierFreq, fp->ul_CarrierFreq, fp->nr_band, uplink_frequency_offset); + LOG_I(PHY, "Initializing frame parms: DL frequency %lu Hz, UL frequency %lu Hz: band %d, uldl offset %ld Hz\n", fp->dl_CarrierFreq, fp->ul_CarrierFreq, fp->nr_band, uplink_frequency_offset); AssertFatal(fp->frame_type==config->cell_config.frame_duplex_type, "Invalid duplex type (frame_type %d,cell_config.frame_duplex_type %d) in config request file for band %d\n", fp->frame_type,config->cell_config.frame_duplex_type,fp->nr_band); - AssertFatal(fp->ul_CarrierFreq == (fp->dl_CarrierFreq + uplink_frequency_offset), "Disagreement in uplink frequency for band %d: ul_CarrierFreq = %lu Hz vs expected %lu Hz\n", fp->nr_band, fp->ul_CarrierFreq, fp->dl_CarrierFreq + uplink_frequency_offset); - LOG_I(PHY,"Initializing frame parms for mu %d, N_RB %d, Ncp %d\n",fp->numerology_index, fp->N_RB_DL, Ncp); @@ -508,7 +506,7 @@ int nr_init_frame_parms_ue_sl(NR_DL_FRAME_PARMS *fp, fp->ofdm_offset_divisor = ofdm_offset_divisor; fp->threequarter_fs = threequarter_fs; - fp->nr_band = get_band(config->sl_carrier_config.sl_frequency, 0); + fp->nr_band = get_band(config->sl_carrier_config.sl_frequency, 0, 0, 0); fp->att_rx = 0; fp->att_tx = 0; @@ -522,8 +520,6 @@ int nr_init_frame_parms_ue_sl(NR_DL_FRAME_PARMS *fp, fp->Ncp = config->sl_bwp_config.sl_cyclic_prefix; fp->frame_type = get_frame_type(fp->nr_band, fp->numerology_index); - int32_t uplink_frequency_offset = get_delta_duplex(fp->nr_band, fp->numerology_index); - uplink_frequency_offset *= 1000; uint64_t bw_khz = (12 * config->sl_carrier_config.sl_grid_size) * (15 << config->sl_bwp_config.sl_scs); // REfer to section 3GPP spec 38.101 5.4E.2.1 @@ -539,20 +535,14 @@ int nr_init_frame_parms_ue_sl(NR_DL_FRAME_PARMS *fp, LOG_D(PHY, "CarrierFreq %lu Hz\n", fp->sl_CarrierFreq); LOG_I(PHY, - "Initializing frame parms: DL frequency %lu Hz, UL frequency %lu Hz SL frequency %lu Hz: band %d, uldl offset %d Hz\n", + "Initializing frame parms: DL frequency %lu Hz, UL frequency %lu Hz SL frequency %lu Hz: band %d\n", fp->dl_CarrierFreq, fp->ul_CarrierFreq, fp->sl_CarrierFreq, - fp->nr_band, - uplink_frequency_offset); + fp->nr_band); AssertFatal(fp->frame_type == TDD, "Sidelink bands only support TDD"); - AssertFatal(fp->ul_CarrierFreq == (fp->dl_CarrierFreq + uplink_frequency_offset), - "Disagreement in uplink frequency for band %d: ul_CarrierFreq = %lu Hz vs expected %lu Hz\n", - fp->nr_band, - fp->ul_CarrierFreq, - fp->dl_CarrierFreq + uplink_frequency_offset); LOG_I(PHY, "Initializing frame parms for mu %d, N_RB %d, Ncp %d\n", fp->numerology_index, fp->N_RB_DL, fp->Ncp); From a96d2d011847f1709bddd6e33b479efb8c9de149 Mon Sep 17 00:00:00 2001 From: Teodora Date: Wed, 22 Oct 2025 12:09:01 +0200 Subject: [PATCH 113/275] Properly pass the arguments in ly_ctx_destroy() v1 function --- radio/fhi_72/mplane/yang/get-yang.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/fhi_72/mplane/yang/get-yang.c b/radio/fhi_72/mplane/yang/get-yang.c index 60131b34cf..abe17cca63 100644 --- a/radio/fhi_72/mplane/yang/get-yang.c +++ b/radio/fhi_72/mplane/yang/get-yang.c @@ -68,7 +68,11 @@ static bool store_schemas(xmlNode *node, ru_session_t *ru_session, struct ly_ctx if (!mod) { MP_LOG_W("Unable to load module \"%s\" from RU \"%s\".\n", module_name, ru_session->ru_ip_add); nc_rpc_free(get_schema_rpc); +#ifdef MPLANE_V1 + ly_ctx_destroy(*ctx, NULL); +#elif defined MPLANE_V2 ly_ctx_destroy(*ctx); +#endif return false; } } From ad6d7a222038c227c34d17613f913517d3e319e5 Mon Sep 17 00:00:00 2001 From: Teodora Date: Wed, 22 Oct 2025 12:24:12 +0200 Subject: [PATCH 114/275] RHEL compilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit error: implicit declaration of function โ€˜reallocโ€™ --- radio/fhi_72/mplane/xml/get-xml.c | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/fhi_72/mplane/xml/get-xml.c b/radio/fhi_72/mplane/xml/get-xml.c index 6456219293..8ebd783901 100644 --- a/radio/fhi_72/mplane/xml/get-xml.c +++ b/radio/fhi_72/mplane/xml/get-xml.c @@ -23,6 +23,7 @@ #include #include +#include static char *find_ru_xml_node(xmlNode *node, const char *filter) { From d4b5291c623c546c57777191a47b6d9696a7e1cc Mon Sep 17 00:00:00 2001 From: Teodora Date: Wed, 27 Aug 2025 10:40:01 +0200 Subject: [PATCH 115/275] Facilitate RU notification initialization Use X macros for carriers and synchronization states, by mapping enum with the state values. --- radio/fhi_72/mplane/init-mplane.c | 64 +++++++++++++++----------- radio/fhi_72/mplane/ru-mplane-api.c | 14 ++++++ radio/fhi_72/mplane/ru-mplane-api.h | 34 ++++++++++++-- radio/fhi_72/mplane/subscribe-mplane.c | 28 ++--------- radio/fhi_72/oran_isolate.c | 2 +- 5 files changed, 88 insertions(+), 54 deletions(-) diff --git a/radio/fhi_72/mplane/init-mplane.c b/radio/fhi_72/mplane/init-mplane.c index fc20ba5af4..f8a401b230 100644 --- a/radio/fhi_72/mplane/init-mplane.c +++ b/radio/fhi_72/mplane/init-mplane.c @@ -141,6 +141,31 @@ bool init_mplane(ru_session_list_t *ru_session_list) return true; } +static void init_ru_notif(ru_session_t *ru_session, const char* buffer) +{ + char* usage_state = get_ru_xml_node(buffer, "usage-state"); + if (usage_state != NULL && strcmp(usage_state, "busy") == 0) { + ru_session->ru_notif.rx_carrier_state = BUSY_CARRIER; + ru_session->ru_notif.tx_carrier_state = BUSY_CARRIER; + ru_session->ru_notif.config_change = true; + } else if (usage_state != NULL && strcmp(usage_state, "active") == 0) { + ru_session->ru_notif.rx_carrier_state = READY_CARRIER; + ru_session->ru_notif.tx_carrier_state = READY_CARRIER; + ru_session->ru_notif.config_change = true; + } else { // "idle" or NULL + ru_session->ru_notif.rx_carrier_state = DISABLED_CARRIER; + ru_session->ru_notif.tx_carrier_state = DISABLED_CARRIER; + ru_session->ru_notif.config_change = false; + } + + char *ptp_state = get_ru_xml_node(buffer, "sync-state"); + ru_session->ru_notif.ptp_state = str_to_enum_ptp(ptp_state); + MP_LOG_I("RU is in \"%s\" sync state.\n", ptp_state); + + free(usage_state); + free(ptp_state); +} + bool manage_ru(ru_session_t *ru_session, const openair0_config_t *oai, const size_t num_rus) { bool success = false; @@ -149,12 +174,8 @@ bool manage_ru(ru_session_t *ru_session, const openair0_config_t *oai, const siz success = get_mplane(ru_session, &operational_ds); AssertError(success, return false, "[MPLANE] Unable to continue: could not get RU answer via get_mplane().\n"); - bool ptp_state = false; - const char *sync_state = get_ru_xml_node(operational_ds, "sync-state"); - if (strcmp(sync_state, "LOCKED") == 0) { - MP_LOG_I("RU is already PTP synchronized.\n"); - ptp_state = true; - } + // init RU notifications + init_ru_notif(ru_session, operational_ds); /* 1) as per M-plane spec, RU must be in supervised mode, where stream = NULL && filter = "/o-ran-supervision:supervision-notification"; @@ -163,7 +184,6 @@ bool manage_ru(ru_session_t *ru_session, const openair0_config_t *oai, const siz => since more than one subscription at the time within one session is not possible, we will subscribe to all notifications */ const char *stream = "NETCONF"; const char *filter = NULL; - ru_session->ru_notif.ptp_state = ptp_state; success = subscribe_mplane(ru_session, stream, filter, (void *)&ru_session->ru_notif); AssertError(success, return false, "[MPLANE] Unable to continue: could not get RU answer via subscribe_mplane().\n"); @@ -189,26 +209,18 @@ bool manage_ru(ru_session_t *ru_session, const openair0_config_t *oai, const siz success = load_yang_models(ru_session, operational_ds); AssertError(success, return false, "[MPLANE] Unable to load yang models.\n"); - if (ru_session->ru_notif.ptp_state) { - char *content = NULL; - success = configure_ru_from_yang(ru_session, oai, num_rus, &content); - AssertError(success, return false, "[MPLANE] Unable to create content for RPC for start-up procedure.\n"); + while (1) { + sleep(5); + if (!ru_session->ru_notif.ptp_state) { + char *content = NULL; + success = configure_ru_from_yang(ru_session, oai, num_rus, &content); + AssertError(success, return false, "[MPLANE] Unable to create content for RPC for start-up procedure.\n"); - success = edit_val_commmit_rpc(ru_session, content); - AssertError(success, return false, "[MPLANE] Unable to continue.\n"); - free(content); - } - - const char *usage_state = get_ru_xml_node(operational_ds, "usage-state"); - MP_LOG_I("Usage state = \"%s\" for RU \"%s\".\n", usage_state, ru_session->ru_ip_add); - if (strcmp(usage_state, "busy") == 0) { // carriers are already activated - ru_session->ru_notif.rx_carrier_state = true; - ru_session->ru_notif.tx_carrier_state = true; - ru_session->ru_notif.config_change = true; - } else { - ru_session->ru_notif.rx_carrier_state = false; - ru_session->ru_notif.tx_carrier_state = false; - ru_session->ru_notif.config_change = false; + success = edit_val_commmit_rpc(ru_session, content); + AssertError(success, return false, "[MPLANE] Unable to continue.\n"); + free(content); + break; + } } free(operational_ds); diff --git a/radio/fhi_72/mplane/ru-mplane-api.c b/radio/fhi_72/mplane/ru-mplane-api.c index df4bf3b8ba..acc3425ca1 100644 --- a/radio/fhi_72/mplane/ru-mplane-api.c +++ b/radio/fhi_72/mplane/ru-mplane-api.c @@ -25,6 +25,20 @@ #include +ptp_state_e str_to_enum_ptp(const char* value) { +#define X(name, str) if (value != NULL && strcmp(value, str) == 0) return name; + PTP_STATE +#undef X + return PTP_COUNT; +}; + +carrier_state_e str_to_enum_carrier(const char* value) { +#define X(name, str) if (value != NULL && strcmp(value, str) == 0) return name; + CARRIER_STATE +#undef X + return CARRIER_COUNT; +}; + static void free_match_list(char **match_list, size_t count) { for (size_t i = 0; i < count; i++) { diff --git a/radio/fhi_72/mplane/ru-mplane-api.h b/radio/fhi_72/mplane/ru-mplane-api.h index 85fac5515c..46f4e27deb 100644 --- a/radio/fhi_72/mplane/ru-mplane-api.h +++ b/radio/fhi_72/mplane/ru-mplane-api.h @@ -31,10 +31,38 @@ #define MP_LOG_I(x, args...) LOG_I(HW, "[MPLANE] " x, ##args) #define MP_LOG_W(x, args...) LOG_W(HW, "[MPLANE] " x, ##args) +#define PTP_STATE \ + X(LOCKED_PTP, "LOCKED") \ + X(FREERUN_PTP, "FREERUN") \ + X(HOLDOVER_PTP, "HOLDOVER") + +typedef enum { +#define X(name, str) name, + PTP_STATE +#undef X + PTP_COUNT +} ptp_state_e; + +ptp_state_e str_to_enum_ptp(const char* value); + +#define CARRIER_STATE \ + X(READY_CARRIER, "READY") \ + X(DISABLED_CARRIER, "DISABLED") \ + X(BUSY_CARRIER, "BUSY") + +typedef enum { +#define X(name, str) name, + CARRIER_STATE +#undef X + CARRIER_COUNT +} carrier_state_e; + +carrier_state_e str_to_enum_carrier(const char* value); + typedef struct { - bool ptp_state; - bool rx_carrier_state; - bool tx_carrier_state; + ptp_state_e ptp_state; // "LOCKED", "FREERUN", "HOLDOVER" + carrier_state_e rx_carrier_state; // "READY", "DISABLED", "BUSY" + carrier_state_e tx_carrier_state; // "READY", "DISABLED", "BUSY" bool config_change; // to be extended with any notification callback diff --git a/radio/fhi_72/mplane/subscribe-mplane.c b/radio/fhi_72/mplane/subscribe-mplane.c index bec7e28a68..bff81c5391 100644 --- a/radio/fhi_72/mplane/subscribe-mplane.c +++ b/radio/fhi_72/mplane/subscribe-mplane.c @@ -30,13 +30,8 @@ static void recv_notif_v1(const struct nc_notif *notif, ru_notif_t *answer) { const char *node_name = notif->tree->child->attr->name; - const char *value = notif->tree->child->attr->value_str; if (strcmp(node_name, "sync-state")) { - if (strcmp(value, "LOCKED") == 0) { - answer->ptp_state = true; - } else { - answer->ptp_state = false; - } + answer->ptp_state = str_to_enum_ptp(notif->tree->child->attr->value_str); } // carriers state - to be filled @@ -90,26 +85,11 @@ static void recv_notif_v2(struct lyd_node_inner *op, ru_notif_t *answer) const char *notif = op->schema->name; if (strcmp(notif, "synchronization-state-change") == 0) { - const char *value = lyd_get_value(op->child); - if (strcmp(value, "LOCKED") == 0) { - answer->ptp_state = true; - } else { // "FREERUN" or "HOLDOVER" - answer->ptp_state = false; - } + answer->ptp_state = str_to_enum_ptp(lyd_get_value(op->child)); } else if (strcmp(notif, "rx-array-carriers-state-change") == 0) { - const char *value = lyd_get_value(lyd_child(op->child)->next); - if (strcmp(value, "READY") == 0) { - answer->rx_carrier_state = true; - } else { // "DISABLED" or "BUSY" - answer->rx_carrier_state = false; - } + answer->rx_carrier_state = str_to_enum_carrier(lyd_get_value(lyd_child(op->child)->next)); } else if (strcmp(notif, "tx-array-carriers-state-change") == 0) { - const char *value = lyd_get_value(lyd_child(op->child)->next); - if (strcmp(value, "READY") == 0) { - answer->tx_carrier_state = true; - } else { // "DISABLED" or "BUSY" - answer->tx_carrier_state = false; - } + answer->tx_carrier_state = str_to_enum_carrier(lyd_get_value(lyd_child(op->child)->next)); } else if (strcmp(notif, "netconf-config-change") == 0) { answer->config_change = true; } diff --git a/radio/fhi_72/oran_isolate.c b/radio/fhi_72/oran_isolate.c index 48e95e7344..8a2ac0ae46 100644 --- a/radio/fhi_72/oran_isolate.c +++ b/radio/fhi_72/oran_isolate.c @@ -342,7 +342,7 @@ __attribute__((__visibility__("default"))) int transport_init(openair0_device *d bool all_rus_ready = true; for (int i = 0; i < ru_session_list.num_rus; i++) { ru_session_t *ru_session = &ru_session_list.ru_session[i]; - if (!ru_ready[i] && ru_session->ru_notif.config_change && ru_session->ru_notif.rx_carrier_state && ru_session->ru_notif.tx_carrier_state) { + if (!ru_ready[i] && ru_session->ru_notif.config_change && !ru_session->ru_notif.rx_carrier_state && !ru_session->ru_notif.tx_carrier_state) { MP_LOG_I("RU \"%s\" is now ready.\n", ru_session->ru_ip_add); ru_ready[i] = true; if (!ru_session->pm_stats.start_up_timing) { From d542021f860bb1da1b17815db098cffdbf04a207 Mon Sep 17 00:00:00 2001 From: Teodora Date: Wed, 27 Aug 2025 10:41:48 +0200 Subject: [PATCH 116/275] Retrieve the hardware states if available These 3 states: "oper-state", "admin-state", "availability-state" are optional, but shall be checked in order to activate the carriers. If an O-RU doesn't support it, we assume the expected values: - "oper-state" = "enabled" - "admin-state" = "unlocked" - "availability-state" = "NORMAL" Implement retrieval of the hardware states if O-RU supports, i.e. Benetel release v1.4.1. --- radio/fhi_72/mplane/init-mplane.c | 14 +++++++- radio/fhi_72/mplane/ru-mplane-api.c | 21 +++++++++++ radio/fhi_72/mplane/ru-mplane-api.h | 48 ++++++++++++++++++++++++++ radio/fhi_72/mplane/subscribe-mplane.c | 18 ++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) diff --git a/radio/fhi_72/mplane/init-mplane.c b/radio/fhi_72/mplane/init-mplane.c index f8a401b230..fcd908562a 100644 --- a/radio/fhi_72/mplane/init-mplane.c +++ b/radio/fhi_72/mplane/init-mplane.c @@ -158,11 +158,23 @@ static void init_ru_notif(ru_session_t *ru_session, const char* buffer) ru_session->ru_notif.config_change = false; } + char *oper_state = get_ru_xml_node(buffer, "oper-state"); + ru_session->ru_notif.hardware.oper_state = (str_to_enum_oper(oper_state) != OPER_COUNT) ? str_to_enum_oper(oper_state) : ENABLED_OPER; + + char *admin_state = get_ru_xml_node(buffer, "admin-state"); + ru_session->ru_notif.hardware.admin_state = (str_to_enum_admin(admin_state) != ADMIN_COUNT) ? str_to_enum_admin(admin_state) : UNLOCKED_ADMIN; + + char *avail_state = get_ru_xml_node(buffer, "availability-state"); + ru_session->ru_notif.hardware.avail_state = (str_to_enum_avail(avail_state) != AVAIL_COUNT) ? str_to_enum_avail(avail_state) : NORMAL_AVAIL; + char *ptp_state = get_ru_xml_node(buffer, "sync-state"); ru_session->ru_notif.ptp_state = str_to_enum_ptp(ptp_state); MP_LOG_I("RU is in \"%s\" sync state.\n", ptp_state); free(usage_state); + free(oper_state); + free(admin_state); + free(avail_state); free(ptp_state); } @@ -211,7 +223,7 @@ bool manage_ru(ru_session_t *ru_session, const openair0_config_t *oai, const siz while (1) { sleep(5); - if (!ru_session->ru_notif.ptp_state) { + if (!ru_session->ru_notif.ptp_state && !ru_session->ru_notif.hardware.oper_state && !ru_session->ru_notif.hardware.admin_state && !ru_session->ru_notif.hardware.avail_state) { char *content = NULL; success = configure_ru_from_yang(ru_session, oai, num_rus, &content); AssertError(success, return false, "[MPLANE] Unable to create content for RPC for start-up procedure.\n"); diff --git a/radio/fhi_72/mplane/ru-mplane-api.c b/radio/fhi_72/mplane/ru-mplane-api.c index acc3425ca1..649cf34a20 100644 --- a/radio/fhi_72/mplane/ru-mplane-api.c +++ b/radio/fhi_72/mplane/ru-mplane-api.c @@ -25,6 +25,27 @@ #include +oper_state_e str_to_enum_oper(const char* value) { +#define X(name, str) if (value != NULL && strcmp(value, str) == 0) return name; + OPER_STATE +#undef X + return OPER_COUNT; +}; + +admin_state_e str_to_enum_admin(const char* value) { +#define X(name, str) if (value != NULL && strcmp(value, str) == 0) return name; + ADMIN_STATE +#undef X + return ADMIN_COUNT; +}; + +avail_state_e str_to_enum_avail(const char* value) { +#define X(name, str) if (value != NULL && strcmp(value, str) == 0) return name; + AVAIL_STATE +#undef X + return AVAIL_COUNT; +}; + ptp_state_e str_to_enum_ptp(const char* value) { #define X(name, str) if (value != NULL && strcmp(value, str) == 0) return name; PTP_STATE diff --git a/radio/fhi_72/mplane/ru-mplane-api.h b/radio/fhi_72/mplane/ru-mplane-api.h index 46f4e27deb..f221f7117c 100644 --- a/radio/fhi_72/mplane/ru-mplane-api.h +++ b/radio/fhi_72/mplane/ru-mplane-api.h @@ -31,6 +31,53 @@ #define MP_LOG_I(x, args...) LOG_I(HW, "[MPLANE] " x, ##args) #define MP_LOG_W(x, args...) LOG_W(HW, "[MPLANE] " x, ##args) +#define OPER_STATE \ + X(ENABLED_OPER, "enabled") \ + X(DISABLED_OPER, "disabled") + +typedef enum { +#define X(name, str) name, + OPER_STATE +#undef X + OPER_COUNT +} oper_state_e; + +oper_state_e str_to_enum_oper(const char* value); + +#define ADMIN_STATE \ + X(UNLOCKED_ADMIN, "unlocked") \ + X(SHUTTING_DOWN_ADMIN, "shutting-down") \ + X(LOCKED_ADMIN, "locked") + +typedef enum { +#define X(name, str) name, + ADMIN_STATE +#undef X + ADMIN_COUNT +} admin_state_e; + +admin_state_e str_to_enum_admin(const char* value); + +#define AVAIL_STATE \ + X(NORMAL_AVAIL, "NORMAL") \ + X(DEGRADED_AVAIL, "DEGRADED") \ + X(FAULTY_AVAIL, "FAULTY") + +typedef enum { +#define X(name, str) name, + AVAIL_STATE +#undef X + AVAIL_COUNT +} avail_state_e; + +avail_state_e str_to_enum_avail(const char* value); + +typedef struct { + oper_state_e oper_state; // "enabled", "disabled" + admin_state_e admin_state; // "unlocked", "shutting-down", "locked" + avail_state_e avail_state; // "NORMAL", "DEGRADED", "FAULTY" +} hardware_notif_t; + #define PTP_STATE \ X(LOCKED_PTP, "LOCKED") \ X(FREERUN_PTP, "FREERUN") \ @@ -60,6 +107,7 @@ typedef enum { carrier_state_e str_to_enum_carrier(const char* value); typedef struct { + hardware_notif_t hardware; ptp_state_e ptp_state; // "LOCKED", "FREERUN", "HOLDOVER" carrier_state_e rx_carrier_state; // "READY", "DISABLED", "BUSY" carrier_state_e tx_carrier_state; // "READY", "DISABLED", "BUSY" diff --git a/radio/fhi_72/mplane/subscribe-mplane.c b/radio/fhi_72/mplane/subscribe-mplane.c index bff81c5391..ffb77265e5 100644 --- a/radio/fhi_72/mplane/subscribe-mplane.c +++ b/radio/fhi_72/mplane/subscribe-mplane.c @@ -80,6 +80,18 @@ static void log_v2_pm_info(const char *ru_ip_add, struct lyd_node_inner *stats) meas_list[7], count_list[7]); } +static void get_hardware_states(struct lyd_node_inner *op, ru_notif_t *answer) +{ + struct lyd_node *child = NULL; + LY_LIST_FOR(op->child, child) { + if (strcmp(child->schema->name, "admin-state") == 0) { + answer->hardware.admin_state = str_to_enum_admin(lyd_get_value(child)); + } else if (strcmp(child->schema->name, "availability-state") == 0) { + answer->hardware.avail_state = str_to_enum_avail(lyd_get_value(child)); + } + } +} + static void recv_notif_v2(struct lyd_node_inner *op, ru_notif_t *answer) { const char *notif = op->schema->name; @@ -92,6 +104,12 @@ static void recv_notif_v2(struct lyd_node_inner *op, ru_notif_t *answer) answer->tx_carrier_state = str_to_enum_carrier(lyd_get_value(lyd_child(op->child)->next)); } else if (strcmp(notif, "netconf-config-change") == 0) { answer->config_change = true; + } else if (strcmp(notif, "hardware-state-oper-enabled") == 0) { + answer->hardware.oper_state = ENABLED_OPER; + get_hardware_states(op, answer); + } else if (strcmp(notif, "hardware-state-oper-disabled") == 0) { + answer->hardware.oper_state = DISABLED_OPER; + get_hardware_states(op, answer); } } From 4233b89530bafd8f712371f6e99f02c1ceb80ee0 Mon Sep 17 00:00:00 2001 From: Teodora Date: Wed, 27 Aug 2025 12:12:44 +0200 Subject: [PATCH 117/275] Handle MIMO mode MIMO mode is defined by setting separate carriers to different antenna ports. Benetel v1.2.2 does not support MIMO mode to be set via M-plane, and therefore we only created one Tx and one Rx carrier. Benetel v1.4.1 supports MIMO mode setting via M-plane. Therefore, this commit introduces the adaptation based on the number of supported carriers by a DUT RU. --- radio/fhi_72/mplane/yang/create-yang-config.c | 133 +++++++++--------- 1 file changed, 70 insertions(+), 63 deletions(-) diff --git a/radio/fhi_72/mplane/yang/create-yang-config.c b/radio/fhi_72/mplane/yang/create-yang-config.c index 187fe5b7c1..8f35888797 100644 --- a/radio/fhi_72/mplane/yang/create-yang-config.c +++ b/radio/fhi_72/mplane/yang/create-yang-config.c @@ -233,85 +233,91 @@ static bool create_uplane_conf_v2(const ru_session_t *ru_session, const openair0 ret = lyd_new_path(NULL, (struct ly_ctx *)ru_session->ctx, "/o-ran-uplane-conf:user-plane-configuration", NULL, 0, root); VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create root \"user-plane-configuration\" node.\n"); - if (ru_session->ru_mplane_config.rx_carriers.num != 1 || ru_session->ru_mplane_config.tx_carriers.num !=1) - MP_LOG_I("Only one carrier supported at the moment.\n"); - + const size_t num_rx_ch = oai->rx_num_channels / num_rus; + // RX carriers - struct lyd_node *rx_carrier_node = NULL; - ret = lyd_new_list(*root, NULL, "rx-array-carriers", 0, &rx_carrier_node, ru_session->ru_mplane_config.rx_carriers.name[0]); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"rx-array-carriers\" node.\n"); + const size_t num_rx_carriers = (ru_session->ru_mplane_config.rx_carriers.num == 1) ? 1 : num_rx_ch; + for (size_t i = 0; i < num_rx_carriers; i++) { + struct lyd_node *rx_carrier_node = NULL; + ret = lyd_new_list(*root, NULL, "rx-array-carriers", 0, &rx_carrier_node, ru_session->ru_mplane_config.rx_carriers.name[i]); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"rx-array-carriers\" node.\n"); - char rx_freq[16]; - snprintf(rx_freq, sizeof(rx_freq), "%.0f", oai->rx_freq[0]); - ret = lyd_new_term(rx_carrier_node, NULL, "center-of-channel-bandwidth", rx_freq, 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"center-of-channel-bandwidth\" node.\n"); + char rx_freq[16]; + snprintf(rx_freq, sizeof(rx_freq), "%.0f", oai->rx_freq[0]); + ret = lyd_new_term(rx_carrier_node, NULL, "center-of-channel-bandwidth", rx_freq, 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"center-of-channel-bandwidth\" node.\n"); - char rx_arfcn[16]; - snprintf(rx_arfcn, sizeof(rx_arfcn), "%d", to_nrarfcn(oai->nr_band, oai->rx_freq[0], oai->nr_scs_for_raster, oai->rx_bw)); - ret = lyd_new_term(rx_carrier_node, NULL, "absolute-frequency-center", rx_arfcn, 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"absolute-frequency-center\" node.\n"); + char rx_arfcn[16]; + snprintf(rx_arfcn, sizeof(rx_arfcn), "%d", to_nrarfcn(oai->nr_band, oai->rx_freq[0], oai->nr_scs_for_raster, oai->rx_bw)); + ret = lyd_new_term(rx_carrier_node, NULL, "absolute-frequency-center", rx_arfcn, 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"absolute-frequency-center\" node.\n"); - char rx_bw[16]; - snprintf(rx_bw, sizeof(rx_bw), "%.0f", oai->rx_bw); - ret = lyd_new_term(rx_carrier_node, NULL, "channel-bandwidth", rx_bw, 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"channel-bandwidth\" node.\n"); + char rx_bw[16]; + snprintf(rx_bw, sizeof(rx_bw), "%.0f", oai->rx_bw); + ret = lyd_new_term(rx_carrier_node, NULL, "channel-bandwidth", rx_bw, 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"channel-bandwidth\" node.\n"); - ret = lyd_new_term(rx_carrier_node, NULL, "downlink-radio-frame-offset", "0", 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-radio-frame-offset\" node.\n"); + ret = lyd_new_term(rx_carrier_node, NULL, "downlink-radio-frame-offset", "0", 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-radio-frame-offset\" node.\n"); - ret = lyd_new_term(rx_carrier_node, NULL, "downlink-sfn-offset", "0", 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-sfn-offset\" node.\n"); + ret = lyd_new_term(rx_carrier_node, NULL, "downlink-sfn-offset", "0", 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-sfn-offset\" node.\n"); - // gain offset applied uniformly across all array elements or layers - // needs to be within - ret = lyd_new_term(rx_carrier_node, NULL, "gain-correction", "0", 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"gain-correction\" node.\n"); + // gain offset applied uniformly across all array elements or layers + // needs to be within + ret = lyd_new_term(rx_carrier_node, NULL, "gain-correction", "0", 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"gain-correction\" node.\n"); - ret = lyd_new_term(rx_carrier_node, NULL, "n-ta-offset", "0", 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"n-ta-offset\" node.\n"); + ret = lyd_new_term(rx_carrier_node, NULL, "n-ta-offset", "0", 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"n-ta-offset\" node.\n"); - ret = lyd_new_term(rx_carrier_node, NULL, "active", "ACTIVE", 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"active\" node.\n"); + ret = lyd_new_term(rx_carrier_node, NULL, "active", "ACTIVE", 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"active\" node.\n"); + } + + const size_t num_tx_ch = oai->tx_num_channels / num_rus; // TX carriers - struct lyd_node *tx_carrier_node = NULL; - ret = lyd_new_list(*root, NULL, "tx-array-carriers", 0, &tx_carrier_node, ru_session->ru_mplane_config.tx_carriers.name[0]); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"tx-array-carriers\" node.\n"); + const size_t num_tx_carriers = (ru_session->ru_mplane_config.tx_carriers.num == 1) ? 1 : num_tx_ch; + for (size_t i = 0; i < num_tx_carriers; i++) { + struct lyd_node *tx_carrier_node = NULL; + ret = lyd_new_list(*root, NULL, "tx-array-carriers", 0, &tx_carrier_node, ru_session->ru_mplane_config.tx_carriers.name[i]); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"tx-array-carriers\" node.\n"); - char tx_freq[16]; - snprintf(tx_freq, sizeof(tx_freq), "%.0f", oai->tx_freq[0]); - ret = lyd_new_term(tx_carrier_node, NULL, "center-of-channel-bandwidth", tx_freq, 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"center-of-channel-bandwidth\" node.\n"); + char tx_freq[16]; + snprintf(tx_freq, sizeof(tx_freq), "%.0f", oai->tx_freq[0]); + ret = lyd_new_term(tx_carrier_node, NULL, "center-of-channel-bandwidth", tx_freq, 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"center-of-channel-bandwidth\" node.\n"); - char tx_arfcn[16]; - snprintf(tx_arfcn, sizeof(tx_arfcn), "%d", to_nrarfcn(oai->nr_band, oai->tx_freq[0], oai->nr_scs_for_raster, oai->tx_bw)); - ret = lyd_new_term(tx_carrier_node, NULL, "absolute-frequency-center", tx_arfcn, 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"absolute-frequency-center\" node.\n"); + char tx_arfcn[16]; + snprintf(tx_arfcn, sizeof(tx_arfcn), "%d", to_nrarfcn(oai->nr_band, oai->tx_freq[0], oai->nr_scs_for_raster, oai->tx_bw)); + ret = lyd_new_term(tx_carrier_node, NULL, "absolute-frequency-center", tx_arfcn, 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"absolute-frequency-center\" node.\n"); - char tx_bw[16]; - snprintf(tx_bw, sizeof(tx_bw), "%.0f", oai->tx_bw); - ret = lyd_new_term(tx_carrier_node, NULL, "channel-bandwidth", tx_bw, 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"channel-bandwidth\" node.\n"); + char tx_bw[16]; + snprintf(tx_bw, sizeof(tx_bw), "%.0f", oai->tx_bw); + ret = lyd_new_term(tx_carrier_node, NULL, "channel-bandwidth", tx_bw, 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"channel-bandwidth\" node.\n"); - // oai->tx_gain takes the value of att_tx from the RU section of gNB config file - // we assume the same gain is applied to all channels; same as above for the frequency - const double tx_gain_value = ((oai->tx_gain[0] * 10000) < ru_session->xran_mplane.max_tx_gain) ? (oai->tx_gain[0] * 10000) : ru_session->xran_mplane.max_tx_gain; - char tx_gain_str[8]; - snprintf(tx_gain_str, sizeof(tx_gain_str), "%.f", tx_gain_value); - ret = lyd_new_term(tx_carrier_node, NULL, "gain", tx_gain_str, 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"gain\" node.\n"); + // oai->tx_gain takes the value of att_tx from the RU section of gNB config file + // we assume the same gain is applied to all channels; same as above for the frequency + const double tx_gain_value = ((oai->tx_gain[0] * 10000) < ru_session->xran_mplane.max_tx_gain) ? (oai->tx_gain[0] * 10000) : ru_session->xran_mplane.max_tx_gain; + char tx_gain_str[8]; + snprintf(tx_gain_str, sizeof(tx_gain_str), "%.f", tx_gain_value); + ret = lyd_new_term(tx_carrier_node, NULL, "gain", tx_gain_str, 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"gain\" node.\n"); - ret = lyd_new_term(tx_carrier_node, NULL, "downlink-radio-frame-offset", "0", 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-radio-frame-offset\" node.\n"); + ret = lyd_new_term(tx_carrier_node, NULL, "downlink-radio-frame-offset", "0", 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-radio-frame-offset\" node.\n"); - ret = lyd_new_term(tx_carrier_node, NULL, "downlink-sfn-offset", "0", 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-sfn-offset\" node.\n"); + ret = lyd_new_term(tx_carrier_node, NULL, "downlink-sfn-offset", "0", 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-sfn-offset\" node.\n"); - ret = lyd_new_term(tx_carrier_node, NULL, "active", "ACTIVE", 0, NULL); - VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"active\" node.\n"); + ret = lyd_new_term(tx_carrier_node, NULL, "active", "ACTIVE", 0, NULL); + VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"active\" node.\n"); + } // PUSCH & PRACH endpoints - const size_t num_rx_ch = oai->rx_num_channels / num_rus; for (size_t i = 0; i < num_rx_ch; i++) { struct lyd_node *pusch_node = NULL; ret = lyd_new_list(*root, NULL, "low-level-rx-endpoints", 0, &pusch_node, ru_session->ru_mplane_config.rx_endpoints.name[i]); @@ -330,7 +336,6 @@ static bool create_uplane_conf_v2(const ru_session_t *ru_session, const openair0 } // PDSCH endpoints - const size_t num_tx_ch = oai->tx_num_channels / num_rus; for (size_t i = 0; i < num_tx_ch; i++) { struct lyd_node *pdsch_node = NULL; ret = lyd_new_list(*root, NULL, "low-level-tx-endpoints", 0, &pdsch_node, ru_session->ru_mplane_config.tx_endpoints.name[i]); @@ -342,6 +347,7 @@ static bool create_uplane_conf_v2(const ru_session_t *ru_session, const openair0 // PUSCH and PRACH links for (size_t i = 0; i < num_rx_ch; i++) { + const size_t rx_carrier_id = (num_rx_carriers == 1) ? 0 : i; // PUSCH struct lyd_node *pusch_link_node = NULL; char pusch_link[16]; @@ -352,7 +358,7 @@ static bool create_uplane_conf_v2(const ru_session_t *ru_session, const openair0 ret = lyd_new_term(pusch_link_node, NULL, "processing-element", u_proc_elem, 0, NULL); VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"processing-element\" node.\n"); - ret = lyd_new_term(pusch_link_node, NULL, "rx-array-carrier", ru_session->ru_mplane_config.rx_carriers.name[0], 0, NULL); + ret = lyd_new_term(pusch_link_node, NULL, "rx-array-carrier", ru_session->ru_mplane_config.rx_carriers.name[rx_carrier_id], 0, NULL); VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"rx-array-carrier\" node.\n"); ret = lyd_new_term(pusch_link_node, NULL, "low-level-rx-endpoint", ru_session->ru_mplane_config.rx_endpoints.name[i], 0, NULL); @@ -368,7 +374,7 @@ static bool create_uplane_conf_v2(const ru_session_t *ru_session, const openair0 ret = lyd_new_term(prach_link_node, NULL, "processing-element", u_proc_elem, 0, NULL); VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"processing-element\" node.\n"); - ret = lyd_new_term(prach_link_node, NULL, "rx-array-carrier", ru_session->ru_mplane_config.rx_carriers.name[0], 0, NULL); + ret = lyd_new_term(prach_link_node, NULL, "rx-array-carrier", ru_session->ru_mplane_config.rx_carriers.name[rx_carrier_id], 0, NULL); VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"rx-array-carrier\" node.\n"); const size_t prach_endpoint_name_offset = i + (ru_session->ru_mplane_config.rx_endpoints.num / 2); @@ -378,6 +384,7 @@ static bool create_uplane_conf_v2(const ru_session_t *ru_session, const openair0 // PDSCH links for (size_t i = 0; i < num_tx_ch; i++) { + const size_t tx_carrier_id = (num_tx_carriers == 1) ? 0 : i; struct lyd_node *pdsch_link_node = NULL; char pdsch_link[16]; snprintf(pdsch_link, sizeof(pdsch_link), "%s%ld", "PdschLink", i); @@ -387,7 +394,7 @@ static bool create_uplane_conf_v2(const ru_session_t *ru_session, const openair0 ret = lyd_new_term(pdsch_link_node, NULL, "processing-element", u_proc_elem, 0, NULL); VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"processing-element\" node.\n"); - ret = lyd_new_term(pdsch_link_node, NULL, "tx-array-carrier", ru_session->ru_mplane_config.tx_carriers.name[0], 0, NULL); + ret = lyd_new_term(pdsch_link_node, NULL, "tx-array-carrier", ru_session->ru_mplane_config.tx_carriers.name[tx_carrier_id], 0, NULL); VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"tx-array-carrier\" node.\n"); ret = lyd_new_term(pdsch_link_node, NULL, "low-level-tx-endpoint", ru_session->ru_mplane_config.tx_endpoints.name[i], 0, NULL); From 0ad393f28e9ef1f6864896314da07c8ed6aa2ec8 Mon Sep 17 00:00:00 2001 From: Teodora Date: Thu, 28 Aug 2025 12:45:19 +0200 Subject: [PATCH 118/275] Update yang models to the O-RAN.WG4.MP-YANGs-R004-v16.01 It is backwards compatible with the O-RAN.WG4.MP-YANGs-v04.00, previous supported version. --- radio/fhi_72/mplane/yang/get-yang.c | 2 +- .../mplane/yang/models/ietf-crypto-types.yang | 1985 +++------ .../yang/models/o-ran-common-yang-types.yang | 449 ++ .../models/o-ran-compression-factors.yang | 438 +- .../yang/models/o-ran-delay-management.yang | 591 +++ .../yang/models/o-ran-file-management.yang | 287 +- .../mplane/yang/models/o-ran-hardware.yang | 461 ++ .../mplane/yang/models/o-ran-interfaces.yang | 346 +- .../mplane/yang/models/o-ran-module-cap.yang | 576 ++- .../models/o-ran-performance-management.yang | 2008 ++++++++- .../yang/models/o-ran-processing-element.yang | 425 +- .../mplane/yang/models/o-ran-uplane-conf.yang | 3771 ++++++++++++++++- .../mplane/yang/models/o-ran-usermgmt.yang | 331 ++ .../yang/models/o-ran-wg4-features.yang | 532 +++ 14 files changed, 10223 insertions(+), 1979 deletions(-) create mode 100644 radio/fhi_72/mplane/yang/models/o-ran-common-yang-types.yang create mode 100644 radio/fhi_72/mplane/yang/models/o-ran-delay-management.yang create mode 100644 radio/fhi_72/mplane/yang/models/o-ran-hardware.yang create mode 100644 radio/fhi_72/mplane/yang/models/o-ran-usermgmt.yang create mode 100644 radio/fhi_72/mplane/yang/models/o-ran-wg4-features.yang diff --git a/radio/fhi_72/mplane/yang/get-yang.c b/radio/fhi_72/mplane/yang/get-yang.c index abe17cca63..08abeb4896 100644 --- a/radio/fhi_72/mplane/yang/get-yang.c +++ b/radio/fhi_72/mplane/yang/get-yang.c @@ -120,7 +120,7 @@ bool load_yang_models(ru_session_t *ru_session, const char *buffer) 1) the yang models order is not good - the dependancy models have to be loaded first 2) earlier O-RAN yang versions (e.g. v4) is not properly defined (i.e. optional parameters should not be included by default) */ const char *yang_dir = YANG_MODELS; - const char *yang_models[] = {"ietf-interfaces", "iana-if-type", "ietf-ip", "iana-hardware", "ietf-hardware", "o-ran-interfaces", "o-ran-module-cap", "o-ran-compression-factors", "o-ran-processing-element", "o-ran-uplane-conf", "ietf-netconf-acm", "ietf-crypto-types", "o-ran-file-management", "o-ran-performance-management"}; + const char *yang_models[] = {"ietf-interfaces", "iana-if-type", "ietf-ip", "iana-hardware", "ietf-hardware", "o-ran-wg4-features", "o-ran-interfaces", "o-ran-module-cap", "o-ran-compression-factors", "ietf-crypto-types", "o-ran-usermgmt", "o-ran-processing-element", "o-ran-hardware", "o-ran-common-yang-types", "o-ran-delay-management", "o-ran-uplane-conf", "ietf-netconf-acm", "o-ran-file-management", "o-ran-performance-management"}; #ifdef MPLANE_V1 *ctx = ly_ctx_new(yang_dir, 0); diff --git a/radio/fhi_72/mplane/yang/models/ietf-crypto-types.yang b/radio/fhi_72/mplane/yang/models/ietf-crypto-types.yang index 1b94b03176..342dde899c 100644 --- a/radio/fhi_72/mplane/yang/models/ietf-crypto-types.yang +++ b/radio/fhi_72/mplane/yang/models/ietf-crypto-types.yang @@ -19,28 +19,27 @@ module ietf-crypto-types { "IETF NETCONF (Network Configuration) Working Group"; contact - "WG Web: - WG List: - Author: Kent Watsen - Author: Wang Haiguang "; + "WG Web: https://datatracker.ietf.org/wg/netconf + WG List: NETCONF WG list + Author: Kent Watsen "; description "This module defines common YANG types for cryptographic applications. - Copyright (c) 2019 IETF Trust and the persons identified + Copyright (c) 2022 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or without modification, is permitted pursuant to, and - subject to the license terms contained in, the Simplified + subject to the license terms contained in, the Revised BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info). - This version of this YANG module is part of RFC XXXX - (https://www.rfc-editor.org/info/rfcXXXX); see the RFC - itself for full legal notices.; + This version of this YANG module is part of RFC AAAA + (https://www.rfc-editor.org/info/rfcAAAA); see the RFC + itself for full legal notices. The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', @@ -49,1066 +48,361 @@ module ietf-crypto-types { (RFC 8174) when, and only when, they appear in all capitals, as shown here."; - revision 2019-04-29 { + revision 2022-12-12 { description "Initial version"; reference - "RFC XXXX: Common YANG Data Types for Cryptography"; + "RFC AAAA: YANG Data Types and Groupings for Cryptography"; } - /**************************************/ - /* Identities for Hash Algorithms */ - /**************************************/ + /****************/ + /* Features */ + /****************/ - identity hash-algorithm { + feature one-symmetric-key-format { description - "A base identity for hash algorithm verification."; + "Indicates that the server supports the + 'one-symmetric-key-format' identity."; } - identity sha-224 { - base hash-algorithm; + feature one-asymmetric-key-format { description - "The SHA-224 algorithm."; + "Indicates that the server supports the + 'one-asymmetric-key-format' identity."; + } + + feature symmetrically-encrypted-value-format { + description + "Indicates that the server supports the + 'symmetrically-encrypted-value-format' identity."; + } + + feature asymmetrically-encrypted-value-format { + description + "Indicates that the server supports the + 'asymmetrically-encrypted-value-format' identity."; + } + + feature cms-enveloped-data-format { + description + "Indicates that the server supports the + 'cms-enveloped-data-format' identity."; + } + + feature cms-encrypted-data-format { + description + "Indicates that the server supports the + 'cms-encrypted-data-format' identity."; + } + + feature csr-generation { + description + "Indicates that the server implements the + 'generate-csr' action."; + } + + feature p10-based-csrs { + description + "Indicates that the erver implements support + for generating P10-based CSRs, as defined + in RFC 2986."; reference - "RFC 6234: US Secure Hash Algorithms."; - } - identity sha-256 { - base hash-algorithm; - description - "The SHA-256 algorithm."; - reference - "RFC 6234: US Secure Hash Algorithms."; + "RFC 2986: PKCS #10: Certification Request Syntax + Specification Version 1.7"; } - identity sha-384 { - base hash-algorithm; + feature certificate-expiration-notification { description - "The SHA-384 algorithm."; - reference - "RFC 6234: US Secure Hash Algorithms."; + "Indicates that the server implements the + 'certificate-expiration' notification."; } - identity sha-512 { - base hash-algorithm; + feature hidden-keys { description - "The SHA-512 algorithm."; - reference - "RFC 6234: US Secure Hash Algorithms."; + "Indicates that the server supports hidden keys."; } - /***********************************************/ - /* Identities for Asymmetric Key Algorithms */ - /***********************************************/ - - identity asymmetric-key-algorithm { + feature password-encryption { description - "Base identity from which all asymmetric key - encryption Algorithm."; + "Indicates that the server supports password + encryption."; } - identity rsa1024 { - base asymmetric-key-algorithm; + feature symmetric-key-encryption { description - "The RSA algorithm using a 1024-bit key."; - reference - "RFC 8017: - PKCS #1: RSA Cryptography Specifications Version 2.2."; + "Indicates that the server supports encryption + of symmetric keys."; } - identity rsa2048 { - base asymmetric-key-algorithm; + feature private-key-encryption { description - "The RSA algorithm using a 2048-bit key."; - reference - "RFC 8017: - PKCS #1: RSA Cryptography Specifications Version 2.2."; + "Indicates that the server supports encryption + of private keys."; } - identity rsa3072 { - base asymmetric-key-algorithm; + /*************************************************/ + /* Base Identities for Key Format Structures */ + /*************************************************/ + + identity symmetric-key-format { description - "The RSA algorithm using a 3072-bit key."; - reference - "RFC 8017: - PKCS #1: RSA Cryptography Specifications Version 2.2."; + "Base key-format identity for symmetric keys."; } - identity rsa4096 { - base asymmetric-key-algorithm; + identity public-key-format { description - "The RSA algorithm using a 4096-bit key."; - reference - "RFC 8017: - PKCS #1: RSA Cryptography Specifications Version 2.2."; + "Base key-format identity for public keys."; } - identity rsa7680 { - base asymmetric-key-algorithm; + identity private-key-format { description - "The RSA algorithm using a 7680-bit key."; - reference - "RFC 8017: - PKCS #1: RSA Cryptography Specifications Version 2.2."; - } - - identity rsa15360 { - base asymmetric-key-algorithm; - description - "The RSA algorithm using a 15360-bit key."; - reference - "RFC 8017: - PKCS #1: RSA Cryptography Specifications Version 2.2."; - } - - identity secp192r1 { - base asymmetric-key-algorithm; - description - "The ECDSA algorithm using a NIST P256 Curve."; - reference - "RFC 6090: - Fundamental Elliptic Curve Cryptography Algorithms."; - } - identity secp224r1 { - base asymmetric-key-algorithm; - description - "The ECDSA algorithm using a NIST P256 Curve."; - reference - "RFC 6090: - Fundamental Elliptic Curve Cryptography Algorithms."; - } - - identity secp256r1 { - base asymmetric-key-algorithm; - description - "The ECDSA algorithm using a NIST P256 Curve."; - reference - "RFC 6090: - Fundamental Elliptic Curve Cryptography Algorithms."; - } - - identity secp384r1 { - base asymmetric-key-algorithm; - description - "The ECDSA algorithm using a NIST P256 Curve."; - reference - "RFC 6090: - Fundamental Elliptic Curve Cryptography Algorithms."; - } - - identity secp521r1 { - base asymmetric-key-algorithm; - description - "The ECDSA algorithm using a NIST P256 Curve."; - reference - "RFC 6090: - Fundamental Elliptic Curve Cryptography Algorithms."; - } - - /*************************************/ - /* Identities for MAC Algorithms */ - /*************************************/ - - identity mac-algorithm { - description - "A base identity for mac generation."; - } - - identity hmac-sha1 { - base mac-algorithm; - description - "Generating MAC using SHA1 hash function"; - reference - "RFC 3174: US Secure Hash Algorithm 1 (SHA1)"; - } - - identity hmac-sha1-96 { - base mac-algorithm; - description - "Generating MAC using SHA1 hash function"; - reference - "RFC 2404: The Use of HMAC-SHA-1-96 within ESP and AH"; - } - - identity hmac-sha2-224 { - base mac-algorithm; - description - "Generating MAC using SHA2 hash function"; - reference - "RFC 6234: - US Secure Hash Algorithms (SHA and SHA-based HMAC and - HKDF)"; - } - - identity hmac-sha2-256 { - base mac-algorithm; - description - "Generating MAC using SHA2 hash function"; - reference - "RFC 6234: - US Secure Hash Algorithms (SHA and SHA-based HMAC and - HKDF)"; - } - - identity hmac-sha2-256-128 { - base mac-algorithm; - description - "Generating a 256 bits MAC using SHA2 hash function and - truncate it to 128 bits"; - reference - "RFC 4868: - Using HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512 - with IPsec"; - } - - identity hmac-sha2-384 { - base mac-algorithm; - description - "Generating MAC using SHA2 hash function"; - reference - "RFC 6234: - US Secure Hash Algorithms (SHA and SHA-based HMAC and - HKDF)"; - } - - identity hmac-sha2-384-192 { - base mac-algorithm; - description - "Generating a 384 bits MAC using SHA2 hash function and - truncate it to 192 bits"; - reference - "RFC 4868: - Using HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512 with - IPsec"; - } - - identity hmac-sha2-512 { - base mac-algorithm; - description - "Generating MAC using SHA2 hash function"; - reference - "RFC 6234: - US Secure Hash Algorithms (SHA and SHA-based HMAC and - HKDF)"; - } - - identity hmac-sha2-512-256 { - base mac-algorithm; - description - "Generating a 512 bits MAC using SHA2 hash function and - truncating it to 256 bits"; - reference - "RFC 4868: - Using HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512 with - IPsec"; - } - - identity aes-128-gmac { - base mac-algorithm; - description - "Generating MAC using the Advanced Encryption Standard (AES) - Galois Message Authentication Code (GMAC) as a mechanism to - provide data origin authentication"; - reference - "RFC 4543: - The Use of Galois Message Authentication Code (GMAC) in - IPsec ESP and AH"; - } - - identity aes-192-gmac { - base mac-algorithm; - description - "Generating MAC using the Advanced Encryption Standard (AES) - Galois Message Authentication Code (GMAC) as a mechanism to - provide data origin authentication"; - reference - "RFC 4543: - The Use of Galois Message Authentication Code (GMAC) in - IPsec ESP and AH"; - } - - identity aes-256-gmac { - base mac-algorithm; - description - "Generating MAC using the Advanced Encryption Standard (AES) - Galois Message Authentication Code (GMAC) as a mechanism to - provide data origin authentication"; - reference - "RFC 4543: - The Use of Galois Message Authentication Code (GMAC) in - IPsec ESP and AH"; - } - - identity aes-cmac-96 { - base mac-algorithm; - description - "Generating MAC using Advanced Encryption Standard (AES) - Cipher-based Message Authentication Code (CMAC)"; - reference - "RFC 4494: The AES-CMAC-96 Algorithm and its Use with IPsec"; - } - - identity aes-cmac-128 { - base mac-algorithm; - description - "Generating MAC using Advanced Encryption Standard (AES) - Cipher-based Message Authentication Code (CMAC)"; - reference - "RFC 4493: The AES-CMAC Algorithm"; - } - - /********************************************/ - /* Identities for Encryption Algorithms */ - /********************************************/ - - identity encryption-algorithm { - description - "A base identity for encryption algorithm."; - } - - identity aes-128-cbc { - base encryption-algorithm; - description - "Encrypt message with AES algorithm in CBC mode with a key - length of 128 bits"; - reference - "RFC 3565: - Use of the Advanced Encryption Standard (AES) Encryption - Algorithm in Cryptographic Message Syntax (CMS)"; - } - - identity aes-192-cbc { - base encryption-algorithm; - description - "Encrypt message with AES algorithm in CBC mode with a key - length of 192 bits"; - reference - "RFC 3565: - Use of the Advanced Encryption Standard (AES) Encryption - Algorithm in Cryptographic Message Syntax (CMS)"; - } - - identity aes-256-cbc { - base encryption-algorithm; - description - "Encrypt message with AES algorithm in CBC mode with a key - length of 256 bits"; - reference - "RFC 3565: - Use of the Advanced Encryption Standard (AES) Encryption - Algorithm in Cryptographic Message Syntax (CMS)"; - } - - identity aes-128-ctr { - base encryption-algorithm; - description - "Encrypt message with AES algorithm in CTR mode with a key - length of 128 bits"; - reference - "RFC 3686: - Using Advanced Encryption Standard (AES) Counter Mode with - IPsec Encapsulating Security Payload (ESP)"; - } - identity aes-192-ctr { - base encryption-algorithm; - description - "Encrypt message with AES algorithm in CTR mode with a key - length of 192 bits"; - reference - "RFC 3686: - Using Advanced Encryption Standard (AES) Counter Mode with - IPsec Encapsulating Security Payload (ESP)"; - } - - identity aes-256-ctr { - base encryption-algorithm; - description - "Encrypt message with AES algorithm in CTR mode with a key - length of 256 bits"; - reference - "RFC 3686: - Using Advanced Encryption Standard (AES) Counter Mode with - IPsec Encapsulating Security Payload (ESP)"; + "Base key-format identity for private keys."; } /****************************************************/ - /* Identities for Encryption and MAC Algorithms */ + /* Identities for Private Key Format Structures */ /****************************************************/ - identity encryption-and-mac-algorithm { + identity rsa-private-key-format { + base private-key-format; description - "A base identity for encryption and MAC algorithm."; - } - - identity aes-128-ccm { - base encryption-and-mac-algorithm; - description - "Encrypt message with AES algorithm in CCM mode with a key - length of 128 bits; it can also be used for generating MAC"; + "Indicates that the private key value is encoded + as an RSAPrivateKey (from RFC 3447)."; reference - "RFC 4309: - Using Advanced Encryption Standard (AES) CCM Mode with - IPsec Encapsulating Security Payload (ESP)"; + "RFC 3447: PKCS #1: RSA Cryptography + Specifications Version 2.2"; } - identity aes-192-ccm { - base encryption-and-mac-algorithm; + identity ec-private-key-format { + base private-key-format; description - "Encrypt message with AES algorithm in CCM mode with a key - length of 192 bits; it can also be used for generating MAC"; + "Indicates that the private key value is encoded + as an ECPrivateKey (from RFC 5915)"; reference - "RFC 4309: - Using Advanced Encryption Standard (AES) CCM Mode with - IPsec Encapsulating Security Payload (ESP)"; + "RFC 5915: Elliptic Curve Private Key Structure"; } - identity aes-256-ccm { - base encryption-and-mac-algorithm; + identity one-asymmetric-key-format { + if-feature "one-asymmetric-key-format"; + base private-key-format; description - "Encrypt message with AES algorithm in CCM mode with a key - length of 256 bits; it can also be used for generating MAC"; + "Indicates that the private key value is a CMS + OneAsymmetricKey structure, as defined in RFC 5958, + encoded using ASN.1 distinguished encoding rules + (DER), as specified in ITU-T X.690."; reference - "RFC 4309: - Using Advanced Encryption Standard (AES) CCM Mode with - IPsec Encapsulating Security Payload (ESP)"; + "RFC 5958: Asymmetric Key Packages + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER)."; } - identity aes-128-gcm { - base encryption-and-mac-algorithm; - description - "Encrypt message with AES algorithm in GCM mode with a key - length of 128 bits; it can also be used for generating MAC"; - reference - "RFC 4106: - The Use of Galois/Counter Mode (GCM) in IPsec Encapsulating - Security Payload (ESP)"; - } + /***************************************************/ + /* Identities for Public Key Format Structures */ + /***************************************************/ - identity aes-192-gcm { - base encryption-and-mac-algorithm; + identity ssh-public-key-format { + base public-key-format; description - "Encrypt message with AES algorithm in GCM mode with a key - length of 192 bits; it can also be used for generating MAC"; - reference - "RFC 4106: - The Use of Galois/Counter Mode (GCM) in IPsec Encapsulating - Security Payload (ESP)"; - } + "Indicates that the public key value is an SSH public key, + as specified by RFC 4253, Section 6.6, i.e.: - identity mac-aes-256-gcm { - base encryption-and-mac-algorithm; - description - "Encrypt message with AES algorithm in GCM mode with a key - length of 128 bits; it can also be used for generating MAC"; - reference - "RFC 4106: - The Use of Galois/Counter Mode (GCM) in IPsec Encapsulating - Security Payload (ESP)"; - } - identity chacha20-poly1305 { - base encryption-and-mac-algorithm; - description - "Encrypt message with chacha20 algorithm and generate MAC with - POLY1305; it can also be used for generating MAC"; - reference - "RFC 8439: ChaCha20 and Poly1305 for IETF Protocols"; - } - - /******************************************/ - /* Identities for signature algorithm */ - /******************************************/ - - identity signature-algorithm { - description - "A base identity for asymmetric key encryption algorithm."; - } - - identity dsa-sha1 { - base signature-algorithm; - description - "The signature algorithm using DSA algorithm with SHA1 hash - algorithm"; + string certificate or public key format + identifier + byte[n] key/certificate data."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } - identity rsassa-pkcs1-sha1 { - base signature-algorithm; + identity subject-public-key-info-format { + base public-key-format; description - "The signature algorithm using RSASSA-PKCS1-v1_5 with the SHA1 - hash algorithm."; + "Indicates that the public key value is a SubjectPublicKeyInfo + structure, as described in RFC 5280 encoded using ASN.1 + distinguished encoding rules (DER), as specified in + ITU-T X.690."; reference - "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; + "RFC 5280: + Internet X.509 Public Key Infrastructure Certificate + and Certificate Revocation List (CRL) Profile + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER)."; } - identity rsassa-pkcs1-sha256 { - base signature-algorithm; + /******************************************************/ + /* Identities for Symmetric Key Format Structures */ + /******************************************************/ + + identity octet-string-key-format { + base symmetric-key-format; description - "The signature algorithm using RSASSA-PKCS1-v1_5 with the - SHA256 hash algorithm."; + "Indicates that the key is encoded as a raw octet string. + The length of the octet string MUST be appropriate for + the associated algorithm's block size. + + How the associated algorithm is known is outside the + scope of this module. This statement also applies when + the octet string has been encrypted."; + } + + identity one-symmetric-key-format { + if-feature "one-symmetric-key-format"; + base symmetric-key-format; + description + "Indicates that the private key value is a CMS + OneSymmetricKey structure, as defined in RFC 6031, + encoded using ASN.1 distinguished encoding rules + (DER), as specified in ITU-T X.690."; reference - "RFC 8332: - Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell - (SSH) Protocol - RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; + "RFC 6031: Cryptographic Message Syntax (CMS) + Symmetric Key Package Content Type + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER)."; } - identity rsassa-pkcs1-sha384 { - base signature-algorithm; + + /*************************************************/ + /* Identities for Encrypted Value Structures */ + /*************************************************/ + + identity encrypted-value-format { description - "The signature algorithm using RSASSA-PKCS1-v1_5 with the - SHA384 hash algorithm."; + "Base format identity for encrypted values."; + } + + identity symmetrically-encrypted-value-format { + if-feature "symmetrically-encrypted-value-format"; + base encrypted-value-format; + description + "Base format identity for symmetrically encrypted + values."; + } + + identity asymmetrically-encrypted-value-format { + if-feature "asymmetrically-encrypted-value-format"; + base encrypted-value-format; + description + "Base format identity for asymmetrically encrypted + values."; + } + + identity cms-encrypted-data-format { + if-feature "cms-encrypted-data-format"; + base symmetrically-encrypted-value-format; + description + "Indicates that the encrypted value conforms to + the 'encrypted-data-cms' type with the constraint + that the 'unprotectedAttrs' value is not set."; reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; + "RFC 5652: Cryptographic Message Syntax (CMS) + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER)."; } - identity rsassa-pkcs1-sha512 { - base signature-algorithm; + identity cms-enveloped-data-format { + if-feature "cms-enveloped-data-format"; + base asymmetrically-encrypted-value-format; description - "The signature algorithm using RSASSA-PKCS1-v1_5 with the - SHA512 hash algorithm."; + "Indicates that the encrypted value conforms to the + 'enveloped-data-cms' type with the following constraints: + + The EnvelopedData structure MUST have exactly one + 'RecipientInfo'. + + If the asymmetric key supports public key cryptography + (e.g., RSA), then the 'RecipientInfo' must be a + 'KeyTransRecipientInfo' with the 'RecipientIdentifier' + using a 'subjectKeyIdentifier' with the value set using + 'method 1' in RFC 7093 over the recipient's public key. + + Otherwise, if the asymmetric key supports key agreement + (e.g., ECC), then the 'RecipientInfo' must be a + 'KeyAgreeRecipientInfo'. The 'OriginatorIdentifierOrKey' + value must use the 'OriginatorPublicKey' alternative. + The 'UserKeyingMaterial' value must not be present. + There must be exactly one 'RecipientEncryptedKeys' value + having the 'KeyAgreeRecipientIdentifier' set to 'rKeyId' + with the value set using 'method 1' in RFC 7093 over the + recipient's public key."; reference - "RFC 8332: - Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell - (SSH) Protocol - RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; + "RFC 5652: Cryptographic Message Syntax (CMS) + RFC 7093: + Additional Methods for Generating Key + Identifiers Values + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER)."; } - identity rsassa-pss-rsae-sha256 { - base signature-algorithm; + /*********************************************************/ + /* Identities for Certificate Signing Request Formats */ + /*********************************************************/ + + identity csr-format { description - "The signature algorithm using RSASSA-PSS with mask generation - function 1 and SHA256 hash algorithm. If the public key is - carried in an X.509 certificate, it MUST use the rsaEncryption - OID"; + "A base identity for the certificate signing request + formats. Additional derived identities MAY be defined + by future efforts."; + } + + identity p10-csr { + if-feature "p10-based-csrs"; + base csr-format; + description + "Indicates the 'CertificationRequest' structure + defined in RFC 2986."; reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; + "RFC 2986: PKCS #10: Certification Request Syntax + Specification Version 1.7"; } - identity rsassa-pss-rsae-sha384 { - base signature-algorithm; + /***************************************************/ + /* Typedefs for ASN.1 structures from RFC 2986 */ + /***************************************************/ + + typedef csr-info { + type binary; description - "The signature algorithm using RSASSA-PSS with mask generation - function 1 and SHA384 hash algorithm. If the public key is - carried in an X.509 certificate, it MUST use the rsaEncryption - OID"; + "A CertificationRequestInfo structure, as defined in + RFC 2986, encoded using ASN.1 distinguished encoding + rules (DER), as specified in ITU-T X.690."; reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; + "RFC 2986: PKCS #10: Certification Request Syntax + Specification Version 1.7 + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER)."; } - identity rsassa-pss-rsae-sha512 { - base signature-algorithm; + typedef p10-csr { + type binary; description - "The signature algorithm using RSASSA-PSS with mask generation - function 1 and SHA512 hash algorithm. If the public key is - carried in an X.509 certificate, it MUST use the rsaEncryption - OID"; + "A CertificationRequest structure, as specified in + RFC 2986, encoded using ASN.1 distinguished encoding + rules (DER), as specified in ITU-T X.690."; reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity rsassa-pss-pss-sha256 { - base signature-algorithm; - description - "The signature algorithm using RSASSA-PSS with mask generation - function 1 and SHA256 hash algorithm. If the public key is - carried in an X.509 certificate, it MUST use the RSASSA-PSS - OID"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity rsassa-pss-pss-sha384 { - base signature-algorithm; - description - "The signature algorithm using RSASSA-PSS with mask generation - function 1 and SHA256 hash algorithm. If the public key is - carried in an X.509 certificate, it MUST use the RSASSA-PSS - OID"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity rsassa-pss-pss-sha512 { - base signature-algorithm; - description - "The signature algorithm using RSASSA-PSS with mask generation - function 1 and SHA256 hash algorithm. If the public key is - carried in an X.509 certificate, it MUST use the RSASSA-PSS - OID"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity ecdsa-secp256r1-sha256 { - base signature-algorithm; - description - "The signature algorithm using ECDSA with curve name secp256r1 - and SHA256 hash algorithm."; - reference - "RFC 5656: Elliptic Curve Algorithm Integration in the - Secure Shell Transport Layer - RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity ecdsa-secp384r1-sha384 { - base signature-algorithm; - description - "The signature algorithm using ECDSA with curve name secp384r1 - and SHA384 hash algorithm."; - reference - "RFC 5656: Elliptic Curve Algorithm Integration in the - Secure Shell Transport Layer - RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity ecdsa-secp521r1-sha512 { - base signature-algorithm; - description - "The signature algorithm using ECDSA with curve name secp521r1 - and SHA512 hash algorithm."; - reference - "RFC 5656: Elliptic Curve Algorithm Integration in the - Secure Shell Transport Layer - RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity ed25519 { - base signature-algorithm; - description - "The signature algorithm using EdDSA as defined in RFC 8032 or - its successors."; - reference - "RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)"; - } - - identity ed448 { - base signature-algorithm; - description - "The signature algorithm using EdDSA as defined in RFC 8032 or - its successors."; - reference - "RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)"; - } - - identity eccsi { - base signature-algorithm; - description - "The signature algorithm using ECCSI signature as defined in - RFC 6507."; - reference - "RFC 6507: - Elliptic Curve-Based Certificateless Signatures for - Identity-based Encryption (ECCSI)"; - } - - /**********************************************/ - /* Identities for key exchange algorithms */ - /**********************************************/ - - identity key-exchange-algorithm { - description - "A base identity for Diffie-Hellman based key exchange - algorithm."; - } - - identity psk-only { - base key-exchange-algorithm; - description - "Using Pre-shared key for authentication and key exchange"; - reference - "RFC 4279: - Pre-Shared Key cipher suites for Transport Layer Security - (TLS)"; - } - - identity dhe-ffdhe2048 { - base key-exchange-algorithm; - description - "Ephemeral Diffie Hellman key exchange with 2048 bit - finite field"; - reference - "RFC 7919: - Negotiated Finite Field Diffie-Hellman Ephemeral Parameters - for Transport Layer Security (TLS)"; - } - - identity dhe-ffdhe3072 { - base key-exchange-algorithm; - description - "Ephemeral Diffie Hellman key exchange with 3072 bit finite - field"; - reference - "RFC 7919: - Negotiated Finite Field Diffie-Hellman Ephemeral Parameters - for Transport Layer Security (TLS)"; - } - - identity dhe-ffdhe4096 { - base key-exchange-algorithm; - description - "Ephemeral Diffie Hellman key exchange with 4096 bit - finite field"; - reference - "RFC 7919: - Negotiated Finite Field Diffie-Hellman Ephemeral Parameters - for Transport Layer Security (TLS)"; - } - - identity dhe-ffdhe6144 { - base key-exchange-algorithm; - description - "Ephemeral Diffie Hellman key exchange with 6144 bit - finite field"; - reference - "RFC 7919: - Negotiated Finite Field Diffie-Hellman Ephemeral Parameters - for Transport Layer Security (TLS)"; - } - - identity dhe-ffdhe8192 { - base key-exchange-algorithm; - description - "Ephemeral Diffie Hellman key exchange with 8192 bit - finite field"; - reference - "RFC 7919: - Negotiated Finite Field Diffie-Hellman Ephemeral Parameters - for Transport Layer Security (TLS)"; - } - - identity psk-dhe-ffdhe2048 { - base key-exchange-algorithm; - description - "Key exchange using pre-shared key with Diffie-Hellman key - generation mechanism, where the DH group is FFDHE2048"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity psk-dhe-ffdhe3072 { - base key-exchange-algorithm; - description - "Key exchange using pre-shared key with Diffie-Hellman key - generation mechanism, where the DH group is FFDHE3072"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity psk-dhe-ffdhe4096 { - base key-exchange-algorithm; - description - "Key exchange using pre-shared key with Diffie-Hellman key - generation mechanism, where the DH group is FFDHE4096"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity psk-dhe-ffdhe6144 { - base key-exchange-algorithm; - description - "Key exchange using pre-shared key with Diffie-Hellman key - generation mechanism, where the DH group is FFDHE6144"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity psk-dhe-ffdhe8192 { - base key-exchange-algorithm; - description - "Key exchange using pre-shared key with Diffie-Hellman key - generation mechanism, where the DH group is FFDHE8192"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity ecdhe-secp256r1 { - base key-exchange-algorithm; - description - "Ephemeral Diffie Hellman key exchange with elliptic group - over curve secp256r1"; - reference - "RFC 8422: - Elliptic Curve Cryptography (ECC) Cipher Suites for - Transport Layer Security (TLS) Versions 1.2 and Earlier"; - } - - identity ecdhe-secp384r1 { - base key-exchange-algorithm; - description - "Ephemeral Diffie Hellman key exchange with elliptic group - over curve secp384r1"; - reference - "RFC 8422: - Elliptic Curve Cryptography (ECC) Cipher Suites for - Transport Layer Security (TLS) Versions 1.2 and Earlier"; - } - - identity ecdhe-secp521r1 { - base key-exchange-algorithm; - description - "Ephemeral Diffie Hellman key exchange with elliptic group - over curve secp521r1"; - reference - "RFC 8422: - Elliptic Curve Cryptography (ECC) Cipher Suites for - Transport Layer Security (TLS) Versions 1.2 and Earlier"; - } - - identity ecdhe-x25519 { - base key-exchange-algorithm; - description - "Ephemeral Diffie Hellman key exchange with elliptic group - over curve x25519"; - reference - "RFC 8422: - Elliptic Curve Cryptography (ECC) Cipher Suites for - Transport Layer Security (TLS) Versions 1.2 and Earlier"; - } - - identity ecdhe-x448 { - base key-exchange-algorithm; - description - "Ephemeral Diffie Hellman key exchange with elliptic group - over curve x448"; - reference - "RFC 8422: - Elliptic Curve Cryptography (ECC) Cipher Suites for - Transport Layer Security (TLS) Versions 1.2 and Earlier"; - } - - identity psk-ecdhe-secp256r1 { - base key-exchange-algorithm; - description - "Key exchange using pre-shared key with elliptic group-based - Ephemeral Diffie Hellman key exchange over curve secp256r1"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity psk-ecdhe-secp384r1 { - base key-exchange-algorithm; - description - "Key exchange using pre-shared key with elliptic group-based - Ephemeral Diffie Hellman key exchange over curve secp384r1"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity psk-ecdhe-secp521r1 { - base key-exchange-algorithm; - description - "Key exchange using pre-shared key with elliptic group-based - Ephemeral Diffie Hellman key exchange over curve secp521r1"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity psk-ecdhe-x25519 { - base key-exchange-algorithm; - description - "Key exchange using pre-shared key with elliptic group-based - Ephemeral Diffie Hellman key exchange over curve x25519"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity psk-ecdhe-x448 { - base key-exchange-algorithm; - description - "Key exchange using pre-shared key with elliptic group-based - Ephemeral Diffie Hellman key exchange over curve x448"; - reference - "RFC 8446: - The Transport Layer Security (TLS) Protocol Version 1.3"; - } - - identity diffie-hellman-group14-sha1 { - base key-exchange-algorithm; - description - "Using DH group14 and SHA1 for key exchange"; - reference - "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; - } - - identity diffie-hellman-group14-sha256 { - base key-exchange-algorithm; - description - "Using DH group14 and SHA256 for key exchange"; - reference - "RFC 8268: - More Modular Exponentiation (MODP) Diffie-Hellman (DH) - Key Exchange (KEX) Groups for Secure Shell (SSH)"; - } - - identity diffie-hellman-group15-sha512 { - base key-exchange-algorithm; - description - "Using DH group15 and SHA512 for key exchange"; - reference - "RFC 8268: - More Modular Exponentiation (MODP) Diffie-Hellman (DH) - Key Exchange (KEX) Groups for Secure Shell (SSH)"; - } - - identity diffie-hellman-group16-sha512 { - base key-exchange-algorithm; - description - "Using DH group16 and SHA512 for key exchange"; - reference - "RFC 8268: - More Modular Exponentiation (MODP) Diffie-Hellman (DH) - Key Exchange (KEX) Groups for Secure Shell (SSH)"; - } - - identity diffie-hellman-group17-sha512 { - base key-exchange-algorithm; - description - "Using DH group17 and SHA512 for key exchange"; - - reference - "RFC 8268: - More Modular Exponentiation (MODP) Diffie-Hellman (DH) - Key Exchange (KEX) Groups for Secure Shell (SSH)"; - } - - identity diffie-hellman-group18-sha512 { - base key-exchange-algorithm; - description - "Using DH group18 and SHA512 for key exchange"; - reference - "RFC 8268: - More Modular Exponentiation (MODP) Diffie-Hellman (DH) - Key Exchange (KEX) Groups for Secure Shell (SSH)"; - } - - identity ecdh-sha2-secp256r1 { - base key-exchange-algorithm; - description - "Elliptic curve-based Diffie Hellman key exchange over curve - secp256r1 and using SHA2 for MAC generation"; - reference - "RFC 6239: Suite B Cryptographic Suites for Secure Shell - (SSH)"; - } - - identity ecdh-sha2-secp384r1 { - base key-exchange-algorithm; - description - "Elliptic curve-based Diffie Hellman key exchange over curve - secp384r1 and using SHA2 for MAC generation"; - reference - "RFC 6239: Suite B Cryptographic Suites for Secure Shell - (SSH)"; - } - - identity rsaes-oaep { - base key-exchange-algorithm; - description - "RSAES-OAEP combines the RSAEP and RSADP primitives with the - EME-OAEP encoding method"; - reference - "RFC 8017: - PKCS #1: RSA Cryptography Specifications Version 2.2."; - } - - identity rsaes-pkcs1-v1_5 { - base key-exchange-algorithm; - description - " RSAES-PKCS1-v1_5 combines the RSAEP and RSADP primitives - with the EME-PKCS1-v1_5 encoding method"; - reference - "RFC 8017: - PKCS #1: RSA Cryptography Specifications Version 2.2."; - } - - /**********************************************************/ - /* Typedefs for identityrefs to above base identities */ - /**********************************************************/ - - typedef hash-algorithm-ref { - type identityref { - base hash-algorithm; - } - description - "This typedef enables importing modules to easily define an - identityref to the 'hash-algorithm' base identity."; - } - - typedef signature-algorithm-ref { - type identityref { - base signature-algorithm; - } - description - "This typedef enables importing modules to easily define an - identityref to the 'signature-algorithm' base identity."; - } - - typedef mac-algorithm-ref { - type identityref { - base mac-algorithm; - } - description - "This typedef enables importing modules to easily define an - identityref to the 'mac-algorithm' base identity."; - } - - typedef encryption-algorithm-ref { - type identityref { - base encryption-algorithm; - } - description - "This typedef enables importing modules to easily define an - identityref to the 'encryption-algorithm' - base identity."; - } - - typedef encryption-and-mac-algorithm-ref { - type identityref { - base encryption-and-mac-algorithm; - } - description - "This typedef enables importing modules to easily define an - identityref to the 'encryption-and-mac-algorithm' - base identity."; - } - - typedef asymmetric-key-algorithm-ref { - type identityref { - base asymmetric-key-algorithm; - } - description - "This typedef enables importing modules to easily define an - identityref to the 'asymmetric-key-algorithm' - base identity."; - } - - typedef key-exchange-algorithm-ref { - type identityref { - base key-exchange-algorithm; - } - description - "This typedef enables importing modules to easily define an - identityref to the 'key-exchange-algorithm' base identity."; + "RFC 2986: + PKCS #10: Certification Request Syntax Specification + Version 1.7 + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER)."; } /***************************************************/ @@ -1149,6 +443,43 @@ module ietf-crypto-types { Encoding Rules (DER)."; } + /***************************************************/ + /* Typedefs for ASN.1 structures from RFC 6960 */ + /***************************************************/ + typedef oscp-request { + type binary; + description + "A OCSPRequest structure, as specified in RFC 6960, + encoded using ASN.1 distinguished encoding rules + (DER), as specified in ITU-T X.690."; + reference + "RFC 6960: + X.509 Internet Public Key Infrastructure Online + Certificate Status Protocol - OCSP + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER)."; + } + + typedef oscp-response { + type binary; + description + "A OCSPResponse structure, as specified in RFC 6960, + encoded using ASN.1 distinguished encoding rules + (DER), as specified in ITU-T X.690."; + reference + "RFC 6960: + X.509 Internet Public Key Infrastructure Online + Certificate Status Protocol - OCSP + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER)."; + } + /***********************************************/ /* Typedefs for ASN.1 structures from 5652 */ /***********************************************/ @@ -1228,24 +559,6 @@ module ietf-crypto-types { "RFC 5652: Cryptographic Message Syntax (CMS)"; } - /***************************************************/ - /* Typedefs for structures related to RFC 4253 */ - /***************************************************/ - - typedef ssh-host-key { - type binary; - description - "The binary public key data for this SSH key, as - specified by RFC 4253, Section 6.6, i.e.: - - string certificate or public key format - identifier - byte[n] key/certificate data."; - reference - "RFC 4253: The Secure Shell (SSH) Transport Layer - Protocol"; - } - /*********************************************************/ /* Typedefs for ASN.1 structures related to RFC 5280 */ /*********************************************************/ @@ -1325,376 +638,368 @@ module ietf-crypto-types { and Certificate Revocation List (CRL) Profile."; } - /**********************************************/ - /* Groupings for keys and/or certificates */ - /**********************************************/ + /*****************/ + /* Groupings */ + /*****************/ + + grouping encrypted-value-grouping { + description + "A reusable grouping for a value that has been encrypted by + a referenced symmetric or asymmetric key."; + container encrypted-by { + nacm:default-deny-write; + description + "An empty container enabling a reference to the key that + encrypted the value to be augmented in. The referenced + key MUST be a symmetric key or an asymmetric key. + + A symmetric key MUST be referenced via a leaf node called + 'symmetric-key-ref'. An asymmetric key MUST be referenced + via a leaf node called 'asymmetric-key-ref'. + + The leaf nodes MUST be direct descendants in the data tree, + and MAY be direct descendants in the schema tree."; + } + leaf encrypted-value-format { + type identityref { + base encrypted-value-format; + } + mandatory true; + description + "Identifies the format of the 'encrypted-value' leaf. + + If 'encrypted-by' points to a symmetric key, then a + 'symmetrically-encrypted-value-format' based identity + MUST by set (e.g., cms-encrypted-data-format). + + If 'encrypted-by' points to an asymmetric key, then an + 'asymmetrically-encrypted-value-format' based identity + MUST by set (e.g., cms-enveloped-data-format)."; + } + leaf encrypted-value { + nacm:default-deny-write; + type binary; + must '../encrypted-by'; + mandatory true; + description + "The value, encrypted using the referenced symmetric + or asymmetric key. The value MUST be encoded using + the format associated with the 'encrypted-value-format' + leaf."; + } + } + + grouping password-grouping { + description + "A password that MAY be encrypted."; + choice password-type { + nacm:default-deny-write; + mandatory true; + description + "Choice between password types."; + case cleartext-password { + leaf cleartext-password { + nacm:default-deny-all; + type string; + description + "The cleartext value of the password."; + } + } + case encrypted-password { + if-feature "password-encryption"; + container encrypted-password { + description + "A container for the encrypted password value."; + uses encrypted-value-grouping; + } + } + } + } + + grouping symmetric-key-grouping { + description + "A symmetric key."; + leaf key-format { + nacm:default-deny-write; + type identityref { + base symmetric-key-format; + } + description + "Identifies the symmetric key's format. Implementations + SHOULD ensure that the incoming symmetric key value is + encoded in the specified format. + + For encrypted keys, the value is the same as it would + have been if the key were not encrypted."; + } + choice key-type { + nacm:default-deny-write; + mandatory true; + description + "Choice between key types."; + case cleartext-key { + leaf cleartext-key { + nacm:default-deny-all; + type binary; + must '../key-format'; + description + "The binary value of the key. The interpretation of + the value is defined by the 'key-format' field."; + } + } + case hidden-key { + if-feature "hidden-keys"; + leaf hidden-key { + type empty; + must 'not(../key-format)'; + description + "A hidden key. How such keys are created is outside + the scope of this module."; + } + } + case encrypted-key { + if-feature "symmetric-key-encryption"; + container encrypted-key { + must '../key-format'; + description + "A container for the encrypted symmetric key value. + The interpretation of the 'encrypted-value' node + is via the 'key-format' node"; + uses encrypted-value-grouping; + } + } + } + } grouping public-key-grouping { description - "A public key. - - The 'algorithm' and 'public-key' nodes are not - mandatory because they MAY be defined in . - Implementations SHOULD assert that these values are - either configured or that they exist in ."; - leaf algorithm { + "A public key."; + leaf public-key-format { nacm:default-deny-write; - type asymmetric-key-algorithm-ref; - must '../public-key'; + type identityref { + base public-key-format; + } + mandatory true; description - "Identifies the key's algorithm. More specifically, - this leaf specifies how the 'public-key' binary leaf - is encoded."; - reference - "RFC CCCC: Common YANG Data Types for Cryptography"; + "Identifies the public key's format. Implementations SHOULD + ensure that the incoming public key value is encoded in the + specified format."; } leaf public-key { nacm:default-deny-write; type binary; - must '../algorithm'; + mandatory true; description - "A binary that contains the value of the public key. The - interpretation of the content is defined by the key - algorithm. For example, a DSA key is an integer, an RSA - key is represented as RSAPublicKey as defined in - RFC 8017, and an Elliptic Curve Cryptography (ECC) key - is represented using the 'publicKey' described in - RFC 5915."; - reference - "RFC 8017: Public-Key Cryptography Standards (PKCS) #1: - RSA Cryptography Specifications Version 2.2. - RFC 5915: Elliptic Curve Private Key Structure."; + "The binary value of the public key. The interpretation + of the value is defined by 'public-key-format' field."; } } grouping asymmetric-key-pair-grouping { description - "A private/public key pair. - The 'algorithm', 'public-key', and 'private-key' nodes are - not mandatory because they MAY be defined in . - Implementations SHOULD assert that these values are either - configured or that they exist in ."; + "A private key and its associated public key. Implementations + SHOULD ensure that the two keys are a matching pair."; uses public-key-grouping; - leaf private-key { - nacm:default-deny-all; - type union { - type binary; - type enumeration { - enum permanently-hidden { - description - "The private key is inaccessible due to being - protected by the system (e.g., a cryptographic - hardware module). - - How such keys are backed-up and restored, if - at all, is implementation specific. - - Servers MUST fail any attempt by a client to - configure this value directly. This value is - not set by clients, but rather is set by the - 'generate-hidden-key' and 'install-hidden-key' - actions."; - } - } + leaf private-key-format { + nacm:default-deny-write; + type identityref { + base private-key-format; } - must '../public-key'; description - "A binary that contains the value of the private key. The - interpretation of the content is defined by the key - algorithm. For example, a DSA key is an integer, an RSA - key is represented as RSAPrivateKey as defined in - RFC 8017, and an Elliptic Curve Cryptography (ECC) key - is represented as ECPrivateKey as defined in RFC 5915."; - reference - "RFC 8017: Public-Key Cryptography Standards (PKCS) #1: - RSA Cryptography Specifications Version 2.2. - RFC 5915: Elliptic Curve Private Key Structure."; - } // private-key + "Identifies the private key's format. Implementations SHOULD + ensure that the incoming private key value is encoded in the + specified format. - action generate-hidden-key { - nacm:default-deny-all; + For encrypted keys, the value is the same as it would have + been if the key were not encrypted."; + } + choice private-key-type { + nacm:default-deny-write; + mandatory true; description - "Requests the device to generate a hidden key using the - specified asymmetric key algorithm. This action is - used to request the system to generate a key that is - 'permanently-hidden', perhaps protected by a cryptographic - hardware module. The resulting asymmetric key values are - considered operational state and hence present only in - and bound to the lifetime of the parent - 'config true' node. Subsequent invocations of this or - the 'install-hidden-key' action are denied with error-tag - 'data-exists'."; - input { - leaf algorithm { - type asymmetric-key-algorithm-ref; - mandatory true; - description - "The algorithm to be used when generating the - asymmetric key."; - reference - "RFC CCCC: Common YANG Data Types for Cryptography"; - } - } - } // generate-hidden-key - - action install-hidden-key { - nacm:default-deny-all; - description - "Requests the device to load the specified values into - a hidden key. The resulting asymmetric key values are - considered operational state and hence present only in - and bound to the lifetime of the parent - 'config true' node. Subsequent invocations of this - or the 'generate-hidden-key' action are denied with - error-tag 'data-exists'."; - input { - leaf algorithm { - type asymmetric-key-algorithm-ref; - mandatory true; - description - "The algorithm to be used when generating the - asymmetric key."; - reference - "RFC CCCC: Common YANG Data Types for Cryptography"; - } - leaf public-key { + "Choice between key types."; + case cleartext-private-key { + leaf cleartext-private-key { + nacm:default-deny-all; type binary; + must '../private-key-format'; description - "A binary that contains the value of the public key. - The interpretation of the content is defined by the key - algorithm. For example, a DSA key is an integer, an - RSA key is represented as RSAPublicKey as defined in - RFC 8017, and an Elliptic Curve Cryptography (ECC) key - is represented using the 'publicKey' described in - RFC 5915."; - reference - "RFC 8017: Public-Key Cryptography Standards (PKCS) #1: - RSA Cryptography Specifications Version 2.2. - RFC 5915: Elliptic Curve Private Key Structure."; - } - leaf private-key { - type binary; - description - "A binary that contains the value of the private key. - The interpretation of the content is defined by the key - algorithm. For example, a DSA key is an integer, an RSA - key is represented as RSAPrivateKey as defined in - RFC 8017, and an Elliptic Curve Cryptography (ECC) key - is represented as ECPrivateKey as defined in RFC 5915."; - reference - "RFC 8017: Public-Key Cryptography Standards (PKCS) #1: - RSA Cryptography Specifications Version 2.2. - RFC 5915: Elliptic Curve Private Key Structure."; + "The value of the binary key The key's value is + interpreted by the 'private-key-format' field."; } } - } // install-hidden-key - } // asymmetric-key-pair-grouping + case hidden-private-key { + if-feature "hidden-keys"; + leaf hidden-private-key { + type empty; + must 'not(../private-key-format)'; + description + "A hidden key. How such keys are created is + outside the scope of this module."; + } + } + case encrypted-private-key { + if-feature "private-key-encryption"; + container encrypted-private-key { + must '../private-key-format'; + description + "A container for the encrypted asymmetric private key + value. The interpretation of the 'encrypted-value' + node is via the 'private-key-format' node"; + uses encrypted-value-grouping; + } + } + } + } + grouping certificate-expiration-grouping { + description + "A notification for when a certificate is about to, or + already has, expired."; + notification certificate-expiration { + if-feature "certificate-expiration-notification"; + description + "A notification indicating that the configured certificate + is either about to expire or has already expired. When to + send notifications is an implementation specific decision, + but it is RECOMMENDED that a notification be sent once a + month for 3 months, then once a week for four weeks, and + then once a day thereafter until the issue is resolved."; + leaf expiration-date { + type yang:date-and-time; + mandatory true; + description + "Identifies the expiration date on the certificate."; + } + } + } grouping trust-anchor-cert-grouping { description "A trust anchor certificate, and a notification for when it is about to (or already has) expire."; - leaf cert { + leaf cert-data { nacm:default-deny-write; type trust-anchor-cert-cms; description "The binary certificate data for this certificate."; - reference - "RFC YYYY: Common YANG Data Types for Cryptography"; - } - notification certificate-expiration { - description - "A notification indicating that the configured certificate - is either about to expire or has already expired. When to - send notifications is an implementation specific decision, - but it is RECOMMENDED that a notification be sent once a - month for 3 months, then once a week for four weeks, and - then once a day thereafter until the issue is resolved."; - leaf expiration-date { - type yang:date-and-time; - mandatory true; - description - "Identifies the expiration date on the certificate."; - } - } - } - - grouping trust-anchor-certs-grouping { - description - "A list of trust anchor certificates, and a notification - for when one is about to (or already has) expire."; - leaf-list cert { - nacm:default-deny-write; - type trust-anchor-cert-cms; - description - "The binary certificate data for this certificate."; - reference - "RFC YYYY: Common YANG Data Types for Cryptography"; - } - notification certificate-expiration { - description - "A notification indicating that the configured certificate - is either about to expire or has already expired. When to - send notifications is an implementation specific decision, - but it is RECOMMENDED that a notification be sent once a - month for 3 months, then once a week for four weeks, and - then once a day thereafter until the issue is resolved."; - leaf expiration-date { - type yang:date-and-time; - mandatory true; - description - "Identifies the expiration date on the certificate."; - } } + uses certificate-expiration-grouping; } grouping end-entity-cert-grouping { description "An end entity certificate, and a notification for when - it is about to (or already has) expire."; - leaf cert { + it is about to (or already has) expire. Implementations + SHOULD assert that, where used, the end entity certificate + contains the expected public key."; + leaf cert-data { nacm:default-deny-write; type end-entity-cert-cms; description "The binary certificate data for this certificate."; - reference - "RFC YYYY: Common YANG Data Types for Cryptography"; - } - notification certificate-expiration { - description - "A notification indicating that the configured certificate - is either about to expire or has already expired. When to - send notifications is an implementation specific decision, - but it is RECOMMENDED that a notification be sent once a - month for 3 months, then once a week for four weeks, and - then once a day thereafter until the issue is resolved."; - leaf expiration-date { - type yang:date-and-time; - mandatory true; - description - "Identifies the expiration date on the certificate."; - } } + uses certificate-expiration-grouping; } - grouping end-entity-certs-grouping { + grouping generate-csr-grouping { description - "A list of end entity certificates, and a notification for - when one is about to (or already has) expire."; - leaf-list cert { - nacm:default-deny-write; - type end-entity-cert-cms; - description - "The binary certificate data for this certificate."; - reference - "RFC YYYY: Common YANG Data Types for Cryptography"; - } - notification certificate-expiration { - description - "A notification indicating that the configured certificate - is either about to expire or has already expired. When to - send notifications is an implementation specific decision, - but it is RECOMMENDED that a notification be sent once a - month for 3 months, then once a week for four weeks, and - then once a day thereafter until the issue is resolved."; - leaf expiration-date { - type yang:date-and-time; - mandatory true; - description - "Identifies the expiration date on the certificate."; - } - } - } - - grouping asymmetric-key-pair-with-cert-grouping { - description - "A private/public key pair and an associated certificate."; - uses asymmetric-key-pair-grouping; - uses end-entity-cert-grouping; - - action generate-certificate-signing-request { + "Defines the 'generate-csr' action."; + action generate-csr { + if-feature "csr-generation"; nacm:default-deny-all; description "Generates a certificate signing request structure for the associated asymmetric key using the passed subject - and attribute values. The specified assertions need - to be appropriate for the certificate's use. For - example, an entity certificate for a TLS server - SHOULD have values that enable clients to satisfy - RFC 6125 processing."; + and attribute values. + + This action statement is only available when the + associated 'public-key-format' node's value is + 'subject-public-key-info-format'."; + reference + "RFC 6125: + Representation and Verification of Domain-Based + Application Service Identity within Internet Public Key + Infrastructure Using X.509 (PKIX) Certificates in the + Context of Transport Layer Security (TLS)"; input { - leaf subject { - type binary; + leaf csr-format { + type identityref { + base csr-format; + } mandatory true; description - "The 'subject' field per the CertificationRequestInfo - structure as specified by RFC 2986, Section 4.1 - encoded using the ASN.1 distinguished encoding - rules (DER), as specified in ITU-T X.690."; - reference - "RFC 2986: - PKCS #10: Certification Request Syntax - Specification Version 1.7. - ITU-T X.690: - Information technology - ASN.1 encoding rules: - Specification of Basic Encoding Rules (BER), - Canonical Encoding Rules (CER) and Distinguished - Encoding Rules (DER)."; + "Specifies the format for the returned certifiacte."; } - leaf attributes { - type binary; + leaf csr-info { + type csr-info; + mandatory true; description - "The 'attributes' field from the structure - CertificationRequestInfo as specified by RFC 2986, - Section 4.1 encoded using the ASN.1 distinguished - encoding rules (DER), as specified in ITU-T X.690."; + "A CertificationRequestInfo structure, as defined in + RFC 2986. + + Enables the client to provide a fully-populated + CertificationRequestInfo structure that the server + only needs to sign in order to generate the complete + 'CertificationRequest' structure to return in the + 'output'. + + The 'AlgorithmIdentifier' field contained inside + the 'SubjectPublicKeyInfo' field MUST be one known + to be supported by the device."; reference "RFC 2986: - PKCS #10: Certification Request Syntax - Specification Version 1.7. - ITU-T X.690: - Information technology - ASN.1 encoding rules: - Specification of Basic Encoding Rules (BER), - Canonical Encoding Rules (CER) and Distinguished - Encoding Rules (DER)."; + PKCS #10: Certification Request Syntax Specification + RFC AAAA: + YANG Data Types and Groupings for Cryptography"; } } output { - leaf certificate-signing-request { - type binary; + choice csr-type { mandatory true; description - "A CertificationRequest structure as specified by - RFC 2986, Section 4.2 encoded using the ASN.1 - distinguished encoding rules (DER), as specified - in ITU-T X.690."; - reference - "RFC 2986: - PKCS #10: Certification Request Syntax - Specification Version 1.7. - ITU-T X.690: - Information technology - ASN.1 encoding rules: - Specification of Basic Encoding Rules (BER), - Canonical Encoding Rules (CER) and Distinguished - Encoding Rules (DER)."; + "A choice amongst certificate signing request formats. + Additional formats MAY be augmented into this 'choice' + statement by future efforts."; + case p10-csr { + leaf p10-csr { + type p10-csr; + description + "A CertificationRequest, as defined in RFC 2986."; + } + description + "A CertificationRequest, as defined in RFC 2986."; + reference + "RFC 2986: + PKCS #10: Certification Request Syntax Specification + RFC AAAA: + YANG Data Types and Groupings for Cryptography"; + } } } - } // generate-certificate-signing-request - } // asymmetric-key-pair-with-cert-grouping + } + } // generate-csr-grouping + grouping asymmetric-key-pair-with-cert-grouping { + description + "A private/public key pair and an associated certificate. + Implementations SHOULD assert that certificates contain + the matching public key."; + uses asymmetric-key-pair-grouping; + uses end-entity-cert-grouping; + uses generate-csr-grouping; + } // asymmetric-key-pair-with-cert-grouping grouping asymmetric-key-pair-with-certs-grouping { description - "A private/public key pair and associated certificates."; + "A private/public key pair and associated certificates. + Implementations SHOULD assert that certificates contain + the matching public key."; uses asymmetric-key-pair-grouping; container certificates { nacm:default-deny-write; description - "Certificates associated with this asymmetric key. - More than one certificate supports, for instance, - a TPM-protected asymmetric key that has both IDevID - and LDevID certificates associated."; + "Certificates associated with this asymmetric key."; list certificate { key "name"; description @@ -1702,82 +1007,16 @@ module ietf-crypto-types { leaf name { type string; description - "An arbitrary name for the certificate. If the name - matches the name of a certificate that exists - independently in (i.e., an IDevID), - then the 'cert' node MUST NOT be configured."; + "An arbitrary name for the certificate."; } - uses end-entity-cert-grouping; - } - } // certificates - - action generate-certificate-signing-request { - nacm:default-deny-all; - description - "Generates a certificate signing request structure for - the associated asymmetric key using the passed subject - and attribute values. The specified assertions need - to be appropriate for the certificate's use. For - example, an entity certificate for a TLS server - SHOULD have values that enable clients to satisfy - RFC 6125 processing."; - input { - leaf subject { - type binary; - mandatory true; - description - "The 'subject' field per the CertificationRequestInfo - structure as specified by RFC 2986, Section 4.1 - encoded using the ASN.1 distinguished encoding - rules (DER), as specified in ITU-T X.690."; - reference - "RFC 2986: - PKCS #10: Certification Request Syntax - Specification Version 1.7. - ITU-T X.690: - Information technology - ASN.1 encoding rules: - Specification of Basic Encoding Rules (BER), - Canonical Encoding Rules (CER) and Distinguished - Encoding Rules (DER)."; - } - leaf attributes { - type binary; - description - "The 'attributes' field from the structure - CertificationRequestInfo as specified by RFC 2986, - Section 4.1 encoded using the ASN.1 distinguished - encoding rules (DER), as specified in ITU-T X.690."; - reference - "RFC 2986: - PKCS #10: Certification Request Syntax - Specification Version 1.7. - ITU-T X.690: - Information technology - ASN.1 encoding rules: - Specification of Basic Encoding Rules (BER), - Canonical Encoding Rules (CER) and Distinguished - Encoding Rules (DER)."; + uses end-entity-cert-grouping { + refine "cert-data" { + mandatory true; + } } } - output { - leaf certificate-signing-request { - type binary; - mandatory true; - description - "A CertificationRequest structure as specified by - RFC 2986, Section 4.2 encoded using the ASN.1 - distinguished encoding rules (DER), as specified - in ITU-T X.690."; - reference - "RFC 2986: - PKCS #10: Certification Request Syntax - Specification Version 1.7. - ITU-T X.690: - Information technology - ASN.1 encoding rules: - Specification of Basic Encoding Rules (BER), - Canonical Encoding Rules (CER) and Distinguished - Encoding Rules (DER)."; - } - } - } // generate-certificate-signing-request + } + uses generate-csr-grouping; } // asymmetric-key-pair-with-certs-grouping + } diff --git a/radio/fhi_72/mplane/yang/models/o-ran-common-yang-types.yang b/radio/fhi_72/mplane/yang/models/o-ran-common-yang-types.yang new file mode 100644 index 0000000000..4fa9d7f542 --- /dev/null +++ b/radio/fhi_72/mplane/yang/models/o-ran-common-yang-types.yang @@ -0,0 +1,449 @@ +module o-ran-common-yang-types { + yang-version 1.1; + namespace "urn:o-ran:common-yang-types:1.0"; + prefix "o-ran-types"; + + import ietf-inet-types { + prefix inet; + } + + organization "O-RAN Alliance"; + + + + contact + "www.o-ran.org"; + + description + "This module defines ORAN common YANG types. + + Copyright 2023 the O-RAN Alliance. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the above disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the above disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the Members of the O-RAN Alliance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission."; + + revision "2023-08-14" { + description + "version 2.0.0 + + 1) New availability status typedef"; + + reference "ORAN-WG1.IM.0-v01.00"; + } + + revision "2022-08-15" { + description + "version 1.1.0 + + 1) style guide corrections"; + + reference "ORAN-WG1.IM.0-v01.00"; + } + + revision "2020-10-10" { + description + "version 1.0.0 + + 1) Initial creation"; + + reference "ORAN-WG1.IM.0-v01.00"; + } + + typedef version { + type string { + pattern '[0-9]+(\.[0-9]+){1,2}'; + } + description + "Definition used to represent a version of an interface."; + } + + typedef timezone-name { + type string; + description + "A time zone name as used by the Time Zone Database, + sometimes referred to as the 'Olson Database'. + + The complete set of valid values is defined in + https://www.iana.org/time-zones. + + The exact set of supported values is an + implementation-specific matter."; + + reference + "RFC 6557: Procedures for Maintaining the Time Zone Database + IANA Time Zone Database https://www.iana.org/time-zones "; + } + + typedef protocol { + type enumeration { + enum ECPRI { + description + "Indicates that an O-RU supports the ecpri header format + for the C/U plane"; + } + enum IEEE-1914-3 { + description + "Indicates that an O-RU supports the 1914.3 header format + for the C/U plane"; + } + } + description "the type of C/U plane protocol"; + } + + typedef duplex-scheme { + type enumeration { + enum TDD { + description + "TDD scheme"; + } + enum FDD { + description + "FDD scheme"; + } + } + description + "Type of duplex scheme O-RU supports."; + } + + typedef active { + type enumeration { + enum INACTIVE { + description + "Carrier does not provide signal - transmission is disabled"; + } + enum SLEEP{ + description + "Carrier is fully configured and was active but is energy saving mode"; + } + enum ACTIVE{ + description + "Carrier is fully configured and properly providing the signal"; + } + } + description "the active status of a carrier"; + } + + typedef state { + type enumeration { + enum DISABLED { + description + "Array carrier is not active - transmission of signal is disabled."; + } + enum BUSY { + description + "Array carrier is processing an operation requested by change of active parameter. + When array carriers is BUSY the transmission of signal is not guaranteed."; + } + enum READY { + description + "Array carrier had completed activation operation - is active and transmission of signal is ongoing."; + } + } + description "the state on an array carrier"; + } + + typedef transport-session { + type enumeration { + enum ETHERNET { + description "VLAN based CUS Transport "; + } + enum UDP_IP { +// if-feature o-ran-int:UDPIP-BASED-CU-PLANE; How to handle enum value constrained by feature defined in o-ran-interface.yang? + description "UDP/IP based CUS Transport "; + } + enum ALIAS_MAC{ +// if-feature o-ran-int:ALIASMAC-BASED-CU-PLANE; How to handle enum value constrained by feature defined in o-ran-interface.yang? + description "Alias MAC address based CUS Transport "; + } + enum SHARED_CELL { +// if-feature o-ran-elements:SHARED_CELL; How to handle enum value constrained by feature defined in o-ran-processing-element.yang? + description "VLAN based CUS Transport used for Shared Cell scenario"; + } + } + description "the transport session type"; + } + + typedef netconf-client-id { + type union { + type inet:ip-address; + type inet:uri; + } + description "A NETCONF client identifier"; + } + + typedef ca-ra-server-id { + type union { + type inet:ip-address; + type inet:uri; + } + description "A CA/RA Server identifier"; + } + + typedef segw-id { + type union { + type inet:ip-address; + type inet:uri; + } + description "A SeGW identifier"; + } + + typedef pcp { + type uint8 { + range "0..7"; + } + description + "Priority Code Point. PCP is a 3-bit field that refers to the + class of service applied to a VLAN tagged frame. The + field specifies a priority value between 0 and 7, these values + can be used by quality of service (QoS) to prioritize + different classes of traffic."; + reference + "IEEE 802.1Q-2014: Virtual Bridged Local Area Networks"; + } + + typedef vlan-id { + type uint16 { + range "1..4094"; + } + description + "The VLAN-ID that uniquely identifies a VLAN. This is the 12-bit VLAN-ID + used in the VLAN Tag header."; + reference "[802.1q] 9.6"; + } + + typedef geographic-coordinate-degree { + type decimal64 { + fraction-digits 8; + } + description + "Decimal degree (DD) used to express latitude and longitude + geographic coordinates."; + } + + typedef prach-preamble-format { + type enumeration { + enum LTE-0 { + description + "LTE PRACH Preamble format 0"; + } + enum LTE-1 { + description + "LTE PRACH Preamble format 1"; + } + enum LTE-2 { + description + "LTE PRACH Preamble format 2"; + } + enum LTE-3 { + description + "LTE PRACH Preamble format 3"; + } + enum LTE-4 { + description + "LTE PRACH Preamble format 4"; + } + enum LTE-NB0 { + description + "LTE Narrowband PRACH format 0"; + } + enum LTE-NB1 { + description + "LTE Narrowband PRACH format 1"; + } + enum NR-0 { + description + "5GNR PRACH Preamble format 0"; + } + enum NR-1 { + description + "5GNR PRACH Preamble format 1"; + } + enum NR-2 { + description + "5GNR PRACH Preamble format 2"; + } + enum NR-3 { + description + "5GNR PRACH Preamble format 3"; + } + enum NR-A1 { + description + "5GNR PRACH Preamble format A1"; + } + enum NR-A2 { + description + "5GNR PRACH Preamble format A2"; + } + enum NR-A3 { + description + "5GNR PRACH Preamble format A3"; + } + enum NR-B1 { + description + "5GNR PRACH Preamble format B1"; + } + enum NR-B2 { + description + "5GNR PRACH Preamble format B2"; + } + enum NR-B3 { + description + "5GNR PRACH Preamble format B3"; + } + enum NR-B4 { + description + "5GNR PRACH Preamble format B4"; + } + enum NR-C0 { + description + "5GNR PRACH Preamble format C0"; + } + enum NR-C2 { + description + "5GNR PRACH Preamble format C2"; + } + enum LTE-NB0-a { + description + "LTE Narrowband PRACH format 0-a"; + } + enum LTE-NB1-a { + description + "LTE Narrowband PRACH format 1-a"; + } + enum LTE-NB2 { + description + "LTE Narrowband PRACH format 2"; + } + } + + description + "PRACH preamble format definition"; + } + + typedef polarisation_type { + type enumeration { + enum MINUS_45 { + description "MINUS_45"; + } + enum ZERO { + description "ZERO"; + } + enum PLUS_45 { + description "PLUS_45"; + } + enum PLUS_90 { + description "PLUS_90"; + } + } + description "Type definition for polarisations"; + } + + typedef bandwidth { + type uint32 { + range "200 | 1400 | 3000 | 5000 | 10000 | 15000 | 20000 | 25000 | + 30000 | 40000 | 50000 | 60000 | 70000 | 80000 | 90000 | 100000 + | 200000 | 400000" ; + } + units kilohertz; + description + "transmission bandwidth configuration in units of kHz - + covering NBIoT through to New Radio - see 38.104"; + } + + typedef scs-config-type { + type enumeration { + enum KHZ_15 { + value 0; + description + "15kHz sub carrier spacing"; + } + enum KHZ_30 { + value 1; + description + "30kHz sub carrier spacing"; + } + enum KHZ_60 { + value 2; + description + "60kHz sub carrier spacing"; + } + enum KHZ_120 { + value 3; + description + "120kHz sub carrier spacing"; + } + enum KHZ_240 { + value 4; + description + "240kHz sub carrier spacing"; + } + enum KHZ_1_25 { + value 12; + description + "1,25kHz sub carrier spacing"; + } + enum KHZ_3_75 { + value 13; + description + "3.75kHz sub carrier spacing"; + } + enum KHZ_5 { + value 14; + description + "5kHz sub carrier spacing"; + } + enum KHZ_7_5 { + value 15; + description + "7.5kHz sub carrier spacing"; + } + } + + description + "Scs configuration type definition"; + } + + typedef availability-status { + type union { + type empty; + type enumeration { + enum FAILED { + description + "The resource has an internal fault that prevents it from operating. + The operational state is disabled."; + } + enum DEPENDENCY { + description + "The resource cannot operate because some other resource on which it + depends is unavailable. The operational state is disabled."; + } + } + } + description + "When the value of this attribute is empty set, this implies that none of + the status conditions described in the enumerations are present. Only current relevant status for M-plane defined are listed here"; + reference "CCITT Rec. X.731 (1992 E) 8.1.2.3"; + } + +} diff --git a/radio/fhi_72/mplane/yang/models/o-ran-compression-factors.yang b/radio/fhi_72/mplane/yang/models/o-ran-compression-factors.yang index 5816b5ed63..ffccbcd126 100644 --- a/radio/fhi_72/mplane/yang/models/o-ran-compression-factors.yang +++ b/radio/fhi_72/mplane/yang/models/o-ran-compression-factors.yang @@ -13,7 +13,7 @@ module o-ran-compression-factors { "This module defines the module capabilities for the O-RAN Radio Unit U-Plane configuration. - Copyright 2020 the O-RAN Alliance. + Copyright 2024 the O-RAN Alliance. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -39,6 +39,46 @@ module o-ran-compression-factors { contributors may be used to endorse or promote products derived from this software without specific prior written permission."; + revision "2024-08-12" { + description + "version 16.0.0 + + 1) Clarify handling of deprecated leaf compression-format. + 2) Add in SINR compression details"; + + reference "ORAN-WG4.M.0-v16.00"; + } + + revision "2023-08-14" { + description + "version 13.0.0 + + 1) New compression methods BFP-SRES-MASK-IN-SEC-HEADER and MOD-SRES-MASK-IN-SEC-HEADER."; + + reference "ORAN-WG4.M.0-v13.00"; + } + + revision "2021-12-01" { + description + "version 8.0.0 + + 1) typographical corrections in descriptions. + 2) Configuration for Beamforming weights were added together with changes + and updates to compressions. + 3) add new beamspace compression enumeration for BEAMSPACE_TYPEII."; + + reference "ORAN-WG4.M.0-v08.00"; + } + + revision "2021-03-22" { + description + "version 4.1.0 + + 1) typographical corrections in descriptions."; + + reference "ORAN-WG4.M.0-v04.00"; + } + revision "2020-08-10" { description "version 4.0.0 @@ -62,7 +102,7 @@ module o-ran-compression-factors { description "version 1.1.0 - 1) changes related to compression bitwidth presentation"; + 1) changes related to compression bit-width presentation"; reference "ORAN-WG4.M.0-v01.00"; } @@ -82,6 +122,171 @@ module o-ran-compression-factors { "Presence of this feature means that O-RU supports configurable fs-offset for compression."; } + typedef ci-compression-method-def { + type enumeration { + enum NO_COMPRESSION { + description + "No compression will be used"; + } + + enum BLOCK_FLOATING_POINT { + description + "Block floating point compression and decompression will be used"; + } + + enum BLOCK_SCALING { + description + "Block scaling compression and decompresion will be used"; + } + + enum U_LAW { + description + "u-Law compression and decompresion method will be used"; + } + } + description + "Available compression methods for channel information (e.g., ST6)."; + } + + typedef compression-type-def { + type enumeration { + enum STATIC { + description + "Indicates that static compression method will be used (both compression and IQ bitwidth)"; + } + enum DYNAMIC { + description + "Indicates that dynamic compression method will be used"; + } + } + description + "Compression type that O-DU wants to be supported"; + } + + typedef bf-compression-method-def { + type enumeration { + enum NO_COMPRESSION { + description + "No compression will be used"; + } + enum BLOCK_FLOATING_POINT { + description + "Block floating point compression and decompression will be used"; + } + + enum BLOCK_SCALING { + description + "Block scaling compression and decompresion will be used"; + } + + enum U_LAW { + description + "u-Law compression and decompresion method will be used"; + } + + enum BEAMSPACE { + description + "Beamspace compression and decompression will be used"; + } + + enum VOID_1 { + description + "Leftover of modulation, not to be used for beamforming weights"; + } + + enum VOID_2 { + description + "Leftover of block-floating-point-selective-re-sending, not to be used for beamforming weights"; + } + + enum VOID_3 { + description + "Leftover of modulation-compression-selective-re-sending, not to be used for beamforming weights"; + } + + enum BEAMSPACE_TYPEII { + description + "Beamspace compression typeII and decompression will be used "; + } + } + description + "Available compression methods for beamforming weights."; + } + + typedef compression-method-def { + type enumeration { + enum NO_COMPRESSION { + description + "No compression"; + } + enum BLOCK_FLOATING_POINT { + description + "Block floating point compression and decompression method"; + } + + enum BLOCK_SCALING { + description + "Block scaling compression and decompresion method"; + } + + enum U_LAW { + description + "u-Law compression and decompresion method"; + } + + enum VOID_1 { + description + "Leftover of beamspace, not to be used for IQ data."; + } + + enum MODULATION { + description + "Modulation compression and decompression method"; + } + + enum BLOCK-FLOATING-POINT-SELECTIVE-RE-SENDING { + description + "block floating point with selective re sending + compression and decompression method"; + } + + enum MODULATION-COMPRESSION-SELECTIVE-RE-SENDING { + description + "modulation compression with selective re sending + compression and decompression method"; + } + enum BFP-SRES-MASK-IN-SEC-HEADER { + description + "block floating point with selective re sending + compression and decompression method with selective re sending mask in the section header "; + } + + enum MOD-SRES-MASK-IN-SEC-HEADER { + description + "modulation compression with selective re sending + compression and decompression method with selective re sending mask in the section header"; + } + } + description + "Available compression methods for the data."; + } + + typedef sinr-compression-method-def { + type enumeration { + enum BLOCK_FLOATING_POINT { + description + "Block floating point compression and decompression will be used"; + } + + enum NO_COMPRESSION { + description + "No compression will be used"; + } + } + description + "Available compression methods for the SINR."; + } + grouping compression-method-grouping { description "Grouping for compression method."; @@ -89,68 +294,148 @@ module o-ran-compression-factors { leaf iq-bitwidth { type uint8; description - "Bitwidth to be used in compression"; + "Bit-width to be used in compression"; } leaf compression-method { - type enumeration { - enum NO_COMPRESSION { - description - "No compression will be used"; - } - enum BLOCK_FLOATING_POINT { - description - "Block floating point compression and decompression will be used"; - } - - enum BLOCK_SCALING { - description - "Block scaling compression and decompresion will be used"; - } - - enum U_LAW { - description - "u-Law compression and decompresion method will be used"; - } - - enum BEAMSPACE { - description - "Beamspace compression and decompression will be used"; - } - - enum MODULATION { - description - "Modulation compression and decompression will be used"; - } - enum BLOCK-FLOATING-POINT-SELECTIVE-RE-SENDING { - description - "block floating point with selective re sending - compression and decompression will be used"; - } - enum MODULATION-COMPRESSION-SELECTIVE-RE-SENDING { - description - "modulation compression with selective re sending - compression and decompression will be used"; - } - } + type compression-method-def; description - "Compresion method which can be supported by the O-RU"; + "Compression method which can be supported by the O-RU"; } } + grouping compression-details { + description ""; + + leaf iq-bitwidth { + type uint8; + description + "Bitwidth to be used in compression"; + } + + leaf compression-type { + type compression-type-def; + mandatory true; + description + "Compression type that O-DU wants to be supported"; + } + + leaf bitwidth { + when "../compression-type = 'STATIC'"; + type uint8; + status deprecated; + description + "Bitwidth to be used in compression. + This has since been replaced in M-Plane version + 2.0.0 with the iq-bitwidth schema node"; + } + + leaf compression-method { + type compression-method-def; + description + "Compression method which can be supported by the O-RU"; + } + + uses compresion-format-grp { + status deprecated; + } + } + + grouping bf-compression-details { + description "Compression formats defined for beamforming"; + + leaf iq-bitwidth { + type uint8; + description + "Bitwidth to be used in compression"; + } + + leaf compression-type { + type compression-type-def; + mandatory true; + description + "Compression type that O-DU wants to be supported"; + } + + leaf bitwidth { + when "../compression-type = 'STATIC'"; + type uint8; + status deprecated; + description + "Bitwidth to be used in compression. + This has since been replaced in M-Plane version + 2.0.0 with the iq-bitwidth schema node"; + } + + leaf compression-method { + type bf-compression-method-def; + description + "Compression method which can be supported by the beamforming"; + } + + uses compresion-format-grp { + status deprecated; + } + } + + grouping sinr-compression-details { + description "Compression formats defined for SINR"; + + leaf sinr-bitwidth { + type uint8; + description + "Bitwidth to be used in compression"; + } + + leaf sinr-compression-method { + type sinr-compression-method-def; + description + "Compression method which can be supported by the SINR."; + } + + leaf sinr-block-size { + type uint8; + description + "Block size used for reporting SINR. + If the parameter is absent, then the SINR values are sent without zero-padding + i.e., the block size is equal to the number of SINR values + + Please refer to CUS specification clause 7.2.10 for more details."; + } + } + +// *********** Deprecated *********** + grouping compression-formats { + status deprecated; description - "Grouping deicated to list compression formats as choice"; + "Grouping deicated to list compression formats as choice."; + + uses compresion-format-grp { + status deprecated; + } + } + + grouping compresion-format-grp { + status deprecated; + description + "Grouping to for compression format choice"; choice compression-format { + status deprecated; description - "Choice of compression format for particular element"; + "Choice of compression format for particular element. + Note: This method is deprecated. All should be done as emumeration as details are configured + by udCompHdr in CU-Plane messaging. + NOTE: Changing the configuration from any compression format to no compression would require + a delete-config followed by an edit-config"; case no-compresison { description "Compression for beam weights is not supported."; } + case block-floating-point { - description "Block floating point compression and decompression is supported."; + description "Block floating-point compression and decompression is supported."; leaf exponent { type uint8 { @@ -162,7 +447,7 @@ module o-ran-compression-factors { case block-floating-point-selective-re-sending { description - "Block floating point with selective re sending compression and decompression is supported."; + "Block floating-point with selective re sending compression and decompression is supported."; leaf sres-exponent { type uint8 { @@ -173,7 +458,7 @@ module o-ran-compression-factors { } case block-scaling { - description "Block scaling compression and decompresion is supported."; + description "Block scaling compression and decompression is supported."; leaf block-scalar { type uint8; description @@ -182,7 +467,7 @@ module o-ran-compression-factors { } case u-law { - description "u-Law compression and decompresion method is supported."; + description "u-Law compression and decompression method is supported."; leaf comp-bit-width { type uint8 { range "0..15"; @@ -218,7 +503,7 @@ module o-ran-compression-factors { type uint8 { range "0..1"; } - description "Constallation shift flag"; + description "Constellation shift flag"; } leaf mod-comp-scaler { @@ -235,7 +520,7 @@ module o-ran-compression-factors { type uint8 { range "0..1"; } - description "Constallation shift flag"; + description "Constellation shift flag"; } leaf sres-mod-comp-scaler { @@ -250,54 +535,50 @@ module o-ran-compression-factors { } grouping compression-params { + status deprecated; description "Parameters to define compression"; leaf compression-type { - type enumeration { - enum STATIC { - description - "Indicates that static compression method will be used (both compression and IQ bitwidth)"; - } - enum DYNAMIC { - description - "Indicates that dynamic compression method will be used"; - } - } + type compression-type-def; mandatory true; description "Compression type that O-DU wants to be supported"; } -// *********** TO BE REMOVED *********** leaf bitwidth { when "../compression-type = 'STATIC'"; type uint8; status deprecated; description - "Bitwidth to be used in compression. + "Bit-width to be used in compression. This has since been replaced in M-Plane version 2.0.0 with the iq-bitwidth schema node"; } -// ************************************* - uses compression-formats; + uses compression-formats { + status deprecated; + } } grouping compression-parameters { + status deprecated; description "Parameters used to define description type"; leaf iq-bitwidth { type uint8; description - "Bitwidth to be used in compression"; + "Bit-width to be used in compression"; } - uses compression-formats; + uses compression-formats { + status deprecated; + } } grouping format-of-iq-sample { + status deprecated; description "Indicates module capabilities about IQ samples"; @@ -312,17 +593,20 @@ module o-ran-compression-factors { type boolean; description - "Informs if O-RU supports realtime variable bit with"; + "Informs if O-RU supports real-time variable bit with"; } list compression-method-supported { - uses compression-parameters; + status deprecated; + uses compression-parameters { + status deprecated; + } description "List of supported compression methods by O-RU Note: if O-RU supports different compression methods per endpoint then please refer do endpoints to have information what - exactly is supported on a paticular endpoint"; + exactly is supported on a particular endpoint"; } leaf syminc-supported { @@ -353,16 +637,6 @@ module o-ran-compression-factors { } } +// *********** Deprecated End *********** - grouping compression-details { - description ""; - - leaf iq-bitwidth { - type uint8; - description - "Bitwidth to be used in compression"; - } - - uses compression-params; - } } diff --git a/radio/fhi_72/mplane/yang/models/o-ran-delay-management.yang b/radio/fhi_72/mplane/yang/models/o-ran-delay-management.yang new file mode 100644 index 0000000000..339398c2e1 --- /dev/null +++ b/radio/fhi_72/mplane/yang/models/o-ran-delay-management.yang @@ -0,0 +1,591 @@ +module o-ran-delay-management { + yang-version 1.1; + namespace "urn:o-ran:delay:1.0"; + prefix "o-ran-delay"; + + import o-ran-wg4-features { + prefix "feat"; + } + + organization "O-RAN Alliance"; + + contact + "www.o-ran.org"; + + description + "This module covers off aspects of O-DU to O-RU delay management, + including configuration data related to O-RU transmission and reception + windows. + + Copyright 2024 the O-RAN Alliance. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the above disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the above disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the Members of the O-RAN Alliance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission."; + + revision "2024-08-12" { + description + "version 16.0.0 + + 1) Removing the range to align bandwidth type to same bandwidth type as + in uplane-config module and module-cap modules. + 2) Add in Ta3-2g data processing delay"; + + reference "ORAN-WG4.M.0-v16.00"; + } + + revision "2024-04-15" { + description + "version 15.0.0 + + 1) add support for for beamforming list and delay profiles(s) per endpoint."; + + reference "ORAN-WG4.M.0-v15.00"; + } + + revision "2022-08-15" { + description + "version 10.0.0 + + 1) introducing new feature for ACK NACK feedback."; + + reference "ORAN-WG4.M.0-v10.00"; + } + + revision "2021-12-01" { + description + "version 8.0.0 + + 1) typographical corrections in descriptions. + 2) add new schema node beam-context-gap-period."; + + reference "ORAN-WG4.M.0-v08.00"; + } + + revision "2020-08-10" { + description + "version 4.0.0 + + 1) introduction of new t1a-max-cp-dl leaf to enable decoupled timing between C- and U-Plane"; + + reference "ORAN-WG4.M.0-v04.00"; + } + + revision "2019-07-03" { + description + "version 1.1.0 + + 1) fixing descriptions of ta3-min and ta3-max. + 2) introducing grouping/uses to enable model re-use by WG5"; + + reference "ORAN-WG4.M.0-v01.00"; + } + + revision "2019-02-04" { + description + "version 1.0.0 + + 1) imported model from xRAN + 2) changed namespace and reference from xran to o-ran"; + + reference "ORAN-WG4.M.0-v01.00"; + } + + + feature ADAPTIVE-RU-PROFILE { + description + "This feature indicates that the O-RU supports adaptive O-RU delay profile + based on information provided by the NETCONF client."; + } + + typedef bandwidth { + type uint32; + units kilohertz; + description + "transmission bandwidth configuration in units of kHz - + covering NB-IoT through to New Radio. + Value shall correspond to a value defined in 38.104."; + } + + grouping bandwidth-configuration { + description + "Grouping for bandwidth and SCS configuration"; + + leaf bandwidth { + type bandwidth; + description + "transmission bandwidth configuration in units of kHz - + covering NBIoT through to New Radio - see 38.104"; + } + leaf subcarrier-spacing { + type uint32 { + range "0 .. 240000 "; + } + units Hertz; + description "sub-carrier spacing in Hz"; + } + } + + grouping t2a-up { + description + "configuration of t2a for uplink"; + + leaf t2a-min-up { + type uint32; + units nanoseconds; + mandatory true; + description + "the minimum O-RU data processing delay between receiving IQ data + message over the fronthaul interface and transmitting + the corresponding first IQ sample at the antenna"; + } + leaf t2a-max-up { + type uint32; + units nanoseconds; + mandatory true; + description + "the earliest allowable time when a data packet is received before + the corresponding first IQ sample is transmitted at the antenna"; + } + } + + grouping t2a-cp-dl { + description + "Grouping for t2a CP for downlink"; + + leaf t2a-min-cp-dl { + type uint32; + units nanoseconds; + mandatory true; + description + "the minimum O-RU data processing delay between receiving downlink + real time control plane message over the fronthaul interface and + transmitting the corresponding first IQ sample at the antenna"; + } + leaf t2a-max-cp-dl { + type uint32; + units nanoseconds; + mandatory true; + description + "the earliest allowable time when a downlink real time control message + is received before the corresponding first IQ sample is transmitted at + the antenna"; + } + } + + grouping ta3 { + description + "Grouping for ta3 configuration"; + + leaf ta3-min { + type uint32; + units nanoseconds; + mandatory true; + description + "the minimum O-RU data processing delay between receiving an IQ sample + at the antenna and transmitting the first data sample over the + fronthaul interface"; + } + leaf ta3-max { + type uint32; + units nanoseconds; + mandatory true; + description + "the maximum O-RU data processing delay between receiving an IQ sample + at the antenna and transmitting the last data sample over the + fronthaul interface"; + } + } + + grouping t2a-cp-ul { + description + "Grouping for t2a CP uplink"; + + leaf t2a-min-cp-ul { + type uint32; + units nanoseconds; + mandatory true; + description + "the minimum O-RU data processing delay between receiving real time + up-link control plane message over the fronthaul interface and + receiving the first IQ sample at the antenna"; + } + leaf t2a-max-cp-ul { + type uint32; + units nanoseconds; + mandatory true; + description + "the earliest allowable time when a real time up-link control message + is received before the corresponding first IQ sample is received at + the antenna"; + } + } + + grouping ta3-ack { + description + "Grouping for ta3-ack configuration"; + leaf ta3-min-ack { + type int32; + units nanoseconds; + description + "the minimum delay between the DL/UL air reference point (tDL=0 or tUL=0) of symbol M + and the time O-RU sends section type 8 (ACK/NACK feedback) to O-DU. + This value can be negative, which indicates it is in advance of the air reference point. + This leaf only exists if section extension 22 (ACK/NACK request) and section type 8 (ACK/NACK feedback) + are supported by at least one endpoint."; + } + + leaf ta3-max-ack { + type int32; + units nanoseconds; + description + "the maximum delay between the DL/UL air reference point (tDL=0 or tUL=0) of symbol M + and the time O-RU sends section type 8 (ACK/NACK feedback) to O-DU. + This value can be negative, which indicates it is in advance of the air reference point. + This leaf only exists if section extension 22 (ACK/NACK request) and section type 8 (ACK/NACK feedback) + are supported by at least one endpoint."; + } + } + + grouping ta3-2g { + description + "Grouping for ta3-2g configuration"; + + leaf ta3-min-2g { + if-feature feat:RRM-MEAS-REPORTING; + type uint32; + units nanoseconds; + description + "The minimum O-RU data processing delay between receiving an IQ sample + at the antenna and transmitting a C-Plane message carrying RRM measurements + over the fronthaul interface. + + Please refer to CUS specification clause 4.4.3 for more details."; + } + leaf ta3-max-2g { + if-feature feat:RRM-MEAS-REPORTING; + type uint32; + units nanoseconds; + description + "The maximum O-RU data processing delay between receiving an IQ sample + at the antenna and transmitting a C-Plane message carrying RRM measurements + over the fronthaul interface. + + Please refer to CUS specification clause 4.4.3 for more details."; + } + } + + grouping ru-delay-profile { + description + "Grouping for RU delay profile"; + + uses t2a-up; + uses t2a-cp-dl; + + leaf tcp-adv-dl { + type uint32; + units nanoseconds; + mandatory true; + description + "the time difference (advance) between the reception window for + downlink real time Control messages and reception window for the + corresponding IQ data messages."; + } + + uses ta3; + uses t2a-cp-ul; + uses ta3-ack; + uses ta3-2g; + } + + grouping o-du-delay-profile { + description + "Grouping for O-DU delay profile"; + + leaf t1a-max-up { + type uint32; + units nanoseconds; + description + "the earliest possible time which the O-DU can support transmitting + an IQ data message prior to transmission of the corresponding IQ + samples at the antenna"; + } + leaf tx-max { + type uint32; + units nanoseconds; + description + "The maximum amount of time which the O-DU requires to transmit + all downlink user plane IQ data message for a symbol"; + } + leaf ta4-max { + type uint32; + units nanoseconds; + description + "the latest possible time which the O-DU can support receiving the + last uplink user plane IQ data message for a symbol."; + } + leaf rx-max { + type uint32; + units nanoseconds; + description + "The maximum time difference the O-DU can support between + receiving the first user plane IQ data message for a symbol and + receiving the last user plane IQ data message for the same symbol"; + } + leaf t1a-max-cp-dl { + type uint32; + units nanoseconds; + description + "The earliest possible time which the O-DU can support transmitting the + downlink real time control message prior to transmission of the + corresponding IQ samples at the antenna."; + } + } + + grouping t12 { + description + "Grouping for t12"; + + leaf t12-min { + type uint32; + units nanoseconds; + description + "the minimum measured delay between DU port-ID and O-RU port-ID"; + } +// additional leaf added by Samsung + leaf t12-max { + type uint32; + units nanoseconds; + description + "the maximum measured delay between CU port-ID and O-RU port-ID"; + } + } + + grouping t34 { + description + "Grouping for t34"; + + leaf t34-min { + type uint32; + units nanoseconds; + description + "the minimum measured delay between O-RU port-ID and CU port-ID"; + } +// additional leaf added by Samsung + leaf t34-max { + type uint32; + units nanoseconds; + description + "the maximum measured delay between O-RU port-ID and CU port-ID"; + } + } + + grouping non-default-ru-delay-profile { + description + "Grouping for O-RU delay profile different from the default value in ru-delay-profile."; + leaf t2a-min-up { + type uint32; + units nanoseconds; + description + "the minimum O-RU data processing delay between receiving IQ data + message over the fronthaul interface and transmitting + the corresponding first IQ sample at the antenna"; + } + leaf t2a-max-up { + type uint32; + units nanoseconds; + description + "the earliest allowable time when a data packet is received before + the corresponding first IQ sample is transmitted at the antenna"; + } + leaf t2a-min-cp-dl { + type uint32; + units nanoseconds; + description + "the minimum O-RU data processing delay between receiving downlink + real time control plane message over the fronthaul interface and + transmitting the corresponding first IQ sample at the antenna"; + } + leaf t2a-max-cp-dl { + type uint32; + units nanoseconds; + description + "the earliest allowable time when a downlink real time control message + is received before the corresponding first IQ sample is transmitted at + the antenna"; + } + leaf tcp-adv-dl { + type uint32; + units nanoseconds; + description + "the time difference (advance) between the reception window for + downlink real time Control messages and reception window for the + corresponding IQ data messages."; + } + leaf ta3-min { + type uint32; + units nanoseconds; + description + "the minimum O-RU data processing delay between receiving an IQ sample + at the antenna and transmitting the first data sample over the + fronthaul interface"; + } + leaf ta3-max { + type uint32; + units nanoseconds; + description + "the maximum O-RU data processing delay between receiving an IQ sample + at the antenna and transmitting the last data sample over the + fronthaul interface"; + } + leaf t2a-min-cp-ul { + type uint32; + units nanoseconds; + description + "the minimum O-RU data processing delay between receiving real time + up-link control plane message over the fronthaul interface and + receiving the first IQ sample at the antenna"; + } + leaf t2a-max-cp-ul { + type uint32; + units nanoseconds; + description + "the earliest allowable time when a real time up-link control message + is received before the corresponding first IQ sample is received at + the antenna"; + } + leaf ta3-min-ack { + type int32; + units nanoseconds; + description + "the minimum delay between the DL/UL air reference point (tDL=0 or tUL=0) of symbol M + and the time O-RU sends section type 8 (ACK/NACK feedback) to O-DU. + This value can be negative, which indicates it is in advance of the air reference point. + This leaf only exists if section extension 22 (ACK/NACK request) and section type 8 (ACK/NACK feedback) + are supported by at least one endpoint."; + } + leaf ta3-max-ack { + type int32; + units nanoseconds; + description + "the maximum delay between the DL/UL air reference point (tDL=0 or tUL=0) of symbol M + and the time O-RU sends section type 8 (ACK/NACK feedback) to O-DU. + This value can be negative, which indicates it is in advance of the air reference point. + This leaf only exists if section extension 22 (ACK/NACK request) and section type 8 (ACK/NACK feedback) + are supported by at least one endpoint."; + } + + uses ta3-2g; + } + + grouping delay-management-group { + description "a delay management grouping"; + list bandwidth-scs-delay-state { + key "bandwidth subcarrier-spacing"; + description + "Array of structures containing sets of parameters for delay management."; + + uses bandwidth-configuration; + + container ru-delay-profile { + config false; + description "container for O-RU delay parameters"; + + uses ru-delay-profile; + } + } + + list non-default-ru-delay-profile { + if-feature feat:BF-DELAY-PROFILE; + key "delay-profile-id"; + description + "Sets of parameters defined when o-ru supported multiple delay profiles, + only the parameters that are different from the default delay profile are defined."; + leaf delay-profile-id { + type uint16 { + range "1..max"; + } + description + "Used to identify the set of delay profile that are different from the default delay profile. + Value '0' is reserved to refer to default value"; + } + list non-default-bandwidth-scs-delay-state { + key "bandwidth subcarrier-spacing"; + description + "Array of structures containing sets of parameters for delay management."; + uses bandwidth-configuration; + container non-default-ru-delay-profile { + config false; + description + "O-RU delay parameters that are different from the default delay profile. + If a parameter doesn't exist, this parameter will use the defalt value determined in ru-delay-profile."; + uses non-default-ru-delay-profile; + } + } + } + + container adaptive-delay-configuration { + if-feature ADAPTIVE-RU-PROFILE; + description "container for adaptive delay parameters"; + list bandwidth-scs-delay-state { + key "bandwidth subcarrier-spacing"; + description + "Array of structures containing sets of parameters for delay management."; + + uses bandwidth-configuration; + + container o-du-delay-profile { + description + "O-DU provided delay profile for adaptive delay configuration"; + + uses o-du-delay-profile; + } + } + + container transport-delay { + description + "O-DU provided transport-delay parameters"; + uses t12; + uses t34; + } + } + + leaf beam-context-gap-period { + type uint16; + units microseconds; + description + "Time gap between the end of reception window of the C-Plane message(Msg-A) with new beam weights for a given beamId and end of the reception window + of the C-Plane message(Msg-B) that cites the same beamId without weights in the new context. + Note: Value of '0' indicates that the end of the Msg-A and Msg-B reception windows are perfectly aligned, which should allow Msg-B to use the new + beamforming weights with a '0' microsecond beam-context-gap-period. Value of '65535' implies infinite gap, which means a beamId may not be reused + at all in a different context (applicable only to weight-based dynamic beamforming). If O-DU chooses not to interpret/honor this value, behaviour + of O-RU is unpredictable if a beamId has new weights loaded in one context and is reused in a different context. Please refer the CUS-Plane spec + Section 'Weight-based dynamic beamforming' for detailed description"; + } + } + container delay-management { + description "top-level tree covering off O-DU to O-RU delay management"; + + uses delay-management-group; + } +} diff --git a/radio/fhi_72/mplane/yang/models/o-ran-file-management.yang b/radio/fhi_72/mplane/yang/models/o-ran-file-management.yang index ee07645a44..ee0a553ff0 100644 --- a/radio/fhi_72/mplane/yang/models/o-ran-file-management.yang +++ b/radio/fhi_72/mplane/yang/models/o-ran-file-management.yang @@ -3,10 +3,12 @@ module o-ran-file-management { namespace "urn:o-ran:file-management:1.0"; prefix "o-ran-file-mgmt"; - import ietf-crypto-types { - prefix "ct"; - } + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } organization "O-RAN Alliance"; @@ -16,7 +18,7 @@ module o-ran-file-management { description "This module defines the configuration and operations for handling upload. - Copyright 2019 the O-RAN Alliance. + Copyright 2024 the O-RAN Alliance. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -42,6 +44,52 @@ module o-ran-file-management { contributors may be used to endorse or promote products derived from this software without specific prior written permission."; + revision "2024-08-12" { + description + "version 10.2.0 + + 1) Improve YANG description enforce typing of IPv6 addresses"; + + reference "ORAN-WG4.M.0-v10.00"; + } + + revision "2023-04-10" { + description + "version 10.1.0 + + 1) Remove crypto-types dependency"; + + reference "ORAN-WG4.M.0-v10.00"; + } + + revision "2022-08-15" { + description + "version 10.0.0 + + 1) added password for FTPES + 2) clarified path/folder description "; + + reference "ORAN-WG4.M.0-v10.00"; + } + + revision "2021-12-01" { + description + "version 7.1.0 + + 1) typographical corrections"; + + reference "ORAN-WG4.M.0-v07.00"; + } + + revision "2021-07-26" { + description + "version 7.0.0 + + 1) added FTPES support"; + + reference "ORAN-WG4.M.0-v07.00"; + } + revision "2019-07-03" { description "version 1.1.0 @@ -63,21 +111,157 @@ module o-ran-file-management { reference "ORAN-WG4.M.0-v01.00"; } + /***********************************************/ + /* Identities for Asymmetric Key Algorithms */ + /***********************************************/ + + identity asymmetric-key-algorithm { + description + "Base identity from which all asymmetric key + encryption Algorithm."; + } + + identity rsa1024 { + base asymmetric-key-algorithm; + description + "The RSA algorithm using a 1024-bit key."; + reference + "RFC 8017: + PKCS #1: RSA Cryptography Specifications Version 2.2."; + } + + identity rsa2048 { + base asymmetric-key-algorithm; + description + "The RSA algorithm using a 2048-bit key."; + reference + "RFC 8017: + PKCS #1: RSA Cryptography Specifications Version 2.2."; + } + + identity rsa3072 { + base asymmetric-key-algorithm; + description + "The RSA algorithm using a 3072-bit key."; + reference + "RFC 8017: + PKCS #1: RSA Cryptography Specifications Version 2.2."; + } + + identity rsa4096 { + base asymmetric-key-algorithm; + description + "The RSA algorithm using a 4096-bit key."; + reference + "RFC 8017: + PKCS #1: RSA Cryptography Specifications Version 2.2."; + } + + identity rsa7680 { + base asymmetric-key-algorithm; + description + "The RSA algorithm using a 7680-bit key."; + reference + "RFC 8017: + PKCS #1: RSA Cryptography Specifications Version 2.2."; + } + + identity rsa15360 { + base asymmetric-key-algorithm; + description + "The RSA algorithm using a 15360-bit key."; + reference + "RFC 8017: + PKCS #1: RSA Cryptography Specifications Version 2.2."; + } + + identity secp192r1 { + base asymmetric-key-algorithm; + description + "The ECDSA algorithm using a NIST P256 Curve."; + reference + "RFC 6090: + Fundamental Elliptic Curve Cryptography Algorithms."; + } + identity secp224r1 { + base asymmetric-key-algorithm; + description + "The ECDSA algorithm using a NIST P256 Curve."; + reference + "RFC 6090: + Fundamental Elliptic Curve Cryptography Algorithms."; + } + + identity secp256r1 { + base asymmetric-key-algorithm; + description + "The ECDSA algorithm using a NIST P256 Curve."; + reference + "RFC 6090: + Fundamental Elliptic Curve Cryptography Algorithms."; + } + + identity secp384r1 { + base asymmetric-key-algorithm; + description + "The ECDSA algorithm using a NIST P256 Curve."; + reference + "RFC 6090: + Fundamental Elliptic Curve Cryptography Algorithms."; + } + + identity secp521r1 { + base asymmetric-key-algorithm; + description + "The ECDSA algorithm using a NIST P256 Curve."; + reference + "RFC 6090: + Fundamental Elliptic Curve Cryptography Algorithms."; + } + + /**********************************************************/ + /* Typedefs for identityrefs to above base identities */ + /**********************************************************/ + + typedef asymmetric-key-algorithm-ref { + type identityref { + base asymmetric-key-algorithm; + } + description + "This typedef enables importing modules to easily define an + identityref to the 'asymmetric-key-algorithm' + base identity."; + } + grouping file-path-grouping { - description "Complete logical path of the file to upload/download - (no wildcard is allowed) ex : /o-RAN/log/syslog.1"; + description "Grouping, that provides local path and remote path for the + purpose of File Management scenarios."; + leaf local-logical-file-path { type string; mandatory true; - description "Local logical file path"; + description "URI specifying the complete logical path relative to the root of the logical file system + structure (the common root for o-ran/log, o-ran/pm, o-ran/transceiver or o-ran/beamforming) of the file + to upload/download (no wildcard is allowed), including a file name and its extension. + Example: 'o-ran/log/file_1.abc', where 'o-ran/log/' represents relative path to folder containing log files + as specified by O-RAN, 'file_1' represents desired filename and 'abc' represents desired filename's extension. + The content shall conform to RFC3986 'Uniform Resource Identifier (URI): Generic Syntax'"; } leaf remote-file-path { type string; mandatory true; - description "URI specifying the remote-file-path on O-DU/NMS. - Format:sftp://@[:port]/path"; + description "URI specifying the remote-file-path on O-DU/SMO or on stand-alone file server. + The content shall conform to RFC3986 'Uniform Resource Identifier (URI): Generic Syntax'. + When upload/download is via sftp, the format shall be of the form + sftp://@[:port]/path + When upload/download is via ftpes, the format shall be of the form + ftpes://@[:port]/path + IPv6 addresses are distinguished by enclosing the IP literal within square brackets ('[' and ']') as defined by RFC3986 e.g., sftp://user@[2001:0:130F::9C0:876A:130B]:22/path. + + Note, ftpes is not an IANA registered URI scheme, but used here to signal + that a file transfer should be performed over FTPES"; } } @@ -102,7 +286,7 @@ module o-ran-file-management { } grouping credential-information { - description "Type of authentication to use for SFTP upload or download."; + description "Type of authentication to use for file upload or download."; choice credentials { case password { container password { @@ -111,21 +295,22 @@ module o-ran-file-management { type string; mandatory true; description - "password needed for O-RU authentication."; + "password used for O-RU authentication to sFTP server for the associated username defined in the remote-file-path."; } description - "password for O-RU authentication method in use"; + "password for O-RU authentication method in use. This information + shall be ignored by an O-RU that is using FTPES based file transfer"; } container server { list keys { key algorithm; ordered-by user; - uses ct:public-key-grouping; + uses public-key-grouping; description "List of allowed algorithms with its keys"; } description - "Key for sFTP server authentication"; + "SSH Key for file server authentication"; } } case certificate { @@ -134,9 +319,22 @@ module o-ran-file-management { description "certificate authentication method in use"; } + } description ""; } + container application-layer-credential{ + + leaf appl-password { + type string; + description + "The parameter represents the password which may be needed for O-RU application level authentication. + For example, to perform authenticatation towards an FTPes server which does not allow anonymous account, + in addition to X.509v3 certificate for TLS authentication, password configured here need to be used together with username defined in the remote-file-path "; + } + description + "Application layer credential information"; + } } grouping retrieve-input { @@ -144,12 +342,13 @@ module o-ran-file-management { leaf logical-path { type string; mandatory true; - description "O-RAN unit of which the files are to be listed. - ex : O-RAN/log, o-RAN/PM, O-RAN/transceiver"; + description "URL specifying the logical path relative to the root of the logical file system + structure (the common root for o-ran/log, o-ran/pm, o-ran/transceiver or o-ran/beamforming) + of the files to be listed."; } leaf file-name-filter { type string; - description "Filter which are to be applied on the result list of file names (* is allowed as wild-card)."; + description "Filter which needs to be applied on the result list of file names (* is allowed as wild-card)."; } } @@ -163,10 +362,48 @@ module o-ran-file-management { } } + grouping public-key-grouping { + description + "A public key. + + The 'algorithm' and 'public-key' nodes are not + mandatory because they MAY be defined in . + Implementations SHOULD assert that these values are + either configured or that they exist in ."; + leaf algorithm { + nacm:default-deny-write; + type asymmetric-key-algorithm-ref; + must '../public-key'; + description + "Identifies the key's algorithm. More specifically, + this leaf specifies how the 'public-key' binary leaf + is encoded."; + reference + "RFC CCCC: Common YANG Data Types for Cryptography"; + } + leaf public-key { + nacm:default-deny-write; + type binary; + must '../algorithm'; + description + "A binary that contains the value of the public key. The + interpretation of the content is defined by the key + algorithm. For example, a DSA key is an integer, an RSA + key is represented as RSAPublicKey as defined in + RFC 8017, and an Elliptic Curve Cryptography (ECC) key + is represented using the 'publicKey' described in + RFC 5915."; + reference + "RFC 8017: Public-Key Cryptography Standards (PKCS) #1: + RSA Cryptography Specifications Version 2.2. + RFC 5915: Elliptic Curve Private Key Structure."; + } + } + // RPCs rpc file-upload { - description "File upload over SFTP from equipment to NETCONF client"; + description "Management plane trigger to upload file from O-RU to file-server"; input { uses file-path-grouping; uses credential-information; @@ -187,15 +424,9 @@ module o-ran-file-management { } } - notification file-upload-notification { - uses file-path-grouping; - uses output-status-grouping; - description ""; - } - rpc file-download { description - "Management plane triggered to generate the download file of O-RU."; + "Management plane trigger to download file from file-server to O-RU."; input { uses file-path-grouping; uses credential-information; @@ -205,6 +436,12 @@ module o-ran-file-management { } } + notification file-upload-notification { + uses file-path-grouping; + uses output-status-grouping; + description ""; + } + notification file-download-event { uses file-path-grouping; uses output-status-grouping; diff --git a/radio/fhi_72/mplane/yang/models/o-ran-hardware.yang b/radio/fhi_72/mplane/yang/models/o-ran-hardware.yang new file mode 100644 index 0000000000..4e00d034d5 --- /dev/null +++ b/radio/fhi_72/mplane/yang/models/o-ran-hardware.yang @@ -0,0 +1,461 @@ +module o-ran-hardware { + yang-version 1.1; + namespace "urn:o-ran:hardware:1.0"; + prefix "o-ran-hw"; + + import ietf-hardware { + prefix hw; + } + import iana-hardware { + prefix ianahw; + } + import ietf-yang-types { + prefix yang; + } + import o-ran-wg4-features { + prefix or-feat; + } + + organization "O-RAN Alliance"; + + contact + "www.o-ran.org"; + + description + "This module defines the YANG definitions for managing the O-RAN hardware. + + Copyright 2024 the O-RAN Alliance. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the above disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the above disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the Members of the O-RAN Alliance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission."; + + revision "2024-04-15" { + description + "version 15.0.0 + + 1) Added support for new fan tray component."; + + reference "ORAN-WG4.M.0-v15.00"; + } + + revision "2023-08-14" { + description + "version 13.0.0 + + 1) New connector types for RS485 and external IO"; + + reference "ORAN-WG4.M.0-v13.00"; + } + + revision "2022-12-05" { + description + "version 10.1.0 + + 1) Clarifications for Network Energy Saving"; + + reference "ORAN-WG4.M.0-v10.00"; + } + + revision "2022-08-15" { + description + "version 10.0.0 + + 1) introduction of O-RU connector functionality. + 2) fixing constraints"; + + reference "ORAN-WG4.M.0-v10.00"; + } + + revision "2021-12-01" { + description + "version 5.2.0 + + 1) typographical corrections in descriptions."; + + reference "ORAN-WG4.M.0-v05.00"; + } + + revision "2021-03-22" { + description + "version 5.1.0 + + 1) typographical corrections in descriptions."; + + reference "ORAN-WG4.M.0-v05.00"; + } + + revision "2020-12-10" { + description + "version 5.0.0 + + 1) added date-last-service leaf used in pnfRegistration"; + + reference "ORAN-WG4.M.0-v05.00"; + } + + revision "2020-04-17" { + description + "version 3.0.0 + + 1) added new leaf to indicate whether O-RU supports dying gasp + 2) added new identities for PA and FPGA"; + + reference "ORAN-WG4.M.0-v03.00"; + } + + revision "2019-07-03" { + description + "version 1.1.0 + + 1) added new identities to accommodate cross working group use of + o-ran-hardware and associated set of augmentations that are backwards + compatible to version 1.0.0"; + + reference "ORAN-WG4.M.0-v01.00"; + } + + revision "2019-02-04" { + description + "version 1.0.0 + + 1) imported model from xRAN + 2) changed namespace and reference from xran to o-ran"; + + reference "ORAN-WG4.M.0-v01.00"; + } + + feature ENERGYSAVING { + description + "Indicates that the Radio Unit supports energy saving state."; + } + + // identity statements + identity O-RAN-RADIO { + base ianahw:module; + description + "Module used as it represents a self-contained sub-system + used in /hw:/hardware/hw:component/hw:class to represent + an O-RAN RU"; + } + + identity O-RAN-HW-COMPONENT { + base ianahw:module; + description + "Module used as it represents a self-contained sub-system + used in /hw:/hardware/hw:component/hw:class to represent + any O-RAN hardware component"; + } + + identity O-DU-COMPONENT { + base O-RAN-HW-COMPONENT; + description + "Used in /hw:/hardware/hw:component/hw:class to represent + any O-RAN defined O-DU hardware component"; + } + + identity O-RAN-FAN-TRAY { + base O-RAN-HW-COMPONENT; + description + "Used in /hw:/hardware/hw:component/hw:class to represent + any O-RAN defined fan tray hardware component."; + } + + identity O-RU-COMPONENT { + base O-RAN-HW-COMPONENT; + description + "Used in /hw:/hardware/hw:component/hw:class to represent + any O-RAN defined O-RU hardware component, including a stand-alone + O-RU or an O-RU component integrated into a multi-module system."; + } + + identity O-RU-POWER-AMPLIFIER { + base O-RU-COMPONENT; + description + "Used in /hw:/hardware/hw:component/hw:class to represent + an O-RU's power amplifier, and may be used for reporting + measurements on a per class basis"; + } + + identity O-RU-FPGA { + base O-RU-COMPONENT; + description + "Used in /hw:/hardware/hw:component/hw:class to represent + an FPGA in an O-RU, and may be used for reporting + measurements on a per class basis"; + } + + identity O-RU-CONNECTOR { + base ianahw:hardware-class; + description + "This identity is applicable if the hardware class is some sort + of O-RU connector."; + } + + identity O-RU-ANTENNA-CONNECTOR { + base O-RU-CONNECTOR; + description + "This identity is applicable if the hardware class is some sort + of connector capable of interfacing between an O-RU and some + antenna function."; + } + + identity O-RU-FEEDER { + base O-RU-ANTENNA-CONNECTOR; + description + "This identity is applicable if the hardware class is an + antenna feeder."; + } + + identity O-RU-BF-CAL { + base O-RU-ANTENNA-CONNECTOR; + description + "This identity is applicable if the hardware class is for + beamforming calibration."; + } + + identity O-RU-RS485 { + base O-RU-CONNECTOR; + description + "This identity is applicable if the hardware class is some sort + of connector capable of interfacing between an O-RU and Antenna + Line Devices using RS-485 electrical standard."; + } + + identity O-RU-EXTIO { + base O-RU-CONNECTOR; + description + "This identity is applicable if the hardware class is some sort + of connector capable of interfacing between an O-RU external I/O lines."; + } + + // typedef statements + typedef energysaving-state { + type enumeration { + enum UNKNOWN { + description + "The O-RU is unable to report its energy saving state."; + } + enum SLEEPING { + description + "The O-RU is in energy saving state. In this mode M-Plane connection is active. Depending + on the O-RU's design - other planes, functions and hardware components which are not needed + by the O-RU in energy saving state can be disabled or switched off by the O-RU autonomously."; + } + enum AWAKE { + description + "The O-RU is not in an energy saving state."; + } + } + description + "New typedef since ietf-hardware only covers power-state + for redundancy purposes, not to indicate energy saving operations. + For details please see O-RAN WG4 M-Plane specification, clause 9.1.3 'Modify state'"; + } + + typedef availability-type { + type enumeration { + enum UNKNOWN { + description "The Radio Unit is unable to report its availability state."; + } + enum NORMAL { + description + "The equipment is functioning correctly."; + } + enum DEGRADED { + description + "The equipment may be reporting a major alarm or may be reporting a critical + alarm that is only impacting one or more subcomponent, but where the + equipment's implementation permit it to continue operation (server traffic) + in a degraded state. + + Used for example, when the equipment has M identical sub-components and + when a critical alarm is impacting only N subcomponents, where N@[:]/path"; } - uses o-ran-fm:credential-information; + uses o-ran-file-mgmt:credential-information; + } + + list remote-file-uploads { + key remote-file-upload-path; + description + "file upload can be done to one or more than one file servers + + An O-RU receiving this list should ignore the deprecated + remote-SFTP-uploads list, if received"; + + leaf remote-file-upload-path { + type inet:uri; + description + "URI specifying the remote location where the files are to be uploaded. + + When upload is via SFTP, the format shall be of the form + sftp://@[:port]/path + When upload is via FTPES, the format shall be of the form + ftpes://@[:port]/path + + Note, ftpes is not an IANA registered URI scheme, but used here to signal + that a file transfer should be performed over FTPES"; + } + + uses o-ran-file-mgmt:credential-information; } // transceiver measurements applicable to all O-RAN HW functions @@ -567,6 +1691,42 @@ module o-ran-performance-management { window measurement objects periodically."; } + leaf shared-cell-measurement-interval { + if-feature feat:SHARED-CELL-STATS; + type uint16; + units seconds; + description + "measurement interval to measure the shared-cell measurement objects periodically."; + } + + leaf symbol-rssi-measurement-interval { + type uint16; + units seconds; + description + "measurement interval to measure the symbol-wise TD-RSSI measurement objects periodically."; + } + + leaf tssi-measurement-interval { + type uint16; + units seconds; + description + "Measurement interval to measure the tx TSSI measurement objects periodically."; + } + + leaf rssi-measurement-interval { + type uint16; + units seconds; + description + "Measurement interval to measure the RSSI measurement objects periodically."; + } + + leaf tx-antenna-measurement-interval { + type uint16; + units seconds; + description + "Measurement interval to measure the tx antenna measurement objects periodically."; + } + leaf notification-interval { type uint16; units seconds; @@ -586,11 +1746,14 @@ module o-ran-performance-management { leaf max-bin-count{ type uint16; config false; - mandatory true; + status deprecated; description "indicates the maximum value of configurable bin-count for frequency table in transceiver-measurement-objects as one of module - capabilities."; + capabilities. + + This leaf is deprecated and replaced with the max-bin-count in the + transceiver-objects list."; } list transceiver-measurement-objects { @@ -673,33 +1836,7 @@ module o-ran-performance-management { } leaf-list report-info { - type enumeration { - enum MAXIMUM { - description - "to report maximum value and its recorded time within the - measurement-interval for the measurement-object."; - } - enum MINIMUM { - description - "to report minimum value and its recorded time within the - measurement-interval for the measurement-object."; - } - enum FIRST { - description - "to report first value and its recorded time within the - measurement-interval for the measurement-object."; - } - enum LATEST { - description - "to report latest value and its recorded time within the - measurement-interval for the measurement-object."; - } - enum FREQUENCY_TABLE { - description - "to report frequency bin table within the - measurement-interval for the measurement-object."; - } - } + type transceiver-report-info; description "The reporting info to the measurement object."; } @@ -715,16 +1852,7 @@ module o-ran-performance-management { } leaf function { - type enumeration { - enum RAW { - description - "the value is expressed by real value."; - } - enum LOG_10 { - description - "the value is expressed by logarithm with base 10."; - } - } + type transceiver-function; description "the value to be recorded for transceiver-measurement @@ -851,7 +1979,7 @@ module o-ran-performance-management { description "the reception window stats are counted per transport flow. When there are multiple transport flows between O-DU and O-RU, - e.g. multiple sets of o-du mac address, ru mac address and + e.g. multiple sets of o-du-mac-address, ru-mac-address and vlan-id, the reception window stats per transport flow are counted in this case. This configuration is allowed only when O-RU supports @@ -862,7 +1990,7 @@ module o-ran-performance-management { description "the reception window stats are counted per eAxC ID, which is - used in the header of receivd data packet. + used in the header of received data packet. This configuration is allowed only when O-RU supports a feature GRANULARITY-EAXC-ID-MEASUREMENT."; } @@ -931,7 +2059,7 @@ module o-ran-performance-management { description "the transmission stats are counted per transport flow. When there are multiple transport flows between O-DU and O-RU, - e.g. multiple sets of o-du mac address, ru mac address and + e.g. multiple sets of o-du-mac-address, ru-mac-address and vlan-id, the transmission stats per transport flow are counted in this case. This configuration is allowed only when O-RU supports @@ -942,7 +2070,7 @@ module o-ran-performance-management { description "the transmission stats are counted per eAxC ID, which is - used in the header of receivd data packet. + used in the header of received data packet. This configuration is allowed only when O-RU supports a feature GRANULARITY-EAXC-ID-MEASUREMENT."; } @@ -968,6 +2096,58 @@ module o-ran-performance-management { } + list shared-cell-measurement-objects { + if-feature feat:SHARED-CELL-STATS; + key "measurement-object"; + description + "optional list used to report shared-cell measurements."; + leaf measurement-object { + type enumeration { + enum RX_UP_UL { + description + "the number of received U-Plane data packets in UL direction, corresponds to shared-cell-combine-entity. + Refer to WG4 CUS secification clause 9.1."; + } + enum RX_UP_UL_COMBINED { + description + "the number of U-Plane data packets, which is processed by combine functional block. + Refer to WG4 CUS secification clause 9.1."; + } + } + description + "target metric to measure shared-cell operation."; + } + leaf active { + type boolean; + default false; + description + "Enable/disable the performance measurement per port + measurement object."; + } + leaf object-unit { + type enumeration { + enum TRANSPORT { + description + "unit to measure the performance per object-id, + the shared-cell stats are counted per TRANSPORT."; + } + } + description "unit to measure the performance per object-id."; + } + leaf report-info { + type enumeration { + enum COUNT { + description + "the number of data packet are counted for shared-cell stats."; + } + } + description + "The reporting info to the measurement object."; + } + uses shared-cell-measurement-result-grouping; +// configuration and measurement result for the shared-cell stats + } + list epe-measurement-objects { key "measurement-object"; description @@ -977,12 +2157,20 @@ module o-ran-performance-management { type enumeration { enum TEMPERATURE { description - "Measured temperature in degrees celcius"; + "Measured temperature in degrees Celsius"; } enum POWER { description "Measured power consumed, in watts (W)"; } + enum VOLTAGE { + description + "Measured voltage, in volts (V)"; + } + enum CURRENT { + description + "Measured current consumption, in amperes (A)"; + } } description "Target metric to measure the performance"; } @@ -994,63 +2182,369 @@ module o-ran-performance-management { "Enable/disable the performance measurement per Object"; } - leaf-list report-info { - type enumeration { - enum MAXIMUM { - description - "to report maximum value and its recorded time within the - measurement-interval for the measurement-object."; - } - enum MINIMUM { - description - "to report minimum value and its recorded time within the - measurement-interval for the measurement-object."; - } - enum AVERAGE { - description - "to report average value within the - measurement-interval for the measurement-object."; - } + leaf object-unit { + type leafref { + path "/hw:hardware/hw:component/hw:class"; } + description + "the hardware component type is used for the object-unit for the + EPE measurements. For example, the object-unit will be set to + or-hw:O-RU-POWER-AMPLIFIER if the measurement relates to + O-RU power amplifiers. + + Other hardware-classes include: + ianahw:power-supply, ianahw:fan, ianahw:cpu"; + } + + leaf-list report-info { + type epe-report-info; description "The reporting info to the measurement object."; } - + leaf bin-count { + type uint16; + description + "the number of bin for the frequency table. + This value shall be less than max-bin-count"; + } + leaf lower-bound { + type decimal64 { + fraction-digits 4; + } + description + "the lower value of the first bin of frequency table."; + } + leaf upper-bound { + type decimal64 { + fraction-digits 4; + } + description + "the upper value of the last bin of frequency table."; + } uses epe-measurement-result-grouping; // configuration and measurement result for the epe-measurement } + list symbol-rssi-measurement-objects { + key "measurement-object"; + description + "optional list used to measure and report symbol-wise time domain RSSI for all UL symbols or configured symbols"; + + leaf measurement-object { + type enumeration { + enum ALL-UL-SYMBOLS { + description + "Measure and report symbol-rssi separately for all UL symbols in every configured number of slots (as defined by 'period' in 'symbol-rssi-measurement-objects'. + And the UL symbols are decided by 'configurable-tdd-pattern', 'static-srs-configuration', 'static-prach-configuration', + and 'dataDirection' in the C-plane messages. + This option is recommended for static TDD case. If this option is used in dynamic TDD case, then O-RU measures + only the allocates UL symbols because O-RU may not know 'candidate UL symbols' which are not allocated. "; + } + + enum CONFIGURED-SYMBOLS { + description + "Measure and report symbol-rssi separately for all configured symbols as defined by the leaf-list 'symbol-index', + This can be used for non-dynamic TDD as well as dynamic TDD cases, + the O-RU should measure all configured symbols, irrespective of whether the UL symbol is allocated or not, + If a c-plane message indicates a symbol within this 'symbol-index' list to be a DL symbol, + O-RU shall not measure RSSI on this symbol."; + } + } + description "Target metric to measure the symbol-rssi"; + } + + leaf object-unit { + type enumeration { + enum RX-ARRAY-CARRIER { + description + "unit to measure the performance per object-id, + only the rx-array-carriers which are not configured with mixed numerology will be measured"; + } + } + mandatory true; + description "unit to measure the performance per object-id."; + } + + list per-rx-array-carrier-configuration { + key "rx-array-carrier"; + description + "measurement configuration per rx-array-carrier to accommodate different Subcarrier Spacing + and TDD pattern per rx-array-carrier, and only the symbol-wise TD-RSSI of the rx-array-carriers + included in per-rx-array-carrier-configuration will be measured and reported"; + + leaf rx-array-carrier { + type leafref { + path "/up:user-plane-configuration/up:rx-array-carriers/up:name"; + } + description "reference to the name of rx-array-carriers"; + } + + leaf period { + type uint16; + + description + "the number of slots that correspond to the period of a TDD-Configuration, + and the start point of the 1st period is subframe#0, slot#0 of the 1st even number radio frame after this configuration is received, + here even number radio frame is used because according to 3GPP TS 38.213, A UE expects that P1 + P2 divides 20 msec"; + } + + leaf-list symbol-index { + type uint16; + + description + "the index of the symbols within 'period'. The first symbol within the 'period is symbol-index=0, next symbol is symbol-index =1 etc. + This symbol-index is different from how symbolId is defined in CUS plane spec. + These symbols are configured for O-RU to measure and report symbol-wise rssi, + used only when 'measurement-object' = 'CONFIGURED-SYMBOLS'. + If a c-plane message indicates a symbol within this 'symbol-index' list to be a DL symbol, + O-RU shall not measure rssi on this symbol"; + } + + leaf active { + type boolean; + default false; + description + "Enable/disable the symbol-rssi measurement per Object"; + } + + leaf-list report-info { + type symbol-rssi-report-info; + description "The reporting info to the measurement object."; + } + + leaf bin-count { + type uint16; + + description + "the number of bin for the frequency table. + This value shall be less than max-bin-count"; + } + + leaf lower-bound { + type int16; + units dBm; + description + "the lower value of the first bin of frequency table."; + } + + leaf upper-bound { + type int16; + units dBm; + description + "the upper value of the last bin of frequency table."; + } + } + + uses symbol-rssi-measurement-result-grouping; + // configuration and measurement result for the symbol-rssi-measurement + } + + list tssi-measurement-objects { + key "measurement-object"; + description + "optional list used to measure and report time domain TSSI for per tx-array-carrier on specific tx-array-element."; + leaf measurement-object { + type enumeration { + enum TSSI-IQ-POWER-LEVEL { + description + "Measured TSSI (Transmit Signal Strength Indicator) digital IQ Power level for each Tx array carrier on the specific array element"; + } + } + description "Target metric to measure the tssi"; + } + + leaf active { + type boolean; + default false; + description + "Enable/disable the TSSI measurement per object"; + } + + leaf object-unit { + type enumeration { + enum CARRIER_ARRAY_ELEMENT { + description + "Unit to measure the performance per object-id."; + } + } + mandatory true; + description "unit to measure the performance per object-id."; + } + + leaf-list report-info { + type tssi-report-info; + description "The reporting info to the measurement object."; + } + uses tssi-measurement-result-grouping; + // configuration and measurement result for the tssi-measurement + } + + list rssi-measurement-objects { + key "measurement-object"; + description + "optional list used to measure and report time domain RSSI for per rx-array-carrier on specific rx-array-element."; + leaf measurement-object { + type enumeration { + enum RSSI { + description + "Measured RSSI (Received Signal Strength Indicator) for each Rx array carrier on the specific array element in dbm"; + } + } + description "Target metric to measure the rssi"; + } + leaf active { + type boolean; + default false; + description + "Enable/disable the RSSI measurement per object"; + } + leaf object-unit { + type enumeration { + enum CARRIER_ARRAY_ELEMENT { + description + "Unit to measure the performance per object-id."; + } + } + mandatory true; + description "unit to measure the performance per object-id."; + } + leaf-list report-info { + type rssi-report-info; + description "The reporting info to the measurement object."; + } + uses rssi-measurement-result-grouping; + // configuration and measurement result for the rssi-measurement + } + + list tx-antenna-measurement-objects { + key "measurement-object"; + description + "Optional list used to report tx-antenna measurements."; + leaf measurement-object { + type enumeration { + enum VSWR { + description + "Measure and report values of VSWR."; + } + } + description "Target metric to measure the tx-antenna measurements."; + } + + leaf active { + type boolean; + default false; + description + "Enable/disable the performance measurement per Object"; + } + + leaf-list report-info { + type tx-antenna-report-info; + description "The reporting info to the measurement object."; + } + + leaf object-unit { + type tx-antenna-object-unit; + mandatory true; + description "Unit to measure the performance per object-id."; + } + + leaf bin-count { + type uint16; + + description + "The number of bin for the frequency table. + This value shall be less than max-bin-count"; + } + + leaf lower-bound { + type decimal64 { + fraction-digits 4; + } + + description + "The lower value of the first bin of frequency table."; + } + + leaf upper-bound { + type decimal64 { + fraction-digits 4; + } + + description + "The upper value of the last bin of frequency table."; + } + uses tx-antenna-measurement-result-grouping; +// configuration and measurement result for the tx-antenna-measurement + + } } grouping measurement-capabilities { + description "a measurement capabilities grouping"; container measurement-capabilitites { config false; description "the type of measurement objects supported by the O-RU"; list transceiver-objects { key measurement-object; + description "list of transceiver objects"; leaf measurement-object { type leafref { path "/performance-measurement-objects/transceiver-measurement-objects/measurement-object"; + require-instance false; } + description "a measurement object"; + } + leaf-list report-info { + type transceiver-report-info; + description "The reporting info supported by this measurement object."; + } + leaf-list function { + type transceiver-function; + description "The function supported by this measurement object."; + } + leaf max-bin-count{ + type uint16; + config false; + description + "when O-RU supports FREQUENCY_TABLE, indicates the maximum value of + configurable bin-count for frequency table in transceiver-measurement-objects."; } } list rx-window-objects { key measurement-object; + description "list of rx window objects"; leaf measurement-object { type leafref { path "/performance-measurement-objects/rx-window-measurement-objects/measurement-object"; + require-instance false; } + description "a measurement object"; } } list tx-stats-objects { key measurement-object; + description "list of tx stats objects"; leaf measurement-object { type leafref { path "/performance-measurement-objects/tx-measurement-objects/measurement-object"; + require-instance false; } + description "a measurement object"; + } + } + list shared-cell-stats-objects { + if-feature feat:SHARED-CELL-STATS; + key measurement-object; + description "list of shared-cell stats objects"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/shared-cell-measurement-objects/measurement-object"; + require-instance false; + } + description "a measurement object"; } } list epe-stats-objects { @@ -1061,7 +2555,9 @@ module o-ran-performance-management { leaf measurement-object { type leafref { path "/performance-measurement-objects/epe-measurement-objects/measurement-object"; + require-instance false; } + description "a measurement object"; } leaf-list component-class { type identityref { @@ -1072,13 +2568,114 @@ module o-ran-performance-management { component for which EPE measurements are supported."; } + leaf-list report-info { + type epe-report-info; + description "The reporting info supported of this measurement object."; + } + leaf max-bin-count{ + type uint16; + config false; + description + "when O-RU supports FREQUENCY_TABLE, indicates the maximum value of + configurable bin-count for frequency table in epe-measurement-objects."; + } + } + + list symbol-rssi-stats-objects { + key measurement-object; + description "list of symbol-wise TD-RSSI stats objects"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/symbol-rssi-measurement-objects/measurement-object"; + require-instance false; + } + description "a measurement object"; + } + leaf-list report-info { + type symbol-rssi-report-info; + description "The reporting info supported for thise measurement object."; + } + leaf max-bin-count{ + type uint16; + config false; + description + "when O-RU supports FREQUENCY_TABLE, indicates the maximum value of + configurable bin-count for frequency table in symbol-rssi-stats-objects."; + } + } + + list tssi-stats-objects { + key measurement-object; + description "list of TSSI stats objects"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/tssi-measurement-objects/measurement-object"; + } + description "a measurement object"; + } + leaf-list report-info { + type tssi-report-info; + description "The reporting info supported for thise measurement object."; + } + } + + list rssi-stats-objects { + key measurement-object; + description "list of RSSI stats objects"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/rssi-measurement-objects/measurement-object"; + } + description "a measurement object"; + } + leaf-list report-info { + type rssi-report-info; + description "The reporting info supported for thise measurement object."; + } + } + + list tx-antenna-stats-objects { + key measurement-object; + description "list of tx antenna objects"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/tx-antenna-measurement-objects/measurement-object"; + require-instance false; + } + description "a measurement object"; + } + + leaf-list object-unit { + type tx-antenna-object-unit; + description "The object units supported for this measurement object."; + } + + leaf-list report-info { + type tx-antenna-report-info; + description "The reporting info supported for this measurement object."; + } + + leaf max-bin-count{ + type uint16; + config false; + description + "when O-RU supports FREQUENCY_TABLE, indicates the maximum value of + configurable bin-count for frequency table in tx-antenna-stats-objects."; + } } } } grouping measurement-notification { + + description + "notification may contain measurement result for transceiver-stats + and/or rx-window-stats and/or tx-stats and/or epe-stats"; list transceiver-stats { key "measurement-object"; + + description + "measurement result of transceiver-measurement per measurement-object"; leaf measurement-object { type leafref { path "/performance-measurement-objects/transceiver-measurement-objects/measurement-object"; @@ -1091,12 +2688,33 @@ module o-ran-performance-management { uses start-and-end-time; uses transceiver-measurement-result-grouping; - description - "measurement result of transceiver-measurement per measurement-object"; + list multiple-transceiver-measurement-result { + config false; + description + "Multiple measurement results of transceiver-measurement. + + The O-RU shall use this list to report one or more measurements per measurement-object in a single notification. + + In addition, the O-RU shall always use the + + measurement-result-stats/transceiver-stats[measurement-object]/transceiver-measurement-result + + schema-node to report a single transceiver measurement-object. The O-RU should report its latest + transceiver measurements available. This ensures O-DUs supporting earlier versions + of this specification can recover the measurement."; + + uses start-and-end-time; + uses transceiver-measurement-result-grouping; + } + } list rx-window-stats { key "measurement-object"; + + description + "measurement result for the reception window measurement per + measurement-object"; leaf measurement-object { type leafref { path "/performance-measurement-objects/rx-window-measurement-objects/measurement-object"; @@ -1108,14 +2726,35 @@ module o-ran-performance-management { uses start-and-end-time; uses rx-window-measurement-result-grouping; - description - "measurement result for the reception window measurement per - measurement-object"; + list multiple-rx-window-measurement-result { + config false; + description + "Multiple measurement results of rx-window-measurement. + + The O-RU shall use this list to report one or more measurements per measurement-object in a single notification. + + In addition, the O-RU shall always use one of the + + measurement-result-stats/rx-window-stats[measurement-object]/count or + measurement-result-stats/rx-window-stats[measurement-object]/tr-measured-result/count or + measurement-result-stats/rx-window-stats[measurement-object]/eaxc-measured-result/count + + schema-nodes to report a single rx-window measurement-object. The O-RU should report its latest + rx-window measurement available. This ensures O-DUs supporting earlier versions + of this specification can recover the measurement."; + + uses start-and-end-time; + uses rx-window-measurement-result-grouping; + } } list tx-stats { key "measurement-object"; + + description + "measurement result for the tx stats measurement per + measurement-object"; leaf measurement-object { type leafref { path "/performance-measurement-objects/tx-measurement-objects/measurement-object"; @@ -1127,15 +2766,59 @@ module o-ran-performance-management { uses start-and-end-time; uses tx-measurement-result-grouping; - description - "measurement result for the tx stats measurement per - measurement-object"; + list multiple-tx-measurement-result { + config false; + description + "Multiple measurement results of tx-measurement. + + The O-RU shall use this list to report one or more measurements per measurement-object in a single notification. + + In addition, the O-RU shall always use one of the + + measurement-result-stats/tx-stats[measurement-object]/count or + measurement-result-stats/tx-stats[measurement-object]/tr-measured-result/count or + measurement-result-stats/tx-stats[measurement-object]/eaxc-measured-result/count + + schema-nodes to report a single tx-measurement. The O-RU should report its latest + tx-measurement available. This ensures O-DUs supporting earlier versions + of this specification can recover the measurement."; + + uses start-and-end-time; + uses tx-measurement-result-grouping; + } } + list shared-cell-stats { + if-feature feat:SHARED-CELL-STATS; + key "measurement-object"; + + description + "measurement result for the shared-cell measurement per measurement-object"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/shared-cell-measurement-objects/measurement-object"; + } + description + "measurement-object for the shared-cell measurement"; + } + + list multiple-shared-cell-measurement-result { + config false; + description + "Multiple measurement results of shared-cell-measurement."; + + uses start-and-end-time; + uses shared-cell-measurement-result-grouping; + + } + } + container epe-stats { + status deprecated; description - "container for the epe stats measurement"; + "container for the EPE stats measurement - deprecated because measurement object + isn't included"; uses start-and-end-time; uses epe-measurement-result-grouping; @@ -1143,9 +2826,150 @@ module o-ran-performance-management { } - description - "notification may contain measurement result for transceiver-stats - and/or rx-window-stats"; + list epe-statistics { + key "measurement-object"; + + description + "measurement result for the EPE stats measurement per + measurement-object"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/epe-measurement-objects/measurement-object"; + } + + description + "measurement-object for the EPE stats measurement"; + } + uses start-and-end-time; + uses epe-measurement-result-grouping; + + list multiple-epe-measurement-result { + config false; + description + "Multiple measurement results of epe-measurement. + + The O-RU shall use this list to report one or more measurements per measurement-object in a single notification. + + In addition, the O-RU shall use the + + measurement-result-stats/epe-statistics[measurement-object]/epe-measurement-resultv2/ + + schema-nodes to report a single epe-measurement for a measurement-object. The O-RU + should report its latest epe-measurement available. This ensures O-DUs supporting earlier + versions of this specification can recover the measurement."; + + uses start-and-end-time; + uses epe-measurement-result-grouping; + } + + } + + list symbol-rssi-stats { + key "measurement-object"; + + description + "measurement result of symbol-rssi-measurement per measurement-object"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/symbol-rssi-measurement-objects/measurement-object"; + } + + description + "measurement-object for the symbol-rssi-measurement"; + } + + uses start-and-end-time; + uses symbol-rssi-measurement-result-grouping; + + list multiple-symbol-rssi-measurement-result { + config false; + description + "Multiple measurement results of symbol-rssi-measurement. + + The O-RU shall use this list to report one or more measurements per measurement-object in a single notification. + + In addition, the O-RU shall always use one of the + + measurement-result-stats/symbol-rssi-stats[measurement-object]/ + + schema-nodes to report a single symbol-rssi measurement for a measurement-object. + The O-RU should report its latest symbol-rssi measurement available."; + + uses start-and-end-time; + uses symbol-rssi-measurement-result-grouping; + } + } + + list tssi-stats { + key "measurement-object"; + description + "Measurement result of tssi per measurement-object"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/tssi-measurement-objects/measurement-object"; + } + description + "measurement-object for the tx-array-carriers"; + } + + list tssi-measurement-result { + config false; + description + "Multiple measurement results of tssi-measurement. + The O-RU shall use this list to report one or more measurements per measurement-object in a single notification."; + uses start-and-end-time; + uses tssi-measurement-result-grouping; + } + } + + list rssi-stats { + key "measurement-object"; + description + "Measurement result of rssi per measurement-object"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/rssi-measurement-objects/measurement-object"; + } + description + "measurement-object for the rx-array-carrier"; + } + list rssi-measurement-result { + config false; + description + "Multiple measurement results of rssi-measurement. + The O-RU shall use this list to report one or more measurements per measurement-object in a single notification."; + uses start-and-end-time; + uses rssi-measurement-result-grouping; + } + } + + list tx-antenna-stats { + key "measurement-object"; + + description + "Measurement result of tx-antenna-measurement per measurement-object"; + leaf measurement-object { + type leafref { + path "/performance-measurement-objects/tx-antenna-measurement-objects/measurement-object"; + } + + description + "measurement-object for the tx-antenna-measurement"; + } + + list tx-antenna-measurement-result { + config false; + description + "Multiple measurement results of tx-antenna-measurement. + + The O-RU shall use this list to report one or more measurements per measurement-object in a single notification."; + + uses start-and-end-time; + uses tx-antenna-measurement-result-grouping; + } + + } + } // Top level container diff --git a/radio/fhi_72/mplane/yang/models/o-ran-processing-element.yang b/radio/fhi_72/mplane/yang/models/o-ran-processing-element.yang index fc930a0a81..015d14f2e2 100644 --- a/radio/fhi_72/mplane/yang/models/o-ran-processing-element.yang +++ b/radio/fhi_72/mplane/yang/models/o-ran-processing-element.yang @@ -23,6 +23,14 @@ module o-ran-processing-element { prefix "o-ran-int"; } + import o-ran-wg4-features { + prefix "feat"; + } + + import o-ran-usermgmt { + prefix "or-user"; + } + organization "O-RAN Alliance"; contact @@ -32,7 +40,7 @@ module o-ran-processing-element { "This module defines the YANG definitions for mapping of transport flows to processing elements. Three options are supported: i) virtual MAC based mapping - ii) MAC addrress + VLAN-ID based mapping + ii) MAC address + VLAN-ID based mapping iii) UDP/IP based mapping Copyright 2020 the O-RAN Alliance. @@ -61,16 +69,37 @@ module o-ran-processing-element { contributors may be used to endorse or promote products derived from this software without specific prior written permission."; - revision "2020-04-17" { - description - "version 3.0.0 + revision "2022-08-15" { + description + "version 10.0.0 - 1) added new enum SHARED-CELL-ETH-INTERFACE in - transport-session-type and new containers north-eth-flow and - south-eth-flow to enable Shared cell scenario."; + 1) introducing SHARED-ORU-MULTI-OPERATOR feature + 2) fixing constraints"; - reference "ORAN-WG4.M.0-v03.00"; - } + reference "ORAN-WG4.M.0-v10.00"; + } + + revision "2021-12-01" { + description + "version 8.0.0 + + 1) typographical corrections in descriptions. + 2) Add support for multiple transport-session-type per O-RU"; + + reference "ORAN-WG4.M.0-v08.00"; + } + + + revision "2020-04-17" { + description + "version 3.0.0 + + 1) added new enum SHARED-CELL-ETH-INTERFACE in + transport-session-type and new containers north-eth-flow and + south-eth-flow to enable Shared cell scenario."; + + reference "ORAN-WG4.M.0-v03.00"; + } revision "2019-07-03" { description @@ -78,7 +107,7 @@ module o-ran-processing-element { 1) added new leaf to enable O-RU to report the maximum number of transport flows it can support, e.g., due to restrictions on number - of VLAN-IDs when ethernet type transport is used."; + of VLAN-IDs when Ethernet type transport is used."; reference "ORAN-WG4.M.0-v01.00"; } @@ -99,16 +128,162 @@ module o-ran-processing-element { } // groupings - grouping pe-group { - leaf maximum-number-of-transport-flows { - type uint16 { - range "1..4094"; + grouping non-shared-cell-flow-group { + description "a grouping for non-shared cell O-RU flows"; + + container aliasmac-flow { + when "../../../transport-session-type = 'ALIASMAC-INTERFACE'"; + if-feature o-ran-int:ALIASMAC-BASED-CU-PLANE; + description "leafs for virtual mac type data flows"; + leaf ru-aliasmac-address { + type leafref { + path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:alias-macs"; + } + mandatory true; + description + "O-RU's alias MAC address used for alias MAC based flow"; + } + leaf vlan-id { + type leafref { + path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id"; + } + description + "O-RU's VLAN-ID used for alias MAC based flow"; + } + leaf o-du-mac-address { + type yang:mac-address; + mandatory true; + description + "O-DU's MAC address used for alias MAC based flow"; } - config false; - default 4094; - description - "The maximum number of transport flows that can be supported by an O-RU"; } + container eth-flow { + when "../../../transport-session-type = 'ETH-INTERFACE'"; + description "leafs for mac + vlan-id type data flows"; + leaf ru-mac-address { + type leafref { + path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:mac-address"; + } + mandatory true; + description + "O-RU's MAC address used for Ethernet based flow"; + } + leaf vlan-id { + type leafref { + path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id"; + } + mandatory true; + description + "O-RU's VLAN-ID used for Ethernet based flow"; + } + leaf o-du-mac-address { + type yang:mac-address; + mandatory true; + description + "O-DU's MAC address used for Ethernet based flow"; + } + } + container udpip-flow { + when "../../../transport-session-type = 'UDPIP-INTERFACE'"; + description "leafs for UDP/IP type data flows"; + choice address { + leaf ru-ipv4-address { + type leafref { + path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/ip:ipv4/ip:address/ip:ip"; + } + description "O-RU's IPv4 address"; + } + leaf ru-ipv6-address { + type leafref { + path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/ip:ipv6/ip:address/ip:ip"; + } + description "O-RU's IPv6 address"; + } + mandatory true; + description "choice of O-RU IPv4 or IPv6 address"; + } + leaf o-du-ip-address { + type inet:ip-address; + mandatory true; + description "O-DU's IP address"; + } + leaf ru-ephemeral-udp-port { + type inet:port-number; + mandatory true; + description + "ephemeral port used by O-RU"; + } + leaf o-du-ephemeral-udp-port { + type inet:port-number; + mandatory true; + description + "ephemeral port used by O-DU"; + } + leaf ecpri-destination-udp { + type inet:port-number; + mandatory true; + description "the well-known UDP port number used by eCPRI"; + // fixme - add in a default when allocated by IANA + } + } + } + + grouping shared-cell-flow-group { + description "a grouping for shared cell O-RU flows"; + + container north-eth-flow { + when "../../../transport-session-type = 'SHARED-CELL-ETH-INTERFACE'"; + if-feature "SHARED_CELL"; + description "leafs for mac + vlan-id type data flows"; + leaf ru-mac-address { + type leafref { + path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:mac-address"; + } + description + "O-RU's MAC address used for Ethernet based flow"; + } + leaf vlan-id { + type leafref { + path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id"; + } + description + "O-RU's VLAN-ID used for Ethernet based flow"; + } + leaf north-node-mac-address { + type yang:mac-address; + description + "North-node's MAC address used for Ethernet based flow"; + } + } + container south-eth-flow { + when "../../../transport-session-type = 'SHARED-CELL-ETH-INTERFACE'"; + if-feature "SHARED_CELL"; + description "leafs for mac + vlan-id type data flows"; + leaf ru-mac-address { + type leafref { + path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:mac-address"; + } + description + "O-RU's MAC address used for Ethernet based flow"; + } + leaf vlan-id { + type leafref { + path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id"; + } + description + "O-RU's VLAN-ID used for Ethernet based flow"; + } + leaf south-node-mac-address { + type yang:mac-address; + description + "south-node's MAC address used for Ethernet based flow"; + } + } + } + + grouping session-and-markings-group { + description "a grouping with transport session type and enhanced uplane markings"; + leaf transport-session-type { type enumeration { enum ETH-INTERFACE { @@ -158,7 +333,7 @@ module o-ran-processing-element { case ipv4 { when "(../../transport-session-type = 'UDPIP-INTERFACE')"; leaf upv4-dscp-name { - if-feature o-ran-int:UDPIP-BASED-CU-PLANE; + if-feature o-ran-int:UDPIP-BASED-CU-PLANE; type leafref { path "/if:interfaces/if:interface/ip:ipv4/o-ran-int:diffserv-markings/o-ran-int:enhanced-uplane-markings/o-ran-int:up-marking-name"; } @@ -168,7 +343,7 @@ module o-ran-processing-element { case ipv6 { when "(../../transport-session-type = 'UDPIP-INTERFACE')"; leaf upv6-dscp-name { - if-feature o-ran-int:UDPIP-BASED-CU-PLANE; + if-feature o-ran-int:UDPIP-BASED-CU-PLANE; type leafref { path "/if:interfaces/if:interface/ip:ipv6/o-ran-int:diffserv-markings/o-ran-int:enhanced-uplane-markings/o-ran-int:up-marking-name"; } @@ -178,6 +353,21 @@ module o-ran-processing-element { } } } + } + + grouping pe-group { + description "a grouping of processing elements"; + leaf maximum-number-of-transport-flows { + type uint16 { + range "1..4094"; + } + default 4094; + config false; + description + "The maximum number of transport flows that can be supported by an O-RU"; + } + uses session-and-markings-group; + list ru-elements { key "name"; description @@ -193,6 +383,17 @@ module o-ran-processing-element { This name may be used in fault management to refer to a fault source or affected object"; } + leaf sro-id { + if-feature feat:SHARED-ORU-MULTI-OPERATOR; + type leafref { + path "/or-user:users/or-user:user/or-user:sro-id"; + } + description + "The identity of a shared resource operator. When present, + indicates that the list entry corresponds to a processing element + associated with a shared resource operator where the sro-id identifies + the specific shared resource operator"; + } container transport-flow { description "container for the transport-flow used for CU plane"; @@ -202,154 +403,66 @@ module o-ran-processing-element { } description "the interface name "; } - container aliasmac-flow { - when "../../../transport-session-type = 'ALIASMAC-INTERFACE'"; - if-feature o-ran-int:ALIASMAC-BASED-CU-PLANE; - description "leafs for virtual mac type data flows"; - leaf ru-aliasmac-address { - type leafref { - path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:alias-macs"; - } - mandatory true; - description - "O-RU's alias MAC address used for alias MAC based flow"; - } - leaf vlan-id { - type leafref { - path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id"; - } - description - "O-RU's VLAN-ID used for alias MAC based flow"; - } - leaf o-du-mac-address { - type yang:mac-address; - mandatory true; - description - "O-DU's MAC address used for alias MAC based flow"; + uses non-shared-cell-flow-group; + uses shared-cell-flow-group; + + } + } + list additional-transport-session-type-elements { + if-feature "feat:MULTIPLE-TRANSPORT-SESSION-TYPE"; + key transport-session-type; + + description + "Added to support multiple transport-session-type per O-RU, + it is always assumed that /processing-elements/ru-elements/ is configured with the first type of transport, + and /processing-element/additional-transport-session-elements/ru-elements/ is configured with the other types of transport. + If the O-RU is configured for shared-cell, this list will not be used. An O-RU shall reject any configuration + with a list entry with transport-session-type set to SHARED-CELL-ETH-INTERFACE"; + + uses session-and-markings-group; + + list ru-elements { + key "name"; + description + "the list of transport definitions for each processing element"; + leaf name { + type string { + length "1..255"; } + description + "A name that is unique across the O-RU that identifies a processing + element instance. + + This name may be used in fault management to refer to a fault source + or affected object"; } - container eth-flow { - when "../../../transport-session-type = 'ETH-INTERFACE'"; - description "leafs for mac + vlan-id type data flows"; - leaf ru-mac-address { - type leafref { - path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:mac-address"; - } - mandatory true; - description - "O-RU's MAC address used for Ethernet based flow"; - } - leaf vlan-id { - type leafref { - path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id"; - } - mandatory true; - description - "O-RU's VLAN-ID used for Ethernet based flow"; - } - leaf o-du-mac-address { - type yang:mac-address; - mandatory true; - description - "O-DU's MAC address used for Ethernet based flow"; + leaf sro-id { + if-feature feat:SHARED-ORU-MULTI-OPERATOR; + type leafref { + path "/or-user:users/or-user:user/or-user:sro-id"; } + description + "The identity of a shared resource operator. When present, + indicates that the list entry corresponds to a processing element + associated with a shared resource operator where the sro-id identifies + the specific shared resource operator"; } - container udpip-flow { - when "../../../transport-session-type = 'UDPIP-INTERFACE'"; - description "leafs for UDP/IP type data flows"; - choice address { - leaf ru-ipv4-address { - type leafref { - path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/ip:ipv4/ip:address/ip:ip"; - } - description "O-RU's IPv4 address"; - } - leaf ru-ipv6-address { - type leafref { - path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/ip:ipv6/ip:address/ip:ip"; - } - description "O-RU's IPv6 address"; - } - mandatory true; - description "choice of O-RU IPv4 or IPv6 address"; - } - leaf o-du-ip-address { - type inet:ip-address; - mandatory true; - description "O-DU's IPv address"; - } - leaf ru-ephemeral-udp-port { - type inet:port-number; - mandatory true; - description - "ephemeral port used by O-RU"; - } - leaf o-du-ephemeral-udp-port { - type inet:port-number; - mandatory true; - description - "ephemeral port used by O-DU"; - } - leaf ecpri-destination-udp { - type inet:port-number; - mandatory true; - description "the well known UDP port number used by eCPRI"; - // fixme - add in a default when allocated by IANA - } - } - container north-eth-flow { - when "../../../transport-session-type = 'SHARED-CELL-ETH-INTERFACE'"; - if-feature "SHARED_CELL"; - description "leafs for mac + vlan-id type data flows"; - leaf ru-mac-address { + container transport-flow { + description + "container for the transport-flow used for CU plane"; + leaf interface-name { type leafref { - path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:mac-address"; + path "/if:interfaces/if:interface/if:name"; } - description - "O-RU's MAC address used for Ethernet based flow"; - } - leaf vlan-id { - type leafref { - path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id"; - } - description - "O-RU's VLAN-ID used for Ethernet based flow"; - } - leaf north-node-mac-address { - type yang:mac-address; - description - "North-node's MAC address used for Ethernet based flow"; - } - } - container south-eth-flow { - when "../../../transport-session-type = 'SHARED-CELL-ETH-INTERFACE'"; - if-feature "SHARED_CELL"; - description "leafs for mac + vlan-id type data flows"; - leaf ru-mac-address { - type leafref { - path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:mac-address"; - } - description - "O-RU's MAC address used for Ethernet based flow"; - } - leaf vlan-id { - type leafref { - path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id"; - } - description - "O-RU's VLAN-ID used for Ethernet based flow"; - } - leaf south-node-mac-address { - type yang:mac-address; - description - "south-node's MAC address used for Ethernet based flow"; + description "the interface name "; } + uses non-shared-cell-flow-group; } } } } -// top level container +// top-level container container processing-elements { description diff --git a/radio/fhi_72/mplane/yang/models/o-ran-uplane-conf.yang b/radio/fhi_72/mplane/yang/models/o-ran-uplane-conf.yang index 16fbaf3335..c473e0e14c 100644 --- a/radio/fhi_72/mplane/yang/models/o-ran-uplane-conf.yang +++ b/radio/fhi_72/mplane/yang/models/o-ran-uplane-conf.yang @@ -13,12 +13,34 @@ module o-ran-uplane-conf { import o-ran-module-cap { prefix "mcap"; - revision-date 2020-08-10; } import o-ran-compression-factors { prefix "cf"; - revision-date 2020-08-10; + } + + import o-ran-wg4-features { + prefix "feat"; + } + + import ietf-hardware { + prefix "hw"; + } + + import o-ran-hardware { + prefix "or-hw"; + } + + import o-ran-usermgmt { + prefix "or-user"; + } + + import o-ran-common-yang-types { + prefix "o-ran-cmn"; + } + + import o-ran-delay-management { + prefix "or-dm"; } organization "O-RAN Alliance"; @@ -30,7 +52,7 @@ module o-ran-uplane-conf { "This module defines the module capabilities for the O-RAN Radio Unit U-Plane configuration. - Copyright 2020 the O-RAN Alliance. + Copyright 2024 the O-RAN Alliance. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -56,6 +78,198 @@ module o-ran-uplane-conf { contributors may be used to endorse or promote products derived from this software without specific prior written permission."; + revision "2024-08-12" { + description + "version 16.0.0 + + 1) add up-link performance improvement functionality, including DMRX-BF-EQ and DMS-BF-NEQ features + 2) Introduction of capabilities and configuration for SE27 O-DU controlled dimensionality reduction + 3) add support for se-20-multi-sd-punc-pattern + 4) add support for VSWR thresholds"; + + reference "ORAN-WG4.M.0-v16.00"; + } + + revision "2024-04-15" { + description + "version 15.0.0 + + 1) add support for sending prgSize in SE-21 with ST 5 and ST6. + 2) add support for for beamforming list and delay profiles(s) per endpoint. + 3) add support for user-group optimization. + 4) add support for multiple beamid tables. + 5) support pass band for filterindex09 per scs. + 6) support for M-plane based TRX control for Network Energy Saving. + 7) clarified cp-type is not applicable for ST3 or ST0 + 8) Changed description of max-beams-per-symbol/slot, so they refer to + 'different beamId values' instead of 'beams' + 9) added new leafs called max-beams-per-symbol-multiple-beamid-tables + and max-beams-per-slot-multiple-beamid-tables"; + + reference "ORAN-WG4.M.0-v15.00"; + } + + revision "2023-12-11" { + description + "version 14.0.0 + + 1) Corrected the configurable-tdd-pattern-supported leafref to point + to static-low-level-tx-endpoints + 2) Clarify reference for supported-frame-structures + 3) introduce max-prb-blks-per-sec-ext-23 and se-23-prb-block-mode-supported + 4) Introduction of new energy saving sub-use case - Data Layer control"; + + reference "ORAN-WG4.M.0-v14.00"; + } + + revision "2023-08-14" { + description + "version 13.0.0 + + 1) clarification for I/Q bitwidth parameter + 2) clarification of ul-fft-sampling-offset parameter + 3) adding availability status to carrier operational data and notifications + 4) ability to disable sequence number checking + 5) Introduction of TRX-Control and Advanced Sleep Mode for network energy savings"; + + reference "ORAN-WG4.M.0-v13.00"; + } + + revision "2023-04-10" { + description + "version 12.0.0 + + 1) Typographical corrections + 2) NB-IoT introduction + 3) clarify PRACH repetitions + 4) support for non-scheduled-ueid + 5) continuity bit information in Section Extension 11 + 6) U-Plane Processing Limits"; + + reference "ORAN-WG4.M.0-v12.00"; + } + + revision "2022-12-05" { + description + "version 11.0.0 + + 1) PRB range limit for SE 12 and HP section + 2) Introduction of PRB ranges + 3) uplane-only-dl-mode-enable + 4) Deprecation of power-related capabilities and addition of min-gain + 5) se 23 max mplane limits + 6) support Section Type 4 and associated commands"; + + reference "ORAN-WG4.M.0-v11.00"; + } + + revision "2022-08-15" { + description + "version 10.0.0 + + 1) clarified description statements for representative-t/rx-eaxc-id + 2) clarified description statements for max-num-t/rx-eaxc-ids-per-group + 4) clarified description statements for component carrier + 5) style guide corrections + 6) introduction of O-RU connector functionality + 7) fixing constraints + 8) introducing new feature for ACK NACK feedback + 9) added SHARED-ORU-MULTI-OPERATOR anf SHARED-ORU-MULTI-ODU features"; + + reference "ORAN-WG4.M.0-v10.00"; + } + + revision "2022-04-18" { + description + "version 9.0.0 + + 1) Descriptions fixed for max-beam-updates-per-slot / max-beam-updates-per-slot + 2) Added references to CUS-Plane spec for parameters 'frame-structure' and + 'supported-frame-structures' + 3) clarified max-beams-per-symbol, max-beams-per-slot, max-beam-updates-per-slot, + max-beam-updates-per-symbol parameters + 4) Clarification of cp-length and cp-length-other + 5) deprecation of rw-type and rw-duplex-scheme"; + + reference "ORAN-WG4.M.0-v09.00"; + } + + revision "2021-12-01" { + description + "version 8.0.0 + + 1) Various descriptions corrected and/or clarified + 2) Configuration for Beamforming weights were added together with changes and updates to compressions + 3) Added new leaf node 'beam-update-contention-control-limits-required' to 'endpoint-types' + to indicate endpoint capability to support beam update contention control processing limits. + 4) Added instance of 'beam-update-contention-control-limits' to 'endpoint-types'. + 5) Added new leaf node boolean flag 'beam-update-contention-control-enabled' for O-DU to indicate + support of feature BEAM-UPDATE-CONTENTION-CONTROL feature. + 6) deprecate 'channel-information-iq-bitwidth' , and define a new parameter + 'channel-information-bitwidth' under the container 'compression' + 7) add capability and configuration for channel information PRB granularity. + 8) Add support for multiple transport-session-type per O-RU. + 9) add capability and configuration for channel information compression. + 10) deprecate 'channel-information-iq-bitwidth' , and define a new parameter + 'channel-information-bitwidth' under the container 'compression'"; + + reference "ORAN-WG4.M.0-v08.00"; + } + + revision "2021-07-26" { + description + "version 7.0.0 + + 1) Added new grouping 'per-cplane-message-limits' containing params to limit + per C-Plane packet processing when CPLANE-MESSAGE-PROCESSING-LIMITS feature is + supported by O-RU and used by O-DU. + 2) Added new leaf node 'cplane-message-processing-limits-required' to 'endpoint-types' + to indicate endpoint capability to support C-Plane message processing limits. + 3) Added instances of 'per-cplane-message-limits' to 'endpoint-types' and + 'endpoint-capacity-sharing-groups'. + 4) Added new leaf node to low-level-tx/rx-endpoint 'cplane-message-processing-limits-enabled' + for O-DU to configure support for C-Plane limits + 5) Added t-da-offset and t-au-offset for external antenna support + 6) Added Channel IQ bitwidth for ST6 + 7) Added ordered-by user to leaf-list member-tx-eaxc-id and leaf-list member-rx-eaxc-id + 8) Added new PRACH formats + 9) Ensuring beam-ids can only be 15 bits + 10) Clarify mixed numerology restrictions"; + + reference "ORAN-WG4.M.0-v07.00"; + } + + + revision "2021-03-22" { + description + "version 6.0.0 + + 1) typographical corrections in descriptions. + 2) Updates to transmission window control. + 3) Introduces to existing grouping 'endpoint-section-capacity' two new leaf nodes 'max-uplane-section-header-per-symbol' and + 'max-uplane-section-header-per-slot' to limit U-Plane data section header addition/parsing. + 4) deprecated parameter 'max-remasks-per-section-id' since it is duplicate of leaf node 'max-control-sections-per-data-section'. + 5) introduces new leaf nodes 'max-beams-updates-per-slot' and ''max-beams-updates-per-symbol' + to grouping 'endpoint-beam-capacity' + 6) added new SRS features: + STATIC-TRANSMISSION-WINDOW-CONTROL + DYNAMIC-TRANSMISSION-WINDOW-CONTROL + UNIFORMLY-DISTRIBUTED-TRANSMISSION + ORDERED-TRANSMISSION + INDEPENDENT-TRANSMISSION-WINDOW-CONTROL"; + + reference "ORAN-WG4.M.0-v06.00"; + } + + revision "2020-12-10" { + description + "version 5.0.0 + + 1) introduces new coupling method coupling-via-frequency-and-time-with-priorities-optimized + 2) introduces new max-highest-priority-sections-per-slot parameter"; + + reference "ORAN-WG4.M.0-v05.00"; + } revision "2020-08-10" { description @@ -209,6 +423,18 @@ module o-ran-uplane-conf { description "5GNR PRACH Preamble format C2"; } + enum LTE-NB0-a { + description + "LTE Narrowband PRACH format 0-a"; + } + enum LTE-NB1-a { + description + "LTE Narrowband PRACH format 1-a"; + } + enum LTE-NB2 { + description + "LTE Narrowband PRACH format 2"; + } } description @@ -233,6 +459,270 @@ module o-ran-uplane-conf { description "Type definition for polarisations"; } + typedef beamforming-method-type { + type enumeration { + enum NOBF { + description + "No beamforming."; + } + enum PDBF { + description + "Predefined-beam beamforming."; + } + enum WDBF { + description + "Weight-based dynamic beamforming (based on real-time-updated weights)."; + } + enum ABBF { + description + "Attribute-based dynamic beamforming (based on real-time-updated beam attributes)."; + } + enum CIBF { + description + "Channel-information-based beamforming."; + } + enum DMRS_BF_EQ { + description + "DMRS-based beamforming with equalization."; + } + enum DMRS_BF_NEQ { + description + "DMRS-based beamforming without equalization."; + } + } + description + "Type definition for beamforming method."; + } + + typedef symb-reorder-capability { + type enumeration { + enum PER-TX-WIN-SYMB-REORDER-WITH-DMRS-SENDING { + description + "per-window symbol reordering when sending of port-reduced DMRS data is enabled, + please refer to clause 7.7.25.2 of the CUS-Plane Specification."; + } + enum PER-TX-WIN-SYMB-REORDER-WITHOUT-DMRS-SENDING { + description + "per-window symbol reordering when sending of port-reduced DMRS data is not enabled, + please refer to clause 7.7.25.2 of the CUS-Plane Specification."; + } + enum PER-SECTION-TX-WINDOW-REASSIGNMENT { + description + "per-section tx-window reassignment, please refer to clause 7.7.25.3 of the CUS-Plane + Specification."; + } + } + description + "Type definition for symbol reordering method supported."; + } + typedef symb-reorder-method { + type enumeration { + enum PER-TX-WIN-SYMB-REORDERG { + description + "per-window symbol reordering, + please refer to clause 7.7.25.2 of the CUS-Plane Specification."; + } + enum PER-SECTION-TX-WINDOW-REASSIGNMENT { + description + "per-section tx-window reassignment, please refer to clause 7.7.25.3 of the CUS-Plane + Specification."; + } + } + description + "Type definition for symbol reordering method configured."; + } + typedef up-SymbolId-type { + type enumeration { + enum OVER-AIR-SYMBOL-NUMBER { + description + "U-plane symbol Id interpretation as on-air symbol number for symbol reordering."; + } + enum TRANSMISSION-WINDOW-NUMBER { + description + "U-plane symbol Id interpretation as transmission window number for symbol reordering."; + } + } + description + "Type definition for symbol reordering symbol Id interpretation capability."; + } + + typedef user-group-self-assembly-mode { + type enumeration { + enum SELF-ASSEMBLY-WITH-GROUP-ID { + description + "The mode in which the O-RU is capable of assembling user groups using 'usrGroupId' and + 'usrGroupSize' fields in SE-24."; + } + enum SELF-ASSEMBLY-WITHOUT-GROUP-ID { + description + "The mode in which O-RU is capable of assembling user groups without using 'usrGroupId' and 'usrGroupSize' + fields in SE-24, but instead relying on PRB ranges of all UE layers overall all section for assembling + user groups."; + } + } + description + "Possible values for user group self assembly modes that the O-RU can support."; + } + + typedef rrm-meas-types { + type enumeration { + enum MEAS-UE-TAE { + value 0; + description + "UE timing advance error per UE, per slot"; + } + enum MEAS-UE-LAYER-POWER { + value 1; + description + "UE layer signal power per layer,per slot"; + } + enum MEAS-UE-FREQ-OFFSET { + value 2; + description + "UE frequency offset per UE, per slot"; + } + enum MEAS-IPN-ALLOC { + value 3; + description + "Interference plus Noise for allocated PRBs,per slot or per DMRS symbol group"; + } + enum MEAS-IPN-UNALLOC { + value 4; + description + "Interference plus Noise for unallocated PRBs, per slot or per set of symbols in a slot"; + } + enum MEAS-ANT-DMRS-SNR { + value 5; + description + "DMRS Signal to Noise Ratio per antenna, per layer. "; + } + } + description + "RRM measurement types, refer to CUS spec table 9.2.1-1 for details"; + } + + grouping transmission-buffering-capacity { + description + "UL U-plane buffering capacity available for scheduled transmission of UL U-plane messages"; + + list transmission-buffering-capacity { + description + "Buffering capacity for specific IQ format. + If the list does not contain an entry for particular IQ format than endpoint + or group of endpoints then buffering capacity for this IQ format is + max-buffered-prbs = 0 and max-buffered-symbols = 0."; + + uses cf:compression-details; + + leaf max-buffered-prbs { + type uint32; + description + "Max number of PRBs (for IQ format given in compression-details) that can be buffered by an endpoint + or buffered collectively by group of endpoints sharing capacity. This is divided between symbols buffered"; + } + + leaf max-buffered-symbols { + type uint32; + description + "Max number of symbols (for IQ format given in compression-details) that can be buffered by an endpoint + or buffered collectively by group of endpoints sharing capacity"; + } + } + } + + grouping mplane-trx-control-supp-antenna-masks { + description + "A grouping containing list of antenna masks supported by the O-RU"; + + list mplane-supported-trx-control-masks { + key "index"; + description + "List of supported M-Plane controlled TRX control masks."; + + leaf index { + type uint8; + description + "Index to the list of antenna masks"; + } + + leaf antenna-mask { + type binary; + description + "M-Plane controlled O-RU supported antenna mask value."; + } + } + } + grouping array-config-group { + description + "This group represents configuration for supported arrays, + e.g. mplane trx control mask for supported arrayes"; + + list tx-array-antenna-mask-config { + if-feature feat:MPLANE-TRX-CONTROL; + key array-name; + description + "List of mplane configured trx antenna mask for supported arrays"; + + leaf array-name { + type leafref { + path "/user-plane-configuration/tx-arrays/name"; + require-instance false; + } + description + "Unique name of tx-array. "; + } + leaf antenna-bitmask { + type binary; + description + "If mplane-supported-trx-control-masks list is not advertised as part of capability for the referred [tr]x-array,parameter antenna-bitmask may be used for configuration. + The parameter antenna-bitmask-index shall not be used for configuration. + The antenna mask contains as many binary values as number of array elements associated + with respective tx or rx-array. Value of '1' indicates antenna element shall be + kept active and value of '0' indicates antenna element should be turned inactive. + Refer clause 20.3.2 of M-Plane specification."; + } + leaf antenna-bitmask-index { + type uint8; + description + "If mplane-supported-trx-control-masks list is advertised as part of capability for the referred [tr]x-array, + the parameter antenna-bitmask-index may be used for configuration to refer to the index of advertised mplane-supported-trx-control-masks. + The parameter antenna-bitmask shall not be used for configuration."; + } + } + list rx-array-antenna-mask-config { + if-feature feat:MPLANE-TRX-CONTROL; + key array-name; + description + "List of mplane configured trx antenna mask for supported arrays"; + leaf array-name { + type leafref { + path "/user-plane-configuration/rx-arrays/name"; + require-instance false; + } + description + "Unique name of rx-array. "; + } + + leaf antenna-bitmask { + type binary; + description + "If mplane-supported-trx-control-masks list is not advertised as part of capability for the referred [tr]x-array,parameter antenna-bitmask may be used for configuration. + The parameter antenna-bitmask-index shall not be used for configuration. + The antenna mask contains as many binary values as number of array elements associated + with respective tx or rx-array. Value of '1' indicates antenna element shall be + kept active and value of '0' indicates antenna element should be turned inactive. + Refer clause 20.3.2 of M-Plane specification."; + } + leaf antenna-bitmask-index { + type uint8; + description + "If mplane-supported-trx-control-masks list is advertised as part of capability for the referred [tr]x-array, + the parameter antenna-bitmask-index may be used for configuration to refer to the index of advertised mplane-supported-trx-control-masks. + The parameter antenna-bitmask shall not be used for configuration."; + } + } + } + grouping general-config { description "a group for general configuration"; @@ -243,8 +733,8 @@ module o-ran-uplane-conf { type boolean; default false; description - "Informs if regularizationfactor in section extension is configured(true) or not(false), this - leaf indicates whether the O-DU will send the regularizationfactor in section extension. + "Informs if regularization factor in section extension is configured(true) or not(false), this + leaf indicates whether the O-DU will send the regularization factor in section extension. If the O-RU does not support regularization-factor-se-supported in o-ran-module-cap.yang, this leaf is ignored."; @@ -260,7 +750,204 @@ module o-ran-uplane-conf { for all UL and DL C/U plane data flows. If little endian byte order is NOT supported, this node is ignored - (and the default bigendian byte order used)"; + (and the default big endian byte order used)"; + } + + leaf uplane-only-dl-mode-enable { + if-feature feat:UPLANE-ONLY-DL-MODE; + type boolean; + default false; + description + "If set to true, enable IQ data transfer without C-Plane in DL for all + endpoints as specified in the CUS specification clause 8.2.2."; + } + + leaf st4-for-time-domain-beamforming-weights-enabled { + if-feature feat:ST4-SLOT-CONFIG-MSG-SUPPORT; + type boolean; + default false; + description + "If O-RU indicates support for Section Type 4 command 'TIME_DOMAIN_BEAM_CONFIG' , O-DU may + configure the O-RU to use Section Type 4 TIME_DOMAIN_BEAM_CONFIG command by setting this + flag to 'true'. In which case, O-DU shall use only Section Type 4 TIME_DOMAIN_BEAM_CONFIG + for specifying time-domain beam information. refer to clause 12.4.3.4.4 of CUS-Plane spec."; + } + + leaf seq-id-checking-disabled { + if-feature feat:SEQ-ID-CHECKING-CONFIGURABLE; + type boolean; + default false; + description + "When the parameter has value true, sequence number checking is disabled."; + } + + leaf multiple-beamId-tables-support-enabled { + if-feature feat:MULTIPLE-BEAMID-TABLES-SUPPORTED; + type boolean; + default false; + description + "When the parameter has value true, O-DU will be able to use multiple beamId table capability of O-RU. + O-DU shall be able to associate specific [tr]x-array-carrier with specific beamId table indexes advertised + which are advertised in 'tx-array-beamId-table-indexes/rx-array-beamId-table-indexes' as part of tx-array + and rx-array capabilities."; + } + + leaf-list energy-sharing-groups-disabled { + if-feature feat:MPLANE-DATA-LAYER-CONTROL; + type uint8; + description + "A list that indicates which energy sharing groups are currently switched off due to data layer control energy saving. + By default this list is empty and means that no data layer control energy saving is currently ongoing. + This value shall be specific values from the list of 'energy-sharing-group-id' advertised by O-RU + inside respective static-low-level-[tr]x-endpoints."; + } + + leaf config-symb-reorder-method { + if-feature "feat:DMRS-BF-EQ or feat:DMRS-BF-NEQ"; + type symb-reorder-method; + description + "Indicated which symbol reordering method is used. If it's not configured, O-RU shall + operate with natural order."; + } + leaf config-symb-reorder-upSymbId-type { + when "../config-symb-reorder-method = 'PER-TX-WIN-SYMB-REORDERG'"; + if-feature "feat:DMRS-BF-EQ or feat:DMRS-BF-NEQ"; + type leafref { + path "../../endpoint-types/supported-symb-reorder-capabilities/up-symbolId-type-supported"; + require-instance false; + } + description + "Indicated which config-symb-reorder-upSymbId-type intended to be used for symbol reordering, + i.e. either 'on-air symbol number' or 'transmission window number' to the O-RU. + When 'per-section tx-window reassignment' method is used, + the U-Plane symbolId interpretation shall be configured as 'on-air symbol number' + Please refer to clause 7.7.25 in CUS plane for details."; + } + + leaf ueid-persistence-enabled { + if-feature feat:UEID-PERSISTENCE; + type boolean; + description + "Indicates if ueId persistance over multiple slots is enabled. + See CUS-Plane clause 7.7.24.10."; + } + + leaf ueid-layer-bits-configured { + if-feature "feat:DMRS-BF-EQ or feat:DMRS-BF-NEQ"; + type uint8; + description + "This parameter specified the number of bits configured by the O-DU for specifying ueid layers."; + } + + container vswr-thresholds{ + if-feature feat:CONFIGURABLE-VSWR-THRESHOLDS; + description "VSWR thresholds parameters."; + container default-vswr-thresholds { + config false; + leaf default-vswr-minor-threshold { + type decimal64 { + fraction-digits 1; + } + description + "Default VSWR minor threshold."; + } + leaf default-vswr-major-threshold { + type decimal64 { + fraction-digits 1; + } + description + "Default VSWR major threshold."; + } + description + "Default VSWR thresholds used by this O-RU"; + } + container vswr-thresholds-range { + config false; + leaf min { + type decimal64 { + fraction-digits 1; + } + description + "The lowest allowed VSWR threshold"; + } + leaf max { + type decimal64 { + fraction-digits 1; + } + description + "The highest allowed VSWR threshold"; + } + description + "VSWR thresholds shall be configured within this range"; + } + container configurable-vswr-thresholds{ + description "vswr monitoring parameters to be configured by O-RU Controller"; + leaf global-vswr-monitoring-trigger-power { + type decimal64 { + fraction-digits 1; + } + units dBm; + description + "Power threshold for enabling VSWR monitoring."; + } + leaf global-vswr-minor-threshold { + type decimal64 { + fraction-digits 1; + } + description + "configured VSWR minor threshold which will be used to trigger an alarm."; + } + leaf global-vswr-major-threshold { + type decimal64 { + fraction-digits 1; + } + description + "configured VSWR major threshold which will be used to trigger an alarm."; + } + list vswr-threshold-per-tx-array-element { + key "tx-array-name array-element"; + description + "individual vswr-thresholds configuration which will be applied to each tx array element"; + leaf tx-array-name { + type leafref { + path "/o-ran-uplane-conf:user-plane-configuration/o-ran-uplane-conf:tx-arrays/o-ran-uplane-conf:name"; + require-instance false; + } + description + "tx-array where the configured vswr thresholds will be applied"; + } + leaf array-element { + type uint16; + description + "the array-element number of the tx-array if there are multiple array-elements of the tx-array."; + } + leaf vswr-minor-threshold { + type decimal64 { + fraction-digits 1; + } + description + "configured VSWR minor threshold of the tx-array-element which will be used to trigger an alarm. + It shall override the configured global-vswr-minor-tresholds."; + } + leaf vswr-major-threshold { + type decimal64 { + fraction-digits 1; + } + description + "configured VSWR major threshold of the tx-array-element which will be used to trigger an alarm. + It shall override the configured global-vswr-major-tresholds."; + } + leaf vswr-monitoring-trigger-power { + type decimal64 { + fraction-digits 1; + } + units dBm; + description + "Power threshold for enabling VSWR monitoring. + it shall override the configured global-vswr-monitoring-trigger-power."; + } + } + } } } } @@ -389,15 +1076,21 @@ module o-ran-uplane-conf { description "Coupling via frequency and time with priorities. Can be used when coupling-via-frequency-and-time-with-priorities = true in o-ran-module-cap.yang"; } + enum FREQUENCY_AND_TIME_WITH_PRIORITIES_OPTIMIZED { + description "Coupling via frequency and time with priorities optimized. Can be used when + coupling-via-frequency-and-time-with-priorities-optimized = true in o-ran-module-cap.yang"; + } } default NORMAL; description "Method of coupling between C-Plane and U-Plane messages; see methods of coupling - of C-Plane and U-Plane in CUS-Plane specification"; + of C-Plane and U-Plane in CUS-Plane specification. If an O-RU doesn't support the + necessary capability as defined in o-ran-module-cap.yang, the O-RU shall respond + with rpc-error including an error-message describing the error condition"; } } - grouping eaxc { + grouping eaxc { description "One eAxC identifier (eAxC ID) comprises a band and sector identifier (BandSector_ID), a component-carrier identifier (CC_ID) and a @@ -409,11 +1102,11 @@ module o-ran-uplane-conf { Bit allocation is subdivided as follows: * O_DU_Port_ID: Used to differentiate processing units at O-DU * BandSector_ID: Aggregated cell identifier - * CC_ID: distinguishes Carrier Components + * CC_ID: distinguishes Component Carriers * RU_Port_ID: Used to differentiate spatial streams or beams on the O-RU The bitwidth of each of the above fields is variable this model is supposed to check - if we are occpying bits continuously but we do not have to occupy all 16 bits"; + if we are occupying bits continuously, but we do not have to occupy all 16 bits"; leaf o-du-port-bitmask { @@ -486,7 +1179,7 @@ module o-ran-uplane-conf { } units Meter; - description "Average distance between centers of nearby AE in horizontal direction (in array coordinates system)"; + description "Average distance between centres of nearby AE in horizontal direction (in array coordinates system)"; } leaf vertical-spacing{ @@ -495,7 +1188,7 @@ module o-ran-uplane-conf { } units Meter; - description "Average distance between centers of nearby AE in vertical direction (in array coordinates system)"; + description "Average distance between centres of nearby AE in vertical direction (in array coordinates system)"; } container normal-vector-direction { @@ -580,6 +1273,26 @@ module o-ran-uplane-conf { array is serving for. Intended use is to deal with multiband solutions."; } + + list related-o-ru-connectors { + key name; + + leaf name { + type leafref { + path '/hw:hardware/hw:component/hw:name'; + } + must "derived-from-or-self(deref(current())/../hw:class, 'or-hw:O-RU-ANTENNA-CONNECTOR')"; + description + "Referenced name of O-RU connector used by the antenna array."; + } + leaf-list array-element-id { + type uint16; + description + "Identifiers of array element(s) related to specific O-RU connector. Allowed values and semantic - as per CUS-Plane spec, clause 'Identification and Ordering of Array Elements'"; + } + description + "List of unique names that identify instances of O-RU's connectors the external [tr]x-array is reachable through."; + } } grouping array-choice { @@ -591,7 +1304,7 @@ module o-ran-uplane-conf { } description - "Leafref to tx array if such is choosen"; + "Leafref to tx array if such is chosen"; } } case rx { @@ -601,7 +1314,7 @@ module o-ran-uplane-conf { } description - "Leafref to rx array if such is choosen"; + "Leafref to rx array if such is chosen"; } } description @@ -619,10 +1332,7 @@ module o-ran-uplane-conf { type uint8; description - "This parameter defines the frame structure. The first 4 bits define the FFT/iFFT size - being used for all IQ data processing related to this message. - The second 4 bits define the sub carrier spacing as well as the number of slots per 1ms sub-frame - according to 3GPP TS 38.211, taking for completeness also 3GPP TS 36.211 into account"; + "This parameter provides value of frame structure. For semantics and allowed values see CUS-Plane Specification, clause 'frameStructure (frame structure)'"; } leaf cp-type { @@ -639,7 +1349,10 @@ module o-ran-uplane-conf { } description - "Provides type of CP (cyclic prefix) if section type 3 is not used or type of CP cannot be determined from cpLength."; + "Provides type of CP (cyclic prefix). + + For section type 3 or section type 0, element and the value of this leaf shall be ignored by the O-RU. In such circumstances, + please refer to clause 7.5.2.14 cpLength (cyclic prefix length) of the CUS-Plane Specification."; } leaf cp-length { @@ -647,8 +1360,15 @@ module o-ran-uplane-conf { units Ts; mandatory true; description - "Used for symbol 0 for NR & LTE, and symbol 7*2u for NR. - See CUS-plane"; + "Provides length of CP used for the first symbol after subframe boundary or half-subframe boundary. + See CUS-plane + + If a section type 3 is used in C-plane messages addressed to the endpoint, then O-RU takes length of CP from + C-Plane cpLength Information Element and the value of this leaf shall be ignored by the O-RU. See cpLength (cyclic prefix length) + in CUS-Plane for details. + + If a static configuration (see static-prach-configurations) is applicable to the endpoint, then O-RU takes + length of CP from M-Plane cp-length leaf in static-prach-configurations and the value of this leaf shall be ignored by the O-RU."; } leaf cp-length-other { @@ -656,14 +1376,26 @@ module o-ran-uplane-conf { units Ts; mandatory true; description - "Used for other symbols than by cp-length above"; + "Provides length of CP used for symbols other than provided by cp-length. + + If a section type 3 is used in C-plane messages addressed to the endpoint, then O-RU takes length of CP from + C-Plane cpLength Information Element and the value of this leaf shall be ignored by the O-RU. See cpLength (cyclic prefix length) + in CUS-Plane for details. + + If a static configuration (see static-prach-configurations) is applicable to the endpoint, then O-RU takes + length of CP from M-Plane cp-length leaf in static-prach-configurations and the value of this leaf shall be ignored by the O-RU."; } leaf offset-to-absolute-frequency-center { type int32; mandatory true; description - "This provides value of freqOffset to be used if section type 3 is not used. See freqOffset in CUS-plane."; + "This provides value of freqOffset to be used if section type 3 is not used. See freqOffset in CUS-plane. + offset-to-absolute-frequency-center defines the relative spacing between the centre of RE#0 of RB#0 to the center-of-channel-bandwidth. + If offset-to-absolute-frequency-center for NR with SCS=15kHz is odd, + it implies that 7.5kHz shift as indicated by the 3GPP parameter frequencyShift7p5khz for UL is applied, + which means enable the NR UL transmission with a 7.5 kHz shift to the LTE raster, as defined in 3GPP TS 38.331. + When type (in tx/rx-array-carrier) is DSS, 'offset-to-absolute-frequency-center' indicates the 1st RE of 1st RB of NR."; } list number-of-prb-per-scs { @@ -684,23 +1416,53 @@ module o-ran-uplane-conf { description "Determines max number of PRBs that will be used in all sections per one symbol. This is affecting allocation of resources to endpoint. Value shall not exceed constrains - defined by max-prb-per-symbol of endpoint type. In addition sum (over all used epoints - within a group of endpoints sharing resources) of number-of-prb rounded up to + defined by max-prb-per-symbol of endpoint type. In addition, sum (over all used endpoints + within a group of endpoints sharing resources) of number-of-prb rounded up to the nearest value from prb-capacity-allocation-granularity shall not exceed max-prb-per-symbol of the group."; } } } + grouping occupied-bandwidth { + description + "Grouping describing occupied bandwidth of a carrier."; + + container occupied-bandwidth { + description + "Container describing occupied bandwidth of a carrier. + + Information conveyed in this container can be used to determine minimum filter bandwidth in case + it cannot be fully determined basing on the carrier bandwidth itself e.g., when NB-IoT carriers + are allocated in the carrier's guard band."; + + leaf lower-bound { + type uint64; + units Hz; + + description + "Lower bound of the filter pass bandwidth of a carrier."; + } + + leaf upper-bound { + type uint64; + units Hz; + + description + "Upper bound of the filter pass bandwidth of a carrier."; + } + } + } + grouping tx-common-array-carrier-elements { description - "This grouping containes all common parameters for tx-array-carriers and rx-array-carriers"; + "This grouping contains all common parameters for tx-array-carriers and rx-array-carriers"; leaf absolute-frequency-center { type uint32; - mandatory true; + status deprecated; description - "Absolute Radio Frequency Channel Number - indirectly indicates RF center carrier frequency of signal. - Reflected in arfcn."; + "deprecated in version 5.0.0 and replaced with offset-to-absolute-frequency-center + and common FREF translated from absolute-frequency-center (NREF) as defined in 38.104"; } leaf center-of-channel-bandwidth { @@ -708,7 +1470,10 @@ module o-ran-uplane-conf { units Hz; mandatory true; description - "Center frequency of channel bandwidth in Hz. Common for all numerologies."; + "Centre frequency of channel bandwidth in Hz. Common for all numerologies. + center-of-channel-bandwidth is the FREF translated from absolute-frequency-center (NREF) as per 3GPP TS 38.104. + Refer to Table 5: Centre Bandwidth Calculation in M-plane spec for calculation of this parameter + for even and odd number of PRBs in LTE, NR and DSS carrier configuration."; } leaf channel-bandwidth { @@ -738,9 +1503,9 @@ module o-ran-uplane-conf { default INACTIVE; description - "Indicates if transmission is enabled for this array carriers. Note that Netconf server uses state parameter + "Indicates if transmission is enabled for this array carriers. Note that NETCONF server uses state parameter to indicate actual state of array carriers operation. When array carriers is in sleep status, - Netconf server rejects all other operation request to tx-array-carriers object except either request to change from sleep + NETCONF server rejects all other operation request to tx-array-carriers object except either request to change from sleep to active status or delete MO operation (see 4.8) to the object."; } @@ -766,6 +1531,13 @@ module o-ran-uplane-conf { "Indicates state of array carriers activation operation"; } + leaf-list availability-status { + type o-ran-cmn:availability-status; + config false; + description + "indicates availability-status of array carrier"; + } + leaf type { type enumeration { enum NR { @@ -807,29 +1579,43 @@ module o-ran-uplane-conf { path "/user-plane-configuration/tx-array-carriers[name=current()/../name]" + "/duplex-scheme"; require-instance false; } + status deprecated; description - "Config true type of duplex scheme."; + "Config true type of duplex scheme. + Status is deprecated because 'lte-tdd-frame' container, where it was used, is also deprecated. + Additionally duplex-scheme is derived from the band-number."; } leaf rw-type { type leafref { path "/user-plane-configuration/tx-array-carriers[name=current()/../name]" + "/type"; require-instance false; } + status deprecated; description - "Config true type of carrier."; + "Config true type of carrier. + Status is deprecated because leafref is to a 'config true' schema-node"; + } + uses occupied-bandwidth; + + leaf configured-tx-beamId-table-index { + if-feature feat:MULTIPLE-BEAMID-TABLES-SUPPORTED; + type uint8; + description + "The value of beamId table index configured to be used by the O-DU for a given tx-array-carrier. + The value shall be chosen from the list 'tx-array-beamId-table-indexes' advertised by the O-RU."; } } grouping rx-common-array-carrier-elements { description - "This grouping containes all common parameters for tx-array-carriers and rx-array-carriers"; + "This grouping contains all common parameters for tx-array-carriers and rx-array-carriers"; leaf absolute-frequency-center { type uint32; - mandatory true; + status deprecated; description - "Absolute Radio Frequency Channel Number - indirectly indicates RF center carrier frequency of signal. - Reflected in arfcn."; + "deprecated in version 5.0.0 and replaced with offset-to-absolute-frequency-center + and common FREF translated from absolute-frequency-center (NREF) as defined in 38.104"; } leaf center-of-channel-bandwidth { @@ -837,7 +1623,10 @@ module o-ran-uplane-conf { units Hz; mandatory true; description - "Center frequency of channel bandwidth in Hz. Common for all numerologies."; + "Centre frequency of channel bandwidth in Hz. Common for all numerologies. + center-of-channel-bandwidth is the FREF translated from absolute-frequency-center (NREF) as per 3GPP TS 38.104. + Refer to Table 15.2.4-1: Centre Bandwidth Calculation in M-plane spec for calculation of this parameter + for even and odd number of PRBs in LTE, NR and DSS carrier configuration."; } leaf channel-bandwidth { @@ -867,9 +1656,9 @@ module o-ran-uplane-conf { default INACTIVE; description - "Indicates if transmission is enabled for this array carriers. Note that Netconf server uses state parameter + "Indicates if transmission is enabled for this array carriers. Note that NETCONF server uses state parameter to indicate actual state of array carriers operation. When array carriers is in sleep status, - Netconf server rejects all other operation request to tx-array-carriers object except either request to change from sleep + NETCONF server rejects all other operation request to tx-array-carriers object except either request to change from sleep to active status or delete MO operation (see 4.8) to the object."; } @@ -895,6 +1684,13 @@ module o-ran-uplane-conf { "Indicates state of array carriers activation operation"; } + leaf-list availability-status { + type o-ran-cmn:availability-status; + config false; + description + "indicates availability-status of array carrier"; + } + leaf type { type enumeration { enum NR { @@ -931,6 +1727,15 @@ module o-ran-uplane-conf { description "Type of duplex scheme O-RU supports."; } + uses occupied-bandwidth; + + leaf configured-rx-beamId-table-index { + if-feature feat:MULTIPLE-BEAMID-TABLES-SUPPORTED; + type uint8; + description + "The value of beamId table index configured to be used by the O-DU for a given rx-array-carrier. + The value shall be chosen from list 'rx-array-beamId-table-indexes' advertised by the O-RU."; + } } grouping endpoint-section-capacity { @@ -956,38 +1761,196 @@ module o-ran-uplane-conf { "Max number of sections within one slot that can be processed by endpoint or processed collectively by group of endpoints sharing capacity."; } + leaf max-highest-priority-sections-per-slot { + type uint16; + must "current() < ../max-sections-per-slot" { + error-message "the sectionID for the highest priority needs to be less than the max sectionIds per slot"; + } + description + "Max number of highest priority sections within one slot that can be processed by endpoint or processed collectively by + group of endpoints sharing capacity. This leaf applies only when coupling-via-frequency-and-time-with-priorities-optimized + is true, in other instances this leaf is ignored by the O-RU. The sectionId for highest priority section descriptions shall + start from 0 to specified max value and is a subset of max-sections-per-slot and must be less than max-sections-per-slot."; + } leaf max-remasks-per-section-id { type uint8 { range "1..12"; } default 12; + status deprecated; description "maximum number of different reMask values that is applied to a PRB within one section id. This value can be processed by endpoint - or processed collectively by group of endpoints sharing capacity"; + or processed collectively by group of endpoints sharing capacity. + This parameter is duplicated max-control-sections-per-data-section, hence being deprecated."; + } + leaf max-uplane-section-header-per-symbol { + type uint16; + description + "Maximum number of U-Plane data section header within one symbol that can be processed by an endpoint or processed collectively by group of + endpoints sharing capacity. Clarification for multiple U-Plane data section headers per C-Plane section description is specified in sectionId IE + description in CUS-Plane specification. For the cases where this optional parameter is not populated by O-RU or an older version O-DU does not + interpret this parameter, O-DU may optionally use max-sections-per-symbol as the default value to limit of U-Plane data section messages which + can be processed by the O-RU. If none of the above limitations are supported, O-RU and O-DU may rely on offline negotiation. + As an example, this parameter can be used to limit O-RU processing while using Section extension type#6 and #12, which are used for non-contiguous + PRB allocation, and hence results in multiple U-Plane data section headers for a given C-Plane section description."; + } + leaf max-uplane-section-header-per-slot { + type uint16; + description + "Maximum number of U-Plane data section headers within one slot that can be processed by an endpoint or processed collectively by group of + endpoints sharing capacity. Clarification for multiple U-Plane data section headers per C-Plane section description is specified in sectionId IE + description in CUS-Plane specification. For the cases where this optional parameter is not populated by O-RU or an older version O-DU does not + interpret this parameter, O-DU may optionally use max-sections-per-slot as the default value to limit of U-Plane data section messages which can + be processed by the O-RU. If none of the above limitations are supported, O-RU and O-DU may rely on offline negotiation. + As an example, this parameter can be used to limit O-RU processing while using Section extension type#6 and #12, which are used for non-contiguous + PRB allocation, and hence results in multiple U-Plane data section headers for a given C-Plane section description."; } description - "Parameters describing section capacity where section is undestood as number of different sectionId values"; + "Parameters describing section capacity where section is understood as number of different sectionId values. Additionally, max limits for U-Plane data sections + refer to U-Plane data section headers"; + } + + grouping endpoint-section-extension-limits { + leaf max-mcscaleremask-per-prb { + type uint16; + description + "This parameter limits the number of sets of {mcScaleReMask, csf, mcScaleOffset} values per symPrbPattern the O-RU can support when SE 23 is used in a + data section description. Refer to clause 7.7.23.1 of the CUS-Plane specification."; + } + leaf max-prb-ranges-per-sec-ext-12 { + type uint16; + description + "Optional parameter reported by O-RU which applies to Section Extension 12 and specifies + the maximum number of frequency ranges R per section description supported by O-RU. + If this limit is exceeded by the O-DU, the O-RU may send alarm notification with + 'fault-id = 31' to subscribers following the procedures defined in Clause 11 and + Table A.1-1 of O-RAN.WG4.MP.0. + As an example, the O-RU can include the following in the corresponding active-alarm list + entry and alarm-notification message: + Error C-Plane message + Maximum number of frequency ranges in SE-12 per section description exceeded + Reduce frequency ranges per section description used in SE-12"; + } + leaf max-freq-hops-per-sec-ext-13 { + type uint16; + description + "Optional parameter reported by O-RU which applies to Section Extension 13 and specifies + the maximum number of frequency hops R per section description supported by O-RU. + If this limit is exceeded by the O-DU, the O-RU may send alarm notification with + 'fault-id = 31' to subscribers following the procedures defined in Clause 11 and + Table A.1-1 of O-RAN.WG4.MP.0. + As an example, the O-RU can include the following in the corresponding active-alarm list + entry and alarm-notification message: + Error C-Plane message + Maximum number of frequency hops in SE 13 per section description exceeded + Reduce frequency hops per section description used in SE-13"; + } + + leaf max-prb-blks-per-sec-ext-23 { + if-feature feat:SE23-PRB-BLOCK-MODE-SUPPORT; + type uint16; + description + "Optional parameter reported by O-RU which applies to Section Extension 23 and specifies + the maximum number of prb blocks (Refer Clause 7.7.23.1 of CUS-Plane spec prb block) per + section description supported by O-RU. + If this limit is exceeded by the O-DU, the O-RU may send alarm notification with 'fault-id = 31' + to subscribers following the procedures defined in Clause 11 and + Table A.1-1 of O-RAN.WG4.MP.0. + As an example, the O-RU can include the following in the corresponding active-alarm list + entry and alarm-notification message: + Error C-Plane message + Maximum number of prb blocks in SE-23 per section description exceeded + Reduce prb blocks per section description used in SE-23"; + } + + description + "Parameters describing C-Plane section extension limits/capacities."; } grouping endpoint-beam-capacity { leaf max-beams-per-symbol { - type uint16; + type uint16 { + range "min .. 32767"; + } description - "Max number of beams within one symbol that can be processed by endpoint - or processed collectively by group of endpoints sharing capacity"; + "Max number of different beamId values within one symbol that can be processed by endpoint + or processed collectively by group of endpoints sharing capacity + + If the parameter is absent or if value 0 is reported for the parameter, + then the endpoint does not support beamforming operation. + + The parameter doesn't apply if multiple-beamId-tables-support-enabled is set to true. + In such a case, max-beams-per-symbol-multiple-beamid-tables applies."; } leaf max-beams-per-slot { - type uint16; + type uint16 { + range "min .. 32767"; + } description - "Max number of beams within one slot that can be processed by endpoint - or processed collectively by group of endpoints sharing capacity"; + "Max number of different beamId values within one slot that can be processed by endpoint + or processed collectively by group of endpoints sharing capacity + + If the parameter is absent or if value 0 is reported for the parameter, + then the endpoint does not support beamforming operation. + + The parameter doesn't apply if multiple-beamId-tables-support-enabled is set to true. + In such a case, max-beams-per-slot-multiple-beamid-tables applies."; + } + leaf max-beams-per-symbol-multiple-beamid-tables { + if-feature feat:MULTIPLE-BEAMID-TABLES-SUPPORTED; + type uint32; + description + "Max number of different beamId values within one symbol that can be processed by endpoint + or processed collectively by group of endpoints sharing capacity. + beamId values associated with different beamId tables are always considered different. + + This parameter only applies if multiple-beamId-tables-support-enabled is set to true. + If multiple-beamId-tables-support-enabled is set to false, then max-beams-per-symbol applies."; + } + leaf max-beams-per-slot-multiple-beamid-tables { + if-feature feat:MULTIPLE-BEAMID-TABLES-SUPPORTED; + type uint32; + description + "Max number of different beamId values within one slot that can be processed by endpoint + or processed collectively by group of endpoints sharing capacity. + beamId values associated with different beamId tables are always considered different. + + This parameter only applies if multiple-beamId-tables-support-enabled is set to true. + If multiple-beamId-tables-support-enabled is set to false, then max-beams-per-slot applies."; + } + leaf max-beam-updates-per-slot { + type uint16 { + range "min .. 32767"; + } + description + "Max number of beams within one slot that can be updated by endpoint + or processed collectively by group of endpoints sharing capacity + + If the parameter is absent or if value 0 is reported for the parameter, + then the endpoint does not support beamforming operation."; + } + leaf max-beam-updates-per-symbol { + type uint16 { + range "min .. 32767"; + } + description + "Max number of beams within one symbol that can be updated by endpoint + or processed collectively by group of endpoints sharing capacity + + If the parameter is absent or if value 0 is reported for the parameter, + then the endpoint does not support beamforming operation."; } description - "Parameters describing beam capacity where number of beams is understood as number of different beamId values"; + "Parameters describing beam capacity where number of beams is understood as number of different beamId values. + Parameters defined for beam updates shall be used to put a maximum limit on number of beam/beamIds which can be updated + every slot/symbol. Note: This parameter does not put any restriction on which beams/beamIds can be updated. + + If the parameters defining beam capacity and beam updates are absent or if value 0 is reported for the parameters + defining beam capacity and beam updates, then the endpoint does not support beamforming operation."; } grouping endpoint-prb-capacity { @@ -998,6 +1961,25 @@ module o-ran-uplane-conf { or processed collectively by group of endpoints sharing capacity"; } + leaf max-prb-ranges-per-symbol { + type uint32; + description + "Optional parameter reported by O-RU which indicates the maximum number of PRB ranges + that can be processed by single endpoint or processed collectively by group of endpoints + sharing capacity within one symbol. For details please see O-RAN WG4 CUS-Plane specification, + Clause 7.8.2.1.2 'Limits of number of PRB ranges'. + + In case CU-Plane traffic generated by the O-DU exceeds the limit, an O-RU is responsible + for sending alarm notification with 'fault-id = 31' to subscribers following the procedures + defined in Clause 11 and Table A.1-1 of O-RAN.WG4.MP.0. + + As an example, the O-RU can include the following in the corresponding active-alarm list + entry and alarm-notif message: + Error C-Plane message + max-prb-ranges-per-symbol exceeded + Reduce value of number of PRB ranges used per symbol"; + } + description "Attributes presenting processing capacity related to PRB."; } @@ -1017,6 +1999,111 @@ module o-ran-uplane-conf { is set to true."; } + grouping per-cplane-message-limits { + leaf max-beams-per-cplane-message { + type uint16; + description + "Maximum number of beams which can be received in one single C-Plane message. Number of beams, + may include only beamIds, only beam weights or mix of beamIds and beamweights being received by + an O-RU in a single C-Plane message. + To avoid application layer fragmentation of C-Plane message across the fronthaul, O-RU should + avoid setting this to a very low value. + As a usage scenario, an O-RU may choose to define only a few quantized values e.g. (128, 256). + If the value is set to 0 by O-RU, the O-DU need not restrict the number of beams it can send + in single C-Plane message (except limited by MTU size)."; + } + + leaf max-highest-priority-sec-per-cplane-message { + type uint16; + description + "Maximum number of highest priority sections which can be received in one single C-Plane message. + This leaf applies only when 'coupling-via-frequency-and-time-with-priorities' is true"; + } + + leaf max-beams-per-slot-with-cplane-limits { + type uint16; + description + "Maximum number of beams that are required to be processed by endpoint or processed collectively + by group of endpoints sharing capacity when O-DU enables C-Plane limits. This value should replace + 'max-beams-per-slot' defined in 'endpoint-beam-capacity'"; + } + + leaf max-highest-priority-sections-per-slot-with-cplane-limits { + type uint16; + description + "Maximum number of highest priority sections that are required to be processed by endpoint or processed + collectively by group of endpoints sharing capacity when O-DU supports C-Plane limits. This value should + replace 'max-highest-priority-sections-per-slot' defined in 'endpoint-section-capacity'"; + } + + leaf max-num-se22-per-cplane-message { + type uint16; + description + "This value limits the max number of section extension 22 (ACK/NACK request) included in a C-Plane message as supported + by the endpoint or collectively by group of endpoints sharing capacity when O-DU enables C-Plane limits + and when section extension 22 (ACK/NACK request) and section type 8 (ACK/NACK feedback) are supported by the endpoint or endpoint sharing group."; + } + + leaf max-prb-ranges-per-hp-section-sec-ext-12 { + type uint16; + description + "Optional parameter reported by O-RU which applies to Section Extension 12 and specifies the + maximum number of frequency ranges R per highest priority section description supported by O-RU. + If this limit is exceeded by the O-DU, the O-RU is responsible for sending alarm notification + with 'fault-id = 31' to subscribers following the procedures defined in Clause 11 and + Table A.1-1 of O-RAN.WG4.MP.0. + As an example, the O-RU can include the following in the corresponding active-alarm list + entry and alarm-notification message: + Error C-Plane message + Maximum number of frequency hops per highest priority section description exceeded + Reduce frequency range per highest section description"; + } + + description + "Attributes limit maximum instances of certain parameters contained in a C-Plane message e.g. beams, + highest priority sections. This limitation is useful for O-RUs which are packet processing based and are + dimensioned to handle fixed number of parameters contained in a C-Plane message. In addition, if O-DU supports + cplane limits, O-RU can operate more efficiently and endpoint capacity for applicable parameters are enhanced. + as specified by 'max-beams-per-slot-with-cplane-limits' and 'max-highest-priority-sections-per-slot-with-cplane-limits'"; + } + + + grouping per-uplane-message-limits { + leaf max-section-headers-per-uplane-message { + if-feature feat:UPLANE-MESSAGE-PROCESSING-LIMITS; + type uint16; + description + "Maximum number of section headers which can be received in one single U-Plane message."; + } + + description + "Attributes describe maximum instances of certian parameters contained in a U-Plane mesaage e.g. + number of section headers. Those limitations are required for O-RUs which are packet processing based and are + dimensioned to handle limited number of parameters contained in a U-Plane message. "; + } + + grouping beam-update-contention-control-limits { + leaf max-beams-per-symbol-with-beam-contention-control { + type uint16; + description + "Maximum number of beams within one symbol that can be processed by an endpoint or processed collectively + by group of endpoints sharing capacity when O-DU enables the O-RU feature BEAM-UPDATE-CONTENTION-CONTROL. + This value should replace 'max-beams-per-symbol' defined in 'endpoint-beam-capacity'"; + } + + leaf max-beams-updates-per-symbol-with-beam-contention-control { + type uint16; + description + "Maximum number of beams within one symbol that can be updated by an endpoint or processed collectively + by group of endpoints sharing capacity when O-DU enables the O-RU feature BEAM-UPDATE-CONTENTION-CONTROL. + This value should replace 'max-beam-updates-per-symbol' defined in 'endpoint-beam-capacity'"; + } + + description + "If O-DU supports BEAM-UPDATE-CONTENTION-CONTROL, O-RU can operate more efficiently and endpoint capacity for applicable + parameters are enhanced."; + } + grouping endpoint-static-config-support { leaf static-config-supported { type enumeration { @@ -1047,7 +2134,16 @@ module o-ran-uplane-conf { when "(/user-plane-configuration/static-low-level-rx-endpoints[name=current()/../name]/static-config-supported = 'PRACH')"; type uint8; description - "Maximum number of PRACH patterns the endpoint can handle in PRACH configuration"; + "Maximum number of PRACH patterns the endpoint can handle in PRACH configuration. + Due to limited range of the parameter, an O-DU should use extended-max-prach-patterns parameter."; + } + + leaf extended-max-prach-patterns { + when "(/user-plane-configuration/static-low-level-rx-endpoints[name=current()/../name]/static-config-supported = 'PRACH')"; + if-feature feat:EXTENDED-PRACH-CONFIGURATION; + type uint32; + description + "Maximum number of PRACH patterns the endpoint can handle in PRACH configuration if EXTENDED-STATIC-PRACH is supported."; } leaf max-srs-patterns { @@ -1063,6 +2159,7 @@ module o-ran-uplane-conf { grouping endpoint-tdd-pattern-support { leaf configurable-tdd-pattern-supported { + if-feature mcap:CONFIGURABLE-TDD-PATTERN-SUPPORTED; type boolean; default false; @@ -1085,6 +2182,482 @@ module o-ran-uplane-conf { TDD patterns and limitations regarding common TDD switching per groups of endpoints."; } + grouping u-plane-transmission-control { + description + "Configuration for UL U-plane transmission control"; + + leaf transmission-window-control { + if-feature "feat:STATIC-TRANSMISSION-WINDOW-CONTROL or feat:DYNAMIC-TRANSMISSION-WINDOW-CONTROL"; + type enumeration { + enum NONE { + description + "O-RU will use normal transmission type with normal transmission window"; + } + enum STATIC { + if-feature feat:STATIC-TRANSMISSION-WINDOW-CONTROL; + description + "O-RU will use transmission window offset and size and transmission type as configured over M-plane"; + } + enum DYNAMIC { + if-feature feat:DYNAMIC-TRANSMISSION-WINDOW-CONTROL; + description + "O-RU will use transmission window offset and size and transmission type as requested over C-plane"; + } + } + default NONE; + + description + "Selects type of UL U-plane transmission window control for the endpoint"; + } + + leaf transmission-window-schedule { + when "(../transmission-window-control = 'STATIC')"; + if-feature feat:STATIC-TRANSMISSION-WINDOW-CONTROL; + type union { + type empty; + type leafref { + path "/user-plane-configuration/transmission-window-schedules/id"; + } + } + description + "Identifies U-plane transmission window schedule (which provides a list of symbol-specific offsets) + to be used by the endpoint. + Symbol-specific offset values from the schedule are added to endpoint specific offset value + from transmission-window-offset. + If this element is not present, the O-RU assumes as all symbol specific offsets are zero. + For restrictions on values of symbol-specific offsets see description of transmission-window-offset. + This parameter shall not be set to value other than empty if transmission control is not enabled + by transmission-window-control."; + } + + leaf transmission-window-offset { + when "(../transmission-window-control = 'STATIC')"; + if-feature feat:STATIC-TRANSMISSION-WINDOW-CONTROL; + type uint16; + units "number of symbols"; + default 0; + description + "Transmission window offset. + The effective symbol-specific offset is sum of endpoint-specific offset value from transmission-window-offset + and symbol-specific offset from transmission-window-schedules referenced by transmission-window-schedule. + + If this element is not present then O-RU assumes transmission-window-offset is zero. + This parameter shall not be set to value other than 0 if transmission control is not enabled + by transmission-window-control. + + There is no direct upper bound for this parameter (except value range defined by leaf type) but there are + restrictions on combination of values of transmission-window-offset, transmission-window-offset, + symbol-specific offset from transmission-window-schedules and transmission-window-size. + + Specifically, if endpoint is set for delay managed operation (non-time-managed-delay-enabled = FALSE) + then O-RU handles U-Plane transmission as delay managed traffic and combination of values of + transmission-window-offset, symbol-specific offset from transmission-window-schedules and + transmission-window-size must not exceed O-RU buffering capacity (transmission-buffering-capacity) + for the requested reception schedule. + + If endpoint is set for non-delay managed operation (non-time-managed-delay-enabled = TRUE) + then O-RU handles U-Plane transmission on best effort basis. In this case any combination of values + of transmission-window-offset, symbol-specific offset from transmission-window-schedules and + transmission-window-size is permitted. This does not remove restrictions on parameters due to restrictions + defined for value type or restrictions that depend on INDEPENDENT-TRANSMISSION-WINDOW-CONTROL feature + + Additional restrictions on values (and presence) of transmission-window-schedule, transmission-window-offset + and transmission-window-size depend on presence of INDEPENDENT-TRANSMISSION-WINDOW-CONTROL feature. + + If INDEPENDENT-TRANSMISSION-WINDOW-CONTROL feature is not supported then all endpoints with + transmission control enabled that handle the same carrier type and SCS must be configured + (via M-plane or C-plane) with parameter values resulting in transmission windows that coincide + between the endpoints. That is, for every symbol N, the effective transmission window for symbol N + must be shared (start at the same time and end at the same time) by all the endpoints handling + same carrier type and SCS. This restriction applies only to endpoints that have transmission + control enabled. + + If INDEPENDENT-TRANSMISSION-WINDOW-CONTROL feature is supported then restriction described + above does not apply and a different transmission window (window offset and window size) + can be used for every endpoint capable of transmission window control."; + } + + leaf transmission-window-size { + when "(../transmission-window-control = 'STATIC')"; + if-feature feat:STATIC-TRANSMISSION-WINDOW-CONTROL; + type uint16 { + range "0..10000"; + } + units "microsecond"; + default 0; + description + "Transmission window size to be used; only values that are compatible with SCS used + for the endpoint are allowed. + Minimum supported value is determined by SCS specific transmission window size + reported by O-RU in ru-delay-profile. + + Client can use special value 0 (zero) instead of the SCS specific transmission window + size reported by O-RU in ru-delay-profile with the same effect. + This parameter shall not be set to value other than 0 if transmission control is not enabled + (transmission-window-control = NONE) or the endpoint is set for non-delay managed operation + (non-time-managed-delay-enabled = TRUE). + + There is no direct upper bound for this parameter (except value range defined by leaf type) but there are + restrictions on combination of values of transmission-window-offset, transmission-window-offset, + symbol-specific offset from transmission-window-schedules and transmission-window-size. + See description of transmission-window-offset for more details."; + } + + leaf transmission-type { + when "(../transmission-window-control = 'STATIC')"; + if-feature "feat:STATIC-TRANSMISSION-WINDOW-CONTROL and feat:UNIFORMLY-DISTRIBUTED-TRANSMISSION"; + type enumeration { + enum NORMAL { + description + "Normal transmission (unspecified within transmission window)"; + } + enum UNIFORMLY-DISTRIBUTED { + if-feature feat:UNIFORMLY-DISTRIBUTED-TRANSMISSION; + description + "Uniformly distributed over transmission window: each of N messages in the window + transmitted at random time in a separate subwindow; subwindow size is window size / N "; + } + } + + description "Type of delay supported by the endpoint"; + } + } + + grouping tx-endpoint-compression-grouping { + description + "Grouping for endpoint compression"; + + container compression { + presence + "This container shall exists to avoid misaligned compression + methods between devices"; + + description + "Container which consists of global configurable parameters for compression"; + + uses cf:compression-details; + + leaf fs-offset { + if-feature cf:CONFIGURABLE-FS-OFFSET; + type uint8; + default 0; + description + "Adjusts FS (full scale) value of IQ format relative to FS derived from unmodified IQ format. + Please refer to CU-Plane specification for details"; + } + + list dynamic-compression-configuration { + when "../compression-type = 'DYNAMIC'"; + key "id"; + unique "compression-method iq-bitwidth fs-offset"; + description + "List of possible configuration in case dynamic configuration is used + Note: In case of empty list all available compressions can be chosen dynamically + and default fs-offset is taken (0). + If the feature CONFIGURABLE-FS-OFFSET is not supported, + fs-offset will not be considered as one of the criteria for unique statement. + Refer to RFC7950 clause 7.8.3 for details of unique statement"; + + leaf id { + type uint16; + description + "Identification number for particular compression"; + } + + uses cf:compression-method-grouping; + + leaf fs-offset { + if-feature cf:CONFIGURABLE-FS-OFFSET; + type uint8; + default 0; + description + "Adjusts FS (full scale) value of IQ format relative to FS derived from unmodified IQ format. + Please refer to CU-Plane specification for details"; + } + + leaf channel-information-iq-bitwidth { + type uint8{ + range "1..16"; + } + status deprecated; + description + "I/Q bitwidth for channel information in Section Type6. + Leaf is deprecated as it should be independent of dynamic compression, + NETCONF client instead uses channel-information-bitwidth schema node"; + } + } + + leaf channel-information-bitwidth { + type uint8{ + range "1..16"; + } + description + "I/Q bitwidth for channel information in Section Type6. + Maximum value is limited to 16. + Value 1,2,..16 indicates I and Q are each 1,2..16 bits wide respectively. + Note: This parameter shall be used when the NETCONF client does not make use of + the CHANNEL-INFORMATION-COMPRESSION feature."; + } + + list channel-information-compressions { + if-feature "feat:CHANNEL-INFORMATION-COMPRESSION"; + key id; + + leaf id { + type uint16; + description + "Identification number for particular channel information compression"; + } + + leaf channel-information-compression-method { + type cf:ci-compression-method-def; + description + "Channel information compression method"; + } + + leaf iq-bitwidth { + type uint8{ + range "1..16"; + } + description + "Bitwidth to be used in compression, maximum value is limited to 16. + Value 1,2,..16 indicates I and Q are each 1,2..16 bits wide respectively. + + When this schema node is used in the configuration, the O-RU shall ignore the configuration of the + /low-level-tx-endpoints/compression/channel-information-bitwidth schema node"; + } + + description + "List of available compressions, that can be dynamically chosen, for channel information (e.g. ST6) for this endpoint. + Note1: When only one compression is listed here, it implies that channel information compression + is static and mentioned compression is taken into use. + Note2: O-RU Shall reject configuration with compressions that are not supported"; + } + + list bf-weights-compressions { + key id; + + leaf id { + type uint16; + description + "Identification number for particular beamforming compression"; + } + + leaf bf-weights-compression { + type cf:bf-compression-method-def; + description + "Beamforming weight compression method"; + } + + leaf iq-bitwidth { + type uint8 { + range "1..16"; + } + description + "Bitwidth to be used in compression, maximum value is limited to 16. + Value 1,2,..16 indicates I and Q are each 1,2..16 bits wide respectively"; + } + description + "List of available compressions, that can be dynamically chosen, for beamforming weights for this endpoint. + Note1: When only one compression is listed here, it implies that beamforming weights compression + is static and mentioned compression is taken into use. + Note2: O-RU Shall reject configuration with compressions that are not supported"; + } + } + } + + grouping rx-endpoint-compression-grouping { + description + "Grouping for endpoint compression"; + + container compression { + presence + "This container shall exists to avoid misaligned compression + methods between devices"; + + description + "Container which consists of global configurable parameters for compression"; + + uses cf:compression-details; + + leaf fs-offset { + if-feature cf:CONFIGURABLE-FS-OFFSET; + type uint8; + default 0; + description + "Adjusts FS (full scale) value of IQ format relative to FS derived from unmodified IQ format. + Please refer to CU-Plane specification for details"; + } + + list dynamic-compression-configuration { + when "../compression-type = 'DYNAMIC'"; + key "id"; + unique "compression-method iq-bitwidth fs-offset"; + description + "List of possible configuration in case dynamic configuration is used + Note: In case of empty list all available compressions can be chosen dynamically + and default fs-offset is taken (0). + If the feature CONFIGURABLE-FS-OFFSET is not supported, + fs-offset will not be considered as one of the criteria for unique statement. + Refer to RFC7950 clause 7.8.3 for details of unique statement"; + + leaf id { + type uint16; + description + "Identification number for particular compression"; + } + + uses cf:compression-method-grouping; + + leaf fs-offset { + if-feature cf:CONFIGURABLE-FS-OFFSET; + type uint8; + default 0; + description + "Adjusts FS (full scale) value of IQ format relative to FS derived from unmodified IQ format. + Please refer to CU-Plane specification for details"; + } + } + + list bf-weights-compressions { + key id; + + leaf id { + type uint16; + description + "Identification number for particular beamforming compression"; + } + + leaf bf-weights-compression { + type cf:bf-compression-method-def; + description + "Beamforming weight compression method"; + } + + leaf iq-bitwidth { + type uint8; + description + "Bitwidth to be used in compression"; + } + + description + "List of available compressions, that can be dynamically chosen, for beamforming weights for this endpoint. + Note1: When only one compression is listed here, it implies that beamforming weights compression + is static and mentioned compression is taken into use. + Note2: O-RU Shall reject configuration with compressions that are not supported"; + } + } + } + + grouping supported-section-type4-config { + description + "Set of parameters required for section type 4 slot configuration message."; + list st4-supported-commands { + key "st4-command-type"; + + description + "This parameter specifies the list of Section Type 4 command types supported by O-RU."; + leaf st4-command-type { + type enumeration { + enum TIME_DOMAIN_BEAM_CONFIG { + description "O-RU support for receiving time domain beamforming command."; + } + enum TDD_CONFIG_PATTERN { + description "O-RU support for receiving TDD symbol pattern for a given slot."; + } + enum TRX_CONTROL { + if-feature feat:TRX-CONTROL; + description "O-RU support for receiving TRX CONTROL command in a given slot."; + } + enum ASM { + if-feature feat:ADVANCED-SLEEP-MODE; + description "O-RU support for receiving ASM command in a given slot."; + } + } + description + "Section Type 4 command types supported by O-RU."; + } + + list st4-command-scope-params { + key "cmd-scope"; + + description + "This list contains parameters which are specific to a given command scope associated with a particular section type 4 command type."; + + leaf cmd-scope { + type enumeration { + enum ARRAY-COMMAND { + description + "Command applies to all endpoints assigned to the tx-array(s) and/or rx-array(s).If the O-RU + is operating with multiple O-DUs, the scope of an individual O-DU is limited, to the + endpoints assigned to the O-DU which was the originator of the command."; + } + enum CARRIER-COMMAND { + description + "Command applies to all endpoints assigned to the tx-array-carrier and/or rx-array-carrier."; + } + enum O-RU-COMMAND { + description + "O-RU level command, i.e., one that applies to all arrays and carriers. If the O-RU + is operating with multiple O-DUs, the scope of an individual O-DU is limited, as + specified in CUS-Plane specification, clause 7.5.2.17."; + } + } + description "Command scope for section type 4 commands"; + } + + leaf st4-reception-bitmask { + type uint16; + description + "This parameter specifies the O-RU capability to receive Section Type 4 message with associated commands with a particular command scope + for the symbols for which the corresponding bit is set to '1' in the bitmask. If the O-RU receives a Section Type 4 message with + associated command for a symbol whose bit value is set to '0' in this bitmask, the O-RU shall reject the Section Type 4 message"; + } + } + } + + list st4-reception-mask { + key "cmd-scope"; + status deprecated; + + description + "List of symbol mask per command scope, specifying the O-RU capability to receive Section Type 4 message for a + given command scope only for symbols for which the corresponding bit is set to '1' in the bitmask. If the O-RU + receives a Section Type 4 message with associated command for a symbol whose bit value is set to '0' in this + bitmask, the O-RU shall reject the Section Type 4 message"; + + leaf cmd-scope { + type enumeration { + enum ARRAY-COMMAND { + description + "Command applies to all endpoints assigned to the tx-array(s) and/or rx-array(s).If the O-RU + is operating with multiple O-DUs, the scope of an individual O-DU is limited, to the + endpoints assigned to the O-DU which was the originator of the command."; + } + enum CARRIER-COMMAND { + description + "Command applies to all endpoints assigned to the tx-array-carrier and/or rx-array-carrier."; + } + enum O-RU-COMMAND { + description + "O-RU level command, i.e., one that applies to all arrays and carriers. If the O-RU + is operating with multiple O-DUs, the scope of an individual O-DU is limited, as + specified in CUS-Plane specification, clause 7.5.2.17."; + } + } + description "Command scope for section type 4 commands"; + } + + leaf st4-reception-bitmask { + type uint16; + description + "This parameter specifies the O-RU capability to receive Section Type 4 message with associated commands for the symbols + for which the corresponding bit is set to '1' in the bitmask. If the O-RU receives a Section Type 4 message with + associated command for a symbol whose bit value is set to '0' in this bitmask, the O-RU shall reject the Section Type 4 message"; + } + } + } + grouping uplane-conf-group { description "Grouping for uplane configuration related parameters"; @@ -1100,13 +2673,60 @@ module o-ran-uplane-conf { "Unique name of low-level-tx-link object."; } + leaf sro-id { + if-feature feat:SHARED-ORU-MULTI-OPERATOR; + type leafref { + path "/or-user:users/or-user:user/or-user:sro-id"; + } + description + "An optional shared resource operator associated with this list entry. + When present, indicates that the list entry corresponds to a low-level-tx-link + associated with a shared resource operator where the sro-id identifies + a specific shared resource operator. + This schema-node is used to refine NACM privileges for shared + resource operators"; + } + leaf processing-element { type leafref { path "/o-ran-pe:processing-elements/o-ran-pe:ru-elements/o-ran-pe:name"; } mandatory true; description - "Contains name of processing-element to be used as transport by low-level-tx-link"; + "Contains name of processing-element to be used as transport by low-level-tx-link, + if 'transport-qualified-processing-element' is included in the configuration, + the O-RU shall use 'transport-qualified-processing-element' instead of this schema node, + an O-RU Controller shall still configure this schema node with a leafref to a valid ru-element, + but this shall not be used by the O-RU."; + } + + leaf transport-session-type { + if-feature "feat:MULTIPLE-TRANSPORT-SESSION-TYPE"; + type enumeration { + enum ETH-INTERFACE { + description "VLAN based CUS Transport "; + } + enum UDPIP-INTERFACE { + description "UDP/IP based CUS Transport "; + } + enum ALIASMAC-INTERFACE{ + description "Alias MAC address based CUS Transport "; + } + } + description + "transport session type used when an O-RU is configured with multiple processing elements of different transport session types, + in which case it is used for referencing a processing element in 'transport-qualified-processing-element'"; + } + + leaf transport-qualified-processing-element { + if-feature "feat:MULTIPLE-TRANSPORT-SESSION-TYPE"; + type leafref { + path "/o-ran-pe:processing-elements/o-ran-pe:additional-transport-session-type-elements[o-ran-pe:transport-session-type = current()/../transport-session-type]/o-ran-pe:ru-elements/o-ran-pe:name"; + } + description + "Contains name of processing-element to be used as transport by low-level-tx-link. + Used when the processing element is configured + by the list 'additional-transport-session-type-elements'"; } leaf tx-array-carrier { @@ -1140,13 +2760,60 @@ module o-ran-uplane-conf { "Unique name of low-level-rx-links object."; } + leaf sro-id { + if-feature feat:SHARED-ORU-MULTI-OPERATOR; + type leafref { + path "/or-user:users/or-user:user/or-user:sro-id"; + } + description + "An optional shared resource operator associated with this list entry. + When present, indicates that the list entry corresponds to a low-level-rx-link + associated with a shared resource operator where the sro-id identifies + a specific shared resource operator. + This schema-node is used to refine NACM privileges for shared + resource operators"; + } + leaf processing-element { type leafref { path "/o-ran-pe:processing-elements/o-ran-pe:ru-elements/o-ran-pe:name"; } mandatory true; description - "Contains name of processing-element to be used as transport by LowLevelTxLink"; + "Contains name of processing-element to be used as transport by low-level-rx-link, + if 'transport-qualified-processing-element' is included in the configuration, + the O-RU shall use 'transport-qualified-processing-element' instead of this schema node, + an O-RU Controller shall still configure this schema node with a leafref to a valid ru-element, + but this shall not be used by the O-RU."; + } + + leaf transport-session-type { + if-feature "feat:MULTIPLE-TRANSPORT-SESSION-TYPE"; + type enumeration { + enum ETH-INTERFACE { + description "VLAN based CUS Transport "; + } + enum UDPIP-INTERFACE { + description "UDP/IP based CUS Transport "; + } + enum ALIASMAC-INTERFACE{ + description "Alias MAC address based CUS Transport "; + } + } + description + "transport session type used when an O-RU is configured with multiple processing elements of different transport session types, + in which case it is used for referencing a processing element in 'transport-qualified-processing-element'"; + } + + leaf transport-qualified-processing-element { + if-feature "feat:MULTIPLE-TRANSPORT-SESSION-TYPE"; + type leafref { + path "/o-ran-pe:processing-elements/o-ran-pe:additional-transport-session-type-elements[o-ran-pe:transport-session-type = current()/../transport-session-type]/o-ran-pe:ru-elements/o-ran-pe:name"; + } + description + "Contains name of processing-element to be used as transport by low-level-rx-link. + Used when the processing element is configured + by the list 'additional-transport-session-type-elements'"; } leaf rx-array-carrier { @@ -1176,13 +2843,49 @@ module o-ran-uplane-conf { description "Parameter to set the non-default marking for user-plane"; } + + leaf enhanced-user-plane-uplink-marking { + if-feature "feat:MULTIPLE-TRANSPORT-SESSION-TYPE"; + type leafref { + path "/o-ran-pe:processing-elements/o-ran-pe:additional-transport-session-type-elements[o-ran-pe:transport-session-type = current()/../transport-session-type]/o-ran-pe:enhanced-uplane-mapping/o-ran-pe:uplane-mapping/o-ran-pe:up-marking-name"; + } + description + "Parameter to set the non-default marking for user-plane. + Used when the enhanced-uplane-mapping is configured + by the list 'additional-transport-session-type-elements'"; + } + } + + grouping common-se24-dmrs-capabilities-grp { + description + "Grouping of common SE23 DMRS BF capabilities. + Please refer to CUS specification clause 7.7.24 for more details."; + + leaf max-user-groups-per-slot { + type uint32; + description + "Maximum number user groups per slot."; + } + + leaf max-entries-per-slot { + type uint32; + description + "Maximum number of entries in the table of DMRS configurations per slot."; + } } list endpoint-types { key "id"; config false; description - "Properties of endpoint that are common to multiple endpoints if such are identified"; + "Properties of endpoint that are common to multiple endpoints if such are identified + Note: For any endpoint supporting mixed numerology within slot/symbol (multiple-numerology-supported=TRUE) + * Interval specific (per-slot and per-symbol) capacity constraints instantaneous capacity usage + (i.e. capacity is shared/distributed between all numerologies used at an instant of time). + * Instantaneous capacity usage is the sum of capacity usages of all intervals of a specific type (slot/symbol) + that share the instant of time at which instantaneous capacity usage is evaluated regardless of numerology. + * Capacity used in an interval is allocated at the start of the interval and is released at the end of the interval + (e.g. capacity used for symbol #N of numerology #M is unavailable during symbol #N and available for reuse after symbol #N ends)."; leaf id { type uint16; @@ -1211,11 +2914,15 @@ module o-ran-uplane-conf { } } + uses supported-section-type4-config { + if-feature feat:ST4-SLOT-CONFIG-MSG-SUPPORT; + } + leaf-list supported-frame-structures { type uint8; description - "List of supported values of frame structure"; + "List of supported values of frame structure. For semantic and allowed values see CUS-Plane Specification, clause '7.5.2.13 frameStructure (frame structure)'"; } leaf managed-delay-support { @@ -1266,6 +2973,7 @@ module o-ran-uplane-conf { uses endpoint-section-capacity; uses endpoint-beam-capacity; uses endpoint-prb-capacity; + uses endpoint-section-extension-limits; leaf-list prb-capacity-allocation-granularity { type uint16; @@ -1277,6 +2985,608 @@ module o-ran-uplane-conf { } uses endpoint-numerology-capacity; + + leaf static-transmission-window-control-supported { + if-feature feat:STATIC-TRANSMISSION-WINDOW-CONTROL; + type boolean; + description + "Endpoint supports transmission-window-control = STATIC"; + } + + leaf uniformly-distributed-transmission-supported { + if-feature "feat:STATIC-TRANSMISSION-WINDOW-CONTROL and feat:UNIFORMLY-DISTRIBUTED-TRANSMISSION"; + type boolean; + description + "Endpoint supports transmission-type = UNIFORMLY-DISTRIBUTED"; + } + + leaf ordered-transmission-supported { + if-feature feat:ORDERED-TRANSMISSION; + type boolean; + description + "Endpoint supports ordered transmission of U-plane messages + and can be configured with ordered-transmission = TRUE"; + } + + leaf dynamic-transmission-window-control-supported { + if-feature feat:DYNAMIC-TRANSMISSION-WINDOW-CONTROL; + type boolean; + description + "Endpoint supports U-plane transmission window control via C-plane + (supports transmission-window-control = DYNAMIC) with independent window offset and size per symbol"; + } + + leaf dynamic-transmission-window-control-per-section-supported { + when "../dynamic-transmission-window-control-supported = 'true'"; + if-feature feat:DYNAMIC-TRANSMISSION-WINDOW-CONTROL; + type boolean; + description + "Endpoint supports U-plane transmission window control via C-plane + (supports transmission-window-control = DYNAMIC) with independent window offset and size per section"; + } + + leaf dynamic-uniformly-distributed-transmission-supported { + if-feature "feat:DYNAMIC-TRANSMISSION-WINDOW-CONTROL and feat:UNIFORMLY-DISTRIBUTED-TRANSMISSION"; + type boolean; + description + "Endpoint supports U-plane transmission window control via C-plane + (supports transmission-window-control = DYNAMIC) with independent transmission type per symbol"; + } + + leaf dynamic-uniformly-distributed-transmission-per-section-supported { + when "../dynamic-uniformly-distributed-transmission-supported = 'true'"; + type boolean; + description + "Endpoint supports U-plane transmission window control via C-plane + (supports transmission-window-control = DYNAMIC) with independent transmission type per section"; + } + + uses transmission-buffering-capacity { + when "static-transmission-window-control-supported = 'true' or dynamic-transmission-window-control-supported = 'true'"; + if-feature "feat:STATIC-TRANSMISSION-WINDOW-CONTROL or feat:DYNAMIC-TRANSMISSION-WINDOW-CONTROL"; + } + + leaf cplane-message-processing-limits-required { + if-feature feat:CPLANE-MESSAGE-PROCESSING-LIMITS; + type boolean; + config false; + description + "Endpoint requires applying C-Plane message processing limits defined in 'per-cplane-message-limits'. + and can be configured with cplane-message-processing-limits-enabled = TRUE"; + } + + uses per-cplane-message-limits { + when "cplane-message-processing-limits-required = 'true'"; + if-feature feat:CPLANE-MESSAGE-PROCESSING-LIMITS; + } + + leaf uplane-message-processing-limits-required { + if-feature feat:UPLANE-MESSAGE-PROCESSING-LIMITS; + type boolean; + config false; + description + "Endpoint has processing limits regarding U-Plane message. Detailed types of limits are defined in 'per-uplane-message-limits'."; + } + + uses per-uplane-message-limits { + when "uplane-message-processing-limits-required = 'true'"; + if-feature feat:UPLANE-MESSAGE-PROCESSING-LIMITS; + } + + leaf beam-update-contention-control-limits-required { + if-feature feat:BEAM-UPDATE-CONTENTION-CONTROL; + type boolean; + config false; + description + "Endpoint requires applying beam update contention control limits defined in 'beam-update-contention-control-limits'. + and can be configured with beam-update-contention-control-enabled = TRUE"; + } + + uses beam-update-contention-control-limits { + when "beam-update-contention-control-limits-required = 'true'"; + if-feature feat:BEAM-UPDATE-CONTENTION-CONTROL; + } + + leaf max-ack-nack-per-symbol { + type uint16; + description + "This value indicates the max number of ACK/NACK feedbacks supported by the endpoint in a symbol when + section extension 22 (ACK/NACK request) and section type 8 (ACK/NACK feedback) are supported by the endpoint. + The number of ACK/NACK feedbacks in a symbol is defined as the sum of 'numberofACKs' + 'numberofNACKs' in all C-Plane + messages sent by the endpoints in a symbol"; + } + + leaf non-scheduled-ueid-supported { + if-feature feat:NON-SCHEDULED-UEID; + type boolean; + default false; + description + "The parameter informs that the endpoint supports the capability of interpreting that those PRBs in the specified port are not allocated. + Please refer to CUS specification clause 7.9.13 for more details."; + } + + leaf se-11-continuity-flag-supported { + if-feature feat:SE11-WITH-CONTINUITY-BIT-SUPPORT; + type boolean; + config false; + description + "This flag is applicable for 'static-low-level-tx-endpoints' which support Section Extension 11 (SE 11). The + O-RU may choose to indicate its support for handling continuity flag as defined in CUS-Plane Clause 7.7.11.9 + by advertising this flag to TRUE/FALSE. + - Value of the flag advertised as 'TRUE' implies O-RU supports handling of 'continuity' flag in SE 11; + - Value of the flag advertised to 'FALSE' implies the O-RU does not support handling of 'continuity' flag in SE 11."; + } + + leaf se-23-prb-block-mode-supported { + if-feature feat:SE23-PRB-BLOCK-MODE-SUPPORT; + type boolean; + config false; + description + "This flag is applicable for 'static-low-level-tx-endpoints' which support Section Extension 23 (SE 23). The + O-RU may choose to indicate its support for prbMode = PRB-BLOCK mode flag as defined in CUS-Plane Clause 7.7.23.1 + by advertising this flag as TRUE/FALSE. + - Value of the flag advertised as 'TRUE' implies O-RU supports both prbMode = PRB-BLOCK mode of SE 23 + and prbMode = PRB-MASK which is the default mode when SE-23 is used. + - Value of the flag advertised as 'FALSE' implies O-RU supports prbMode = PRB-MASK which is the + default mode when SE-23 is used and the O-RU does not support prbMode = PRB-BLOCK mode."; + } + + leaf user-group-optimization-supported { + if-feature feat:USER-GROUP-OPTIMIZATION; + type boolean; + default false; + description + "The parameter indicates that if O-RU sets this feature flag to 'TRUE' for a given endpoint, and the O-DU chooses to + support this feature by setting 'user-group-optimization-enabled-tx' to 'TRUE' for static-low-level-tx-endpoints, + 'user-group-optimization-enabled-rx' to 'TRUE' for static-low-level-rx-endpoints,then the O-DU shall be required + to send all layers for a given user group UEs i.e., UEs with same time-frequency allocation using single C-Plane + section description. refer to CUS-Plane specification clause 7.9.14."; + } + + leaf se-20-multi-sd-punc-pattern-suported { + if-feature feat:SE20-MULTI-SD-PUNC-PATTERN-SUPPORT; + type boolean; + config false; + description + "This flag is applicable for 'static-low-level-[tr]x-endpoints' which support Section Estension 20 (SE-20). + The flag associated with each puncturing pattern in SE-20. The details of are defined in CUS-Plane Clause + 7.7.20.1. The O-RU indicates support for this feature by advertising this flag as TRUE/FALSE. + - Value of the flag advertised as 'TRUE' indicates O-RU supports handling of 'multiSDScope' field in SE-20. + - Value of the flag advertised as 'FALSE' indicates O-RU does not supports handling of 'multiSDScope' field in SE-20."; + } + + uses supported-configuration-combinations-grp; + + leaf-list supported-filter-indices { + type uint32; + description + "The list of supported values of filterIndex. + An empty list means that the set of supported values of filterIndex is not constrained."; + } + + leaf-list bf-profile-group { + if-feature feat:BF-DELAY-PROFILE; + type leafref { + path "/user-plane-configuration/endpoint-bf-profile-group/bf-profile-id"; + require-instance false; + } + description + "List of beamforming method groups supported by the endpoint, and the related delay profile values."; + } + + list supported-pass-band-for-filterindex09-per-scs { + key "scs"; + config false; + + description + "When filterIndex 0x9 is used, O-RU shall use minimum pass band filter among the reported passband filter sizes + that can accommodate the numPrbc and startPrbc of all the section descriptions associated with same frameStructure. + Please refer to CUS specification clause 7.5.2.3 for more details."; + + leaf scs { + type mcap:scs-config-type; + description + "Value corresponds to SCS values defined for frameStructure in C-plane."; + } + + leaf supported-max-num-msg-for-filterindex0x9-per-scs { + type uint16; + description + "Max number of filters can be actived at a time, + and this parameter limits the maximum number of ST3 mesaages with filterIndex set to 9 that O-DU can send so that the total number of filters active is below the maximum limit"; + } + + leaf-list filter-bandwidth { + type uint64; + units Hz; + description + "Bandwidth of a filter given in Hz. + If the list for the given SCS indicated via frameStructure is not empty, + O-DU should set filterIndex as 1001b for mixed numerology and other channels except PRACH and NB-IoT as specified in Table 7.5.2.3-1 when ST 3 C-plane message is used."; + + } + } + + container se24-dmrs-capabilities { + if-feature "feat:DMRS-BF-EQ or feat:DMRS-BF-NEQ"; + description + "Container collecting SE24 DMRS BF capabilties. + Please refer to CUS specification clause 7.7.24 for more details."; + + leaf-list supported-entry-types { + type uint8; + description + "Supported values of entryType."; + } + leaf ue-time-partial-overlap-supported { + type boolean; + description + "Indicates if UE time partial overlap is supported. + See CUS-plane clause 12.6.1.2.1."; + } + leaf ue-freq-partial-overlap-supported { + type boolean; + description + "Indicates if UE frequency partial overlap is supported. + See CUS-plane clause 12.6.1.2.1."; + } + leaf max-user-groups-per-ue-in-time { + type uint16; + description + "The maximum supported number of user groups in time-domain per UE. + See CUS-plane clause 12.6.1.2.1"; + } + leaf different-transform-precoding-in-user-group-supported { + type boolean; + description + "Indicates if different transform precoding in user group is supported."; + } + leaf different-cdm-without-data-in-user-group-supported { + type boolean; + description + "Indicates if different cdm without data in user group is supported."; + } + leaf max-dmrs-configs-per-user-group-incl-first-last-prb { + type uint32; + description + "Maximum number of different DMRS configurations (excluding differences in dmrsPortNumber) per user group, + including first and last PRB."; + } + + leaf max-dmrs-configs-per-user-group-excl-first-last-prb { + type uint32; + description + "Maximum number of different DMRS configurations (excluding differences in dmrsPortNumber) per user group, + excluding first and last PRB."; + } + uses common-se24-dmrs-capabilities-grp; + grouping dmr-mask-capability-grp { + description + "Grouping of transform-precoding capabilities"; + + list dmrs-mask-capability { + key id; + description + "Parameters for DMRS mask capabilities"; + leaf id { + type uint16; + description + "An identifier of an entry in the list."; + } + leaf-list dmrs-symbol-mask { + type uint16; + description + "Supported DMRS symbol mask. + Empty list means all the possible symbol masks are supported."; + } + leaf max-dmrs-ant-port-number { + type uint32; + description + "Maximum DMRS antenna port number."; + } + leaf max-user-data-layers { + type uint32; + description + "Maximum number of user data layers"; + } + } + } + container transform-precoding-enabled { + description + "Parameters for SE24 DMRS BF capabilities with transform precoding enabled"; + uses dmr-mask-capability-grp; + list low-papr-config { + key id; + description + "Supported Low-PAPR configs"; + leaf id { + type uint16; + description + "An identifier of an entry in the list."; + } + leaf low-papr-type { + type uint16 { + range "0 .. 3"; + } + description + "Supported values of lowPaprType"; + } + leaf hopping-mode { + type uint16 { + range "0 .. 3"; + } + description + "Supported values of hoppingMode"; + } + } + } + container transform-precoding-disabled { + description + "Parameters for SE24 DMRS BF capabilities with transform precoding disabled"; + uses dmr-mask-capability-grp; + list dmrs-config { + key id; + description + "Supported DMRS configs"; + leaf id { + type uint16; + description + "An identifier of an entry in the list."; + } + leaf d-type { + type uint16 { + range "0 .. 1"; + } + description + "Supported values of dType."; + } + leaf pusch-dmrs-muxing-supported { + type boolean; + description + "Indicates if PUSCH DMRS muxing is supported."; + } + } + } + } + + leaf sinr-reporting-supported { + if-feature feat:DMRS-BF-EQ; + type boolean; + default false; + description + "Indicates if SINR reporting is supported."; + } + container sinr-reporting-capabilities { + when "../sinr-reporting-supported = 'true'"; + if-feature feat:DMRS-BF-EQ; + description + "Container collecting parameters related to capabilities of SINR reporting"; + leaf simultaneous-sinr-and-dmrs-sending-supported { + type boolean; + description + "Indicates if simultaneous SINR and DMRS sending is supported."; + } + list supported-sinr-resolutions { + key "id"; + description + "The list of supported SINR reporting resolutions. + This entry is applicable if maximum number of data layers in any user group in any slot does not exceed value of max-data-layers. + + Please refer to CUS specification clause 7.2.10 for more details."; + leaf id { + type uint16; + description + "Identifies a supported SINR reporting resolution."; + } + leaf sinr-max-data-layers { + type uint8; + description + "The maximum number of data layers scheduled in any user group in a slot."; + } + leaf-list sinr-per-prb { + type uint8; + description + "List of supported values. Indicates number of SINR values per PRB."; + } + leaf-list sinr-slot-mask { + type uint16; + description + "List of bitmasks representing supported patterns of SINR reporting in time domain. An emplty list means that the set of supported SINR reporting is unrestricted."; + } + } + container supported-sinr-reference-level { + description + "Supported range of SINR reference levels values. + + See CUS specification clause 7.2.10 for more details"; + leaf sinr-reference-level-min { + type decimal64 { + fraction-digits 4; + } + units dB; + description + "Minimum supported SINR reference level."; + } + leaf sinr-reference-level-max { + type decimal64 { + fraction-digits 4; + } + units dB; + description + "Maximum supported SINR reference level."; + } + } + list supported-sinr-compression-methods { + key "id"; + description + "The list of supported SINR compression methods."; + leaf id { + type uint16; + description + "Identifies a supported SINR compression method."; + } + uses cf:sinr-compression-details; + } + } + container supported-symb-reorder-capabilities { + if-feature "feat:DMRS-BF-EQ or feat:DMRS-BF-NEQ"; + description + "List of capabilities related to symbol reordering."; + uses supported-symb-reorder-capability; + } + leaf-list supported-user-group-self-assembly-modes { + if-feature feat:USER-GROUP-SELF-ASSEMBLY; + type user-group-self-assembly-mode ; + description + "List of user group self assembly mode(s) supported by an endpoint. The O-RU may support SELF-ASSEMBLY-WITH-GROUP-ID and/or SELF-ASSEMBLY-WITHOUT-GROUP-ID."; + } + leaf-list continuity-block-sizes-supported { + if-feature "(feat:DMRS-BF-EQ or feat:DMRS-BF-NEQ) and feat:CONTINUITY-BLOCK-SIZE"; + type uint16; + description + "List of continuity bock sizes supported by the O-RU."; + } + leaf eq-scale-offset-min { + if-feature feat:DMRS-BF-EQ; + type decimal64 { + fraction-digits 4; + } + units dB; + description + "Refer to cus specification 8.3.1.4, max value 0 dB"; + } + leaf eq-scale-offset-max { + if-feature feat:DMRS-BF-EQ; + type decimal64 { + fraction-digits 4; + } + units dB; + description + "Refer to cus specification 8.3.1.4, max value 0 dB"; + } + leaf-list rrm-meas-supported { + if-feature feat:RRM-MEAS-REPORTING; + type rrm-meas-types; + description + "List of rrm types supported by the endpoint type"; + } + leaf max-ipn-unalloc-reports-supported { + when "../rrm-meas-supported = 'MEAS-IPN-UNALLOC'"; + if-feature feat:RRM-MEAS-REPORTING; + type uint16; + description + "This value indicates the maximum number of ipn unallocated measurement reports per PRB (PRB range) and slot"; + } + leaf max-ipn-unalloc-symbols-supported { + when "../rrm-meas-supported = 'MEAS-IPN-UNALLOC'"; + if-feature feat:RRM-MEAS-REPORTING; + type uint16; + description + "This parameter indicates maximum number of measured symbols of all measurement reports for a PRB (PRB range) in the slot "; + } + leaf max-ipn-alloc-reports-supported { + when "../rrm-meas-supported = 'MEAS-IPN-ALLOC'"; + if-feature feat:RRM-MEAS-REPORTING; + type uint16; + description + "This value indicates the maximum number of ipn alloc measurement reports per PRB per slot. Relationship between this value and alloc-ipn-per-sym-supported can be found in CUS plan specification clause 9.2.2.4"; + } + leaf-list alloc-ipn-per-dmrs-sym-grp-supported { + when "../rrm-meas-supported = 'MEAS-IPN-ALLOC'"; + if-feature feat:RRM-MEAS-REPORTING; + type uint8 { + range "0 .. 1"; + } + description + "This parameter indicates alloc-ipn measurement type(s) supported by the O-RU. + Refer to CUS specification clause 9.2.2.4 for details"; + } + leaf se-10-bgt-11b-supported { + type boolean; + default false; + description + "The parameter is applicable for static-low-level-[tr]x-endpoints supporting Section Extension 10. This flag + if set to TRUE indicates that the O-RU advertised endpoint, supporting Section Extension 10 supports beamGroupType = 11b. + It is mandatory for endpoints supporting DMRS-BF-EQ/DMRS-BF-NEQ beamforming methods to support beamGroupType = 11b. + refer to CUS-Plane specification clause 7.7.10.1"; + } + + leaf-list se-27-odu-controlled-dimensionality-reduction-supported { + if-feature feat:SE27-ODU-CONTROLLED-DIMENSIONALITY-REDUCTION; + type enumeration { + enum STATIC { + description + "Static O-DU controlled dimnesionality reduction enabled."; + } + enum DYNAMIC { + description + "Dynamic O-DU controlled dimnesionality reduction enabled."; + } + } + description + "Supported type O-DU controlled dimensionality reduction using SE27. + Please refer to CUS specification clause 7.7.27 for more details."; + } + leaf se-27-omission-when-enabled-supported { + when "(../se-27-odu-controlled-dimensionality-reduction-supported = 'STATIC') + or (../se-27-odu-controlled-dimensionality-reduction-supported = 'DYNAMIC')"; + if-feature feat:SE27-ODU-CONTROLLED-DIMENSIONALITY-REDUCTION; + type boolean; + description + "Indicates if O-RU supports omission of SE27 when O-DU controlled dimensionality reduction using SE27 is enabled. + Please refer to CUS specification clause 7.7.27 for more details."; + } + list supported-se-27-configurations { + when "(../se-27-odu-controlled-dimensionality-reduction-supported = 'STATIC') + or (../se-27-odu-controlled-dimensionality-reduction-supported = 'DYNAMIC')"; + if-feature feat:SE27-ODU-CONTROLLED-DIMENSIONALITY-REDUCTION; + key number-of-ue-data-layers; + description + "List of supported values of numElements parameter per number of UE + data layers scheduled in user group. + Please refer to CUS specification clause 7.7.27 for more details."; + leaf number-of-ue-data-layers { + type uint16; + description + "Number of UE data layers scheduled in user group."; + } + leaf-list supported-se-27-num-elements { + type uint16; + description + "Supported values of numElements in SE27 for the provided number of UE data layers."; + } + } + } + + list transmission-window-schedules { + if-feature feat:STATIC-TRANSMISSION-WINDOW-CONTROL; + key id; + description + "Parameters for scheduled UL U-plane message transmission (static transmission window control)"; + leaf id { + type uint16; + description + "Identifies a UL U-plane message transmission schedule"; + } + list schedule { + key symbol; + description + "Defines transmission window offset for given symbol. + If no entry is present for a symbol then O-RU assumes offset=0 for that symbol. + Entries with offset=0 shall be omitted."; + leaf symbol { + type uint16; + description + "Number of a symbol within a frame for which transmission offset is given."; + } + leaf offset { + type uint16; + description + "Transmission window offset; expressed as number of symbols the transmission window start is delayed by. + This value is added to u-plane-transmission-control/transmission-window-offset. + For restrictions on value of offset see description of transmission-window-offset."; + } + } } list endpoint-capacity-sharing-groups { @@ -1286,11 +3596,18 @@ module o-ran-uplane-conf { "Represents groups of endpoints that share capacity. Depending on O-RU implementation, processing resources that handle CU-plane (e.g. memory to keep sections and beams) could be allocated per endpoint or shared between several endpoints. - To address this O-RU shall reports own capability per endpoint (see endpoint-types) + To address this O-RU shall report own capability per endpoint (see endpoint-types) and per group of endpoints sharing capacity. If endpoint is in multiple groups then resulting constraint is minimum over all groups. Note: values of parameters representing capacity that is not shared between endpoints in a group - shall be set to max value of specific parameter; this effectively removes related constraint."; + shall be set to max value of specific parameter; this effectively removes related constraint. + Note: For any endpoint supporting mixed numerology within slot/symbol (multiple-numerology-supported=TRUE) + * Interval specific (per-slot and per-symbol) capacity constraints instantaneous capacity usage + (i.e. capacity is shared/distributed between all numerologies used at an instant of time). + * Instantaneous capacity usage is the sum of capacity usages of all intervals of a specific type (slot/symbol) + that share the instant of time at which instantaneous capacity usage is evaluated regardless of numerology. + * Capacity used in an interval is allocated at the start of the interval and is released at the end of the interval + (e.g. capacity used for symbol #N of numerology #M is unavailable during symbol #N and available for reuse after symbol #N ends)."; leaf id { type uint16; @@ -1302,6 +3619,7 @@ module o-ran-uplane-conf { uses endpoint-beam-capacity; uses endpoint-prb-capacity; uses endpoint-numerology-capacity; + uses endpoint-section-extension-limits; leaf max-endpoints { type uint16; @@ -1318,6 +3636,31 @@ module o-ran-uplane-conf { description "Number of endpoints supporting non-managed delay that can be used (configured for use) at a time"; } + uses transmission-buffering-capacity { + if-feature "feat:STATIC-TRANSMISSION-WINDOW-CONTROL or feat:DYNAMIC-TRANSMISSION-WINDOW-CONTROL"; + } + + uses per-cplane-message-limits { + if-feature feat:CPLANE-MESSAGE-PROCESSING-LIMITS; + } + leaf max-ack-nack-per-symbol { + type uint16; + description + "This value indicates the max number of ACK/NACK feedbacks supported by the endpoint sharing group in a symbol when + section extension 22 (ACK/NACK request) and section type 8 (ACK/NACK feedback) are supported by the endpoint sharing group. + The number of ACK/NACK feedbacks in a symbol is defined as the sum of 'numberofACKs' + 'numberofNACKs' in all C-Plane + messages sent by the endpoints in group in a symbol"; + } + uses supported-configuration-combinations-grp; + + container se24-dmrs-capabilities { + if-feature "feat:DMRS-BF-EQ or feat:DMRS-BF-NEQ"; + description + "Container collecting SE24 DMRS BF capabilties. + Please refer to CUS specification clause 7.7.24 for more details."; + + uses common-se24-dmrs-capabilities-grp; + } } list endpoint-prach-group { @@ -1355,7 +3698,10 @@ module o-ran-uplane-conf { } list compression-method-supported { - uses cf:compression-parameters; + uses cf:compression-method-grouping; + uses cf:compresion-format-grp { + status deprecated; + } leaf-list fs-offset { if-feature cf:CONFIGURABLE-FS-OFFSET; @@ -1370,7 +3716,153 @@ module o-ran-uplane-conf { "List of supported compression methods by O-RU Note: if O-RU supports different compression methods per endpoint then please refer to endpoints to have information what - exactly is supported on paticular endpoint"; + exactly is supported on particular endpoint"; + } + } + + grouping supported-configuration-combinations-grp { + description + "Grouping of supported freqOffset & PRB range combinations."; + + list supported-configuration-combinations { + key "combination-id"; + config false; + + leaf combination-id { + type uint32; + description + "Identification number for a combination."; + } + + list set { + key "set-id"; + + leaf set-id { + type uint32; + description + "Identification number for a set."; + } + + leaf max-overlapping-instances { + type uint32; + description + "Max number of configs of the set that can be used at the same time."; + } + + list config { + key "config-id"; + + leaf config-id { + type uint32; + description + "Identification number for a config."; + } + + leaf-list scs { + type mcap:scs-config-type; + description + "Value corresponds to SCS values defined for frameStructure in C-plane."; + } + + leaf-list carrier-types { + type enumeration { + enum NR { + description + "5G technology"; + } + enum LTE { + description + "LTE technology"; + } + enum DSS_LTE_NR { + if-feature mcap:DSS_LTE_NR; + description + "NR and LTE technologies in Dynamic Spectrum Sharing mode"; + } + } + description + "Type of carrier. Indicates array-carrier technology."; + } + + leaf filter-pass-bandwidth { + type uint64; + units Hz; + + description + "Width of a filter given in Hz. + The filter center frequency is given in center-of-channel-bandwidth of an array carrier. + When filterIndex 0x9 is used, O-RU shall report all the passband filter bandwidth per scs using the list supported-pass-band-for-filterindex09-per-scs."; + } + + leaf max-prb-range { + type uint32; + + description + "Maximum frequency range of PRBs that may be requested per frequency offset. Either contiguously or non-contiguously. + Total number of PRBs requested per config shall not exceed this value."; + } + + leaf center-from-freqoffset { + type boolean; + description + "This node allows to distinguish between combinations valid when + center-from-freqoffset-enabled is set to TRUE and combinations + valid when center-from-freqoffset-enabled is set to FALSE"; + } + + leaf-list supported-filter-indices { + type uint32; + description + "The list of supported values of filterIndex. + An empty list means that the set of supported values of filterIndex is constrained by the supported-filter-indices + provided in the endpoint type."; + } + + description + "Supported freqOffset & PRB range configuration. + Configuration is an instance of processing output equivalent to single FFT operation."; + } + description + "Set of freqOffset & PRB range configurations that require same resource allocation."; + } + description + "List of supported combinations of freqOffset & PRB range configurations. + If a leaf is not present in a list element it implies there is no constraint on that particular + parameter for the configuration in question."; + } + leaf center-from-freqoffset-supported { + type boolean; + description + "Endpoint's support for autonomous center frequency (k0) calculation based on frequency offset. + This capability applies only to RX endpoints."; + } + } + + grouping supported-symb-reorder-capability { + description + "List of capabilities related to symbol reordering."; + leaf-list supported-symb-reorder-capability { + type symb-reorder-capability; + description + "List of supported symbol reordering methods."; + } + leaf max-num-symbol-reordering-pattern-per-slot { + when "../supported-symb-reorder-capability = 'PER-SECTION-TX-WINDOW-REASSIGNMENT'"; + type uint16; + description + "This parameter defines the maximum number of unique symbol reassignment patterns supported + per slot per rx-array-carrier, where a 'symbol reassignment pattern' comprises 14 symbol + numbers specifying an altered symbol order (using txWinForOnAirSymbol0~13). The maximum + value applies over all SE25 invocations, including when an SE25 invocation includes + multiple symbol reassignment patterns. Repeated invocations of a single symbol reassignment + pattern shall count as a single unique instance. When endpoints of different endpoint-types + with different values of this parameter are associated with an rx-array-carrier, then the + smallest of the values shall apply."; + } + leaf-list up-symbolId-type-supported { + type up-SymbolId-type; + description + "List of supported symbol reordering symbolId interpretation capability."; } } @@ -1472,7 +3964,7 @@ module o-ran-uplane-conf { type boolean; description - "Informs if endpoint supports realtime variable bit with"; + "Informs if endpoint supports real-time variable bit with"; } leaf supported-compression-set-id { @@ -1487,6 +3979,22 @@ module o-ran-uplane-conf { uses endpoint-tdd-pattern-support; + leaf-list energy-sharing-group-id { + if-feature feat:MPLANE-DATA-LAYER-CONTROL; + type uint8; + description + "This value indicates a group of common underlying HW that can be used for energy savings."; + } + leaf se10-member-candidate-list { + if-feature feat:SE10-MEMBER-CANDIDATE-LIST; + type leafref { + path "/user-plane-configuration/se10-member-candidate-tx-lists/id"; + require-instance false; + } + description + "This parameter is a pointer to a given member-candidate list entry in 'se10-member-candidate-tx-lists'. + This parameter shall be applicable for endpoints supporting Section Extension 10"; + } } list static-low-level-rx-endpoints { @@ -1564,7 +4072,7 @@ module o-ran-uplane-conf { type boolean; description - "Informs if endpoint supports realtime variable bit with"; + "Informs if endpoint supports real-time variable bit with"; } leaf supported-compression-set-id { @@ -1581,6 +4089,42 @@ module o-ran-uplane-conf { uses endpoint-tdd-pattern-support; + leaf transmission-order { + when "(/user-plane-configuration/endpoint-types[id=current()/../endpoint-type]/ordered-transmission-supported = 'true')"; + if-feature feat:ORDERED-TRANSMISSION; + type uint32; + description + "The value indicates relative order of sending U-plane messages specific to same symbol + and transmission window by two endpoints with ordered-transmission = TRUE + belonging to the same transmission-order-group; + multiple endpoints may report the same value if their relative sending order is not guaranteed"; + } + + leaf transmission-order-group { + when "(/user-plane-configuration/endpoint-types[id=current()/../endpoint-type]/ordered-transmission-supported = 'true')"; + if-feature feat:ORDERED-TRANSMISSION; + type uint32; + description + "The value indicates a group of endpoints that preserve relative ordering of message sending; + message sending between endpoints in different groups is not guaranteed"; + } + + leaf-list energy-sharing-group-id { + if-feature feat:MPLANE-DATA-LAYER-CONTROL; + type uint8; + description + "This value indicates a group of common underlying HW that can be used for energy savings."; + } + leaf se10-member-candidate-list { + if-feature feat:SE10-MEMBER-CANDIDATE-LIST; + type leafref { + path "/user-plane-configuration/se10-member-candidate-rx-lists/id"; + require-instance false; + } + description + "This parameter is a pointer to a given member-candidate list entry in 'se10-member-candidate-rx-lists'. + This parameter shall be applicable for endpoints supporting Section Extension 10"; + } } list low-level-tx-endpoints { @@ -1588,7 +4132,7 @@ module o-ran-uplane-conf { description "Object model for low-level-tx-endpoints configuration - augmented static-low-level-tx-endpoints by local-address - which cannot be added to static low-level-tx-endpoints as we cannot have modificable element in static object"; + which cannot be added to static low-level-tx-endpoints as we cannot have modifiable element in static object"; leaf name { type leafref { @@ -1601,71 +4145,250 @@ module o-ran-uplane-conf { "Unique name of low-level-tx-endpoint object. Reference to static object"; } - container compression { - presence - "This container shall exists to avoid missaligned compression - methods between devices"; - + leaf sro-id { + if-feature feat:SHARED-ORU-MULTI-OPERATOR; + type leafref { + path "/or-user:users/or-user:user/or-user:sro-id"; + } description - "Container which consists of global configurable parameters for compression"; - - uses cf:compression-details; - - leaf fs-offset { - if-feature cf:CONFIGURABLE-FS-OFFSET; - type uint8; - default 0; - description - "Adjusts FS (full scale) value of IQ format relative to FS derived from unmodified IQ format. - Please refer to CU-Plane specification for details"; - } - - list dynamic-compression-configuration { - when "../compression-type = 'DYNAMIC'"; - key "id"; - unique "compression-method iq-bitwidth fs-offset"; - description - "List of possible configuration in case dynamic configuration is used - Note: In case of empty list all available compressions can be choosen dynamically - and default fs-offset is taken (0)."; - - leaf id { - type uint16; - description - "Identification number for particular compression"; - } - - uses cf:compression-method-grouping; - - leaf fs-offset { - if-feature cf:CONFIGURABLE-FS-OFFSET; - type uint8; - default 0; - description - "Adjusts FS (full scale) value of IQ format relative to FS derived from unmodified IQ format. - Please refer to CU-Plane specification for details"; - } - } + "An optional shared resource operator associated with this list entry. + When present, indicates that the list entry corresponds to a low-level-tx-endpoint + associated with a shared resource operator where the sro-id identifies + a specific shared resource operator. + This schema-node is used to refine NACM privileges for shared + resource operators"; } + + uses tx-endpoint-compression-grouping; uses scs-config; container e-axcid { uses eaxc; description - "Contains local address of low level TX endpoint offered by Netconf server."; + "Contains local address of low level TX endpoint offered by NETCONF server."; } uses coupling-methods; leaf configurable-tdd-pattern-supported { + if-feature mcap:CONFIGURABLE-TDD-PATTERN-SUPPORTED; type leafref { - path "/user-plane-configuration/static-low-level-rx-endpoints[name=current()/../name]/configurable-tdd-pattern-supported"; + path "/user-plane-configuration/static-low-level-tx-endpoints[name=current()/../name]/configurable-tdd-pattern-supported"; require-instance false; } description "RO to RW parameter mapping - needed for conditional under tx-array-carrier"; } + + leaf cplane-message-processing-limits-enabled { + if-feature feat:CPLANE-MESSAGE-PROCESSING-LIMITS; + type boolean; + default false; + description + "TRUE: O-DU shall support the C-Plane message processing limits and has selected to adhere to the C-Plane limits defined in 'per-cplane-message-limits'. + FALSE: O-DU shall not support C-Plane message processing limits and endpoint limits defined in 'per-cplane-message-limits' shall not apply"; + } + + leaf uplane-message-section-header-limit-enabled { + if-feature feat:UPLANE-MESSAGE-PROCESSING-LIMITS; + type boolean; + default false; + description + "O-DU declares to obey the limit exposed via the leaf max-section-headers-per-uplane-message by setting this parameter to 'true'. + For details of this parameter's usage, please refer to M-plane spec clause 15.10"; + } + + leaf beam-update-contention-control-enabled { + if-feature feat:BEAM-UPDATE-CONTENTION-CONTROL; + type boolean; + default false; + description + "TRUE: O-DU shall support beam update contention control feature of O-RU.Thus requiring O-DU to comply with beam + updates restrictions defined in CUS-Plane Spec Section 'Weight-based dynamic beamforming'. In this case applicable + beam limits defined in 'beam-update-contention-control-limits' shall apply. + FALSE: O-DU shall not support beam update contention control feature of O-RU. Applicable beam limits defined in + 'endpoint-beam-capacity' shall apply"; + } + + container channel-information-prb-group-configuration { + if-feature feat:CHANNEL-INFORMATION-PRB-GROUP; + description + "Container for configurable parameters for channel information prb group"; + + leaf enable-ci-prb-group { + type boolean; + description + "Informs whether or not to enable channel information PRB group in the O-RU, + if its value is set to true, the O-RU shall receive and process channel + information (e.g., ST6) with PRB group size of configured ci-prb-group-size."; + } + + leaf ci-prb-group-size { + type uint8 { + range "2..254"; + } + description + "The configured channel information PRB group size. + If enable-ci-prb-group is configured as TRUE, and the IE ciPrbGroupSize doesn't exist in a section description for channel information, + the parameter ci-prb-group-size shall be used for this section description. + If the IE ciPrbGroupSize exists in a section description for channel information, + no matter whether enable-ci-prb-group is configured as TRUE, the IE ciPrbGroupSize shall be used for this section description."; + } + } + + leaf non-scheduled-ueid-enabled{ + if-feature feat:NON-SCHEDULED-UEID; + type boolean; + default false; + description + "If O-DU configures 'non-scheduled-ueid-enabled' = TRUE, + and Section Extension 10 is applied to Section Types 5 and beamGroupType = 10b, + and the O-DU set ueId of a port in Section Extension 10 to 0x7FFF, + the endpoint shall interpret that the PRBs in the section description are not scheduled for this port. + The endpoint only interprets this leaf if non-scheduled-ueid-supported = true is reported in the related static endpoint, in other cases this leaf is ignored. + Please refer to CUS specification clause 7.9.13 for more details."; + } + + leaf se-11-continuity-flag-enabled { + if-feature feat:SE11-WITH-CONTINUITY-BIT-SUPPORT; + type boolean; + default false; + description + "By setting the value of this leaf node to TRUE, the O-DU declares it will provide 'continuity' flag information in SE 11. + In this case the O-DU shall provide PRB region 'continuity' information in every SE 11 sent to the applicable low-level-tx-endpoint(s) + (static-low-level-tx-endpoit(s) which indicated support of this feature by setting 'se-11-continuity-flag-supported' flag to TRUE). + In case + - O-DU is running SW conformant to older O_RAN release and does not interpret the flag 'se11-continuity-bit-enabled' or + - the O-DU sets 'se11-continuity-flag-enabled' = FALSE, + no information regarding PRB region continuity is sent to the O-RU. In such case the O-RU shall process the PRB bundles as + it would have in the absence of the 'continuity' flag in SE11. + This behavior shall be same as when O-RU receives message with 'reserved' field value set to '0b'."; + } + + leaf prg-size-supp-se-21-with-st6-enabled { + if-feature feat:PRG-SIZE-SUPP-SE-21-WITH-ST6; + type boolean; + default false; + description + "TRUE: O-DU shall send prgSize information from the O-DU to the O-RU as described in Clause 7.7.2.3.1 of the CUS-Plane spec. + FALSE:prgSize information cannot be sent from the O-DU to the O-RU using Section Type 6."; + } + + leaf prg-size-supp-se-21-with-st5-enabled { + if-feature feat:PRG-SIZE-SUPP-SE-21-WITH-ST5; + type boolean; + default false; + description + "TRUE: O-DU shall send prgSize information from the O-DU to the O-RU as described in Clause 7.7.2.3.2 of the CUS-Plane spec. + FALSE:prgSize information cannot be sent from the O-DU to the O-RU using Section Type 5."; + } + + leaf user-group-optimization-enabled-tx { + if-feature feat:USER-GROUP-OPTIMIZATION; + type boolean; + default false; + description + "The parameter indicates that if the O-RU advertises the feature flag to 'user-group-optimization-supported' set to + TRUE for associated static-level-tx-endpoint, the O-DU may choose to set this flag to 'TRUE' or 'FALSE'. + - If set to 'TRUE', the O-DU shall be required to send all layers for a given user group UEs i.e., UEs with same + time-frequency allocation using single C-Plane section description. + - If set to 'FALSE', the O-DU restriction to send all layers for a given user group UEs shall not apply."; + } + + leaf se-20-multi-sd-punc-pattern-enabled-tx { + if-feature feat:SE20-MULTI-SD-PUNC-PATTERN-SUPPORT; + if-feature feat:USER-GROUP-OPTIMIZATION; + type boolean; + default false; + description + "This flag is applicable for O-RU adevertised static-level-tx-endpoint advertises for which + 'se-20-multi-sd-punc-pattern-suported' = TRUE. The flag is configured by the O-DU to TRUE/FALSE. + - Value of the flag set to 'TRUE' indicates O-DU shall set the value 'multiSDScope' in SE-20 to TRUE/FALSE. + - Value of the flag set to 'FALSE' indicates O-DU shall not set the value 'multiSDScope' in SE-20. The corresponding + fields shall be interpretted as reserved field by the O-RU."; + } + + container combination-configuration { + leaf endpoint-type { + type leafref { + path "/user-plane-configuration/static-low-level-tx-endpoints[name=current()/../../name]/endpoint-type"; + require-instance false; + } + description + "Leafref to the endpoint-type of the endpoint."; + } + + leaf combination-id { + type leafref { + path "/user-plane-configuration/endpoint-types[id = current()/../endpoint-type]/supported-configuration-combinations/combination-id"; + require-instance false; + } + description + "A parameter describing which configuration combination will be used by the endpoint."; + } + + list configurations { + key id; + + leaf id { + type uint32; + description + "Identifier of an entry in the list"; + } + + leaf set-id { + type leafref { + path "/user-plane-configuration/endpoint-types[id = current()/../../endpoint-type]/supported-configuration-combinations[combination-id = current()/../../combination-id]/set/set-id"; + require-instance false; + } + description + "A parameter describing which set will be used by the endpoint."; + } + + leaf config-id { + type leafref { + path "/user-plane-configuration/endpoint-types[id = current()/../../endpoint-type]/supported-configuration-combinations[combination-id = current()/../../combination-id]/set[set-id = current()/../set-id]/config/config-id"; + require-instance false; + } + description + "A parameter describing which config will be used by the endpoint."; + } + + description + "An optional list describing which configurations will be used by the endpoint. + The list can be provided by an O-DU, so that an O-RU can optimize the processing in the endpoint. + If the list is empty, then O-DU intends to use every capability within combination identified by combination-id."; + } + + description + "An optional container describing which configuration combination will be used by the endpoint. + The container can be provided by an O-DU, so that an O-RU can optimize the processing in the endpoint."; + } + + leaf configured-bf-profile-id { + if-feature feat:BF-DELAY-PROFILE; + type leafref { + path "/user-plane-configuration/endpoint-types/bf-profile-group"; + require-instance false; + } + description + "Configure the beamforming method list and related delay profiles will be used by the endpoint. + The configured-bf-profile-id must be one of the supported bf-profile-id(s) reported by the endpiont-type related to the endpoint. + If O-DU does not set configured-bf-profile-id, the O-RU shall not support the feature."; + } + + leaf-list bf-methods { + if-feature "feat:SELECTED-BF-METHOD-CONFIGURATION and feat:BF-DELAY-PROFILE"; + type beamforming-method-type; + description + "Beamforming method(s) configured on the endpoint. + + If the value of configured-bf-profile-id is set, then beamforming method(s) configured on the endpoint shall + be selected from the set of supported beamforming methods of the selected endpoint-bf-profile-group + Otherwise, the beamforming method(s) configured on the endpoint shall be selected from the set of supported + beamforming methods reported in the related endpoint-bf-profile-group(s)."; + } } list low-level-rx-endpoints { @@ -1673,7 +4396,7 @@ module o-ran-uplane-conf { description "Object model for low-level-rx-endpoint configuration - augmented static-low-level-rx-endpoints by local-address - which cannot be added to static low-level-rx-endpoints as we cannot have modificable element in static object"; + which cannot be added to static low-level-rx-endpoints as we cannot have modifiable element in static object"; leaf name { type leafref { @@ -1686,49 +4409,21 @@ module o-ran-uplane-conf { "Unique name of low-level-rx-endpoint object. Reference to static object"; } - container compression { + leaf sro-id { + if-feature feat:SHARED-ORU-MULTI-OPERATOR; + type leafref { + path "/or-user:users/or-user:user/or-user:sro-id"; + } description - "Container which consists of global configurable parameters for compression"; - - uses cf:compression-details; - - leaf fs-offset { - if-feature cf:CONFIGURABLE-FS-OFFSET; - type uint8; - default 0; - description - "Adjusts FS (full scale) value of IQ format relative to FS derived from unmodified IQ format. - Please refer to CU-Plane specification for details"; - } - - list dynamic-compression-configuration { - when "../compression-type = 'DYNAMIC'"; - key "id"; - unique "compression-method iq-bitwidth fs-offset"; - description - "List of possible configuration in case dynamic configuration is used - Note: In case of empty list all available compressions can be choosen dynamically - and default fs-offset is taken (0)."; - - leaf id { - type uint16; - description - "Identification number for particular compression"; - } - - uses cf:compression-method-grouping; - - leaf fs-offset { - if-feature cf:CONFIGURABLE-FS-OFFSET; - type uint8; - default 0; - description - "Adjusts FS (full scale) value of IQ format relative to FS derived from unmodified IQ format. - Please refer to CU-Plane specification for details"; - } - } + "An optional shared resource operator associated with this list entry. + When present, indicates that the list entry corresponds to a low-level-rx-endpoint + associated with a shared resource operator where the sro-id identifies + a specific shared resource operator. + This schema-node is used to refine NACM privileges for shared + resource operators"; } + uses rx-endpoint-compression-grouping; uses scs-config; list ul-fft-sampling-offsets { @@ -1751,8 +4446,11 @@ module o-ran-uplane-conf { description "Determines time advance of capture window for FFT. Value represents time advance of capture window start in relation to the end of CP. Unit is Ts. + The value of this parameter shall not be larger than the duration of the cyclic prefix; in case of + variability of the cyclic prefix duration, the limit is the shortest duration of the cyclic prefix in use. Note: value of this parameter is usually set to '0' (zero) for PRACH channels. - Any phase offset resulting from the non-zero value of this parameter is handled in O-DU."; + Any phase offset resulting from the non-zero value of this parameter is handled in O-DU. + Please refer to the O-RAN CUS-Plane, clause 4.4.3 for more details on the intended usage and value restrictions."; } } @@ -1760,7 +4458,7 @@ module o-ran-uplane-conf { uses eaxc; description - "Contains local address of low level RX endpoint offered by Netconf server."; + "Contains local address of low level RX endpoint offered by NETCONF server."; } leaf eaxc-gain-correction { @@ -1771,7 +4469,7 @@ module o-ran-uplane-conf { units dB; default 0; description - "eAxC specifc part of overall gain_correction. + "eAxC specific part of overall gain_correction. gain_correction = common array-carrier gain-correction + eAxC-gain-correction."; } @@ -1810,7 +4508,7 @@ module o-ran-uplane-conf { } description "This parameter creates reference to static SRS configuration applicable for particular endpoint"; - } + } leaf configurable-tdd-pattern-supported { if-feature mcap:CONFIGURABLE-TDD-PATTERN-SUPPORTED; @@ -1820,6 +4518,285 @@ module o-ran-uplane-conf { } description "RO to RW parameter mapping - needed for conditional under rx-array-carrier"; } + + uses u-plane-transmission-control; + + leaf ordered-transmission { + if-feature feat:ORDERED-TRANSMISSION; + type boolean; + default false; + description + "Commands O-RU to order UL U-plane message transmission between endpoints + Meaningless when ordered transmission is not supported for particular endpoint."; + } + + leaf cplane-message-processing-limits-enabled { + if-feature feat:CPLANE-MESSAGE-PROCESSING-LIMITS; + type boolean; + default false; + description + "TRUE: O-DU shall support C-Plane message processing limits and has selected to adhere + to the C-Plane limits defined in 'per-cplane-message-limits'. + FALSE: O-DU shall not support C-Plane message processing limits and hence C-Plane and + endpoint limits defined in 'per-cplane-message-limits' shall not apply"; + } + + leaf beam-update-contention-control-enabled { + if-feature feat:BEAM-UPDATE-CONTENTION-CONTROL; + type boolean; + default false; + description + "TRUE: O-DU shall support beam update contention control feature of O-RU.Thus requiring O-DU to comply with beam + updates restrictions defined in CUS-Plane Spec Section 'Weight-based dynamic beamforming'. In this case applicable + beam limits defined in 'beam-update-contention-control-limits' shall apply. + FALSE: O-DU shall not support beam update contention control feature of O-RU. Applicable beam limits defined in + 'endpoint-beam-capacity' shall apply"; + } + + leaf non-scheduled-ueid-enabled{ + if-feature feat:NON-SCHEDULED-UEID; + type boolean; + default false; + description + "If O-DU configures 'non-scheduled-ueid-enabled' = TRUE, + and Section Extension 10 is applied to Section Types 5 and beamGroupType = 10b, + and the O-DU set ueId of a port in Section Extension 10 to 0x7FFF, + the endpoint shall interpret that the PRBs in the section description are not scheduled for this port. + The endpoint only interprets this leaf if non-scheduled-ueid-supported = true is reported in the related static endpoint, in other cases this leaf is ignored. + Please refer to CUS specification clause 7.9.13 for more details."; + } + + leaf center-from-freqoffset-enabled { + type boolean; + default false; + description + "Endpoint is enabled / prohibited to autonomously calculate center frequency based on frequency offset."; + } + + leaf user-group-optimization-enabled-rx{ + if-feature feat:USER-GROUP-OPTIMIZATION; + type boolean; + default false; + description + "The parameter indicates that if the O-RU advertises the feature flag to 'user-group-optimization-supported' set to + TRUE for associated static-level-rx-endpoint, the O-DU may choose to set this flag to 'TRUE' or 'FALSE'. + - If set to 'TRUE', the O-DU shall be required to send all layers for a given user group UEs i.e., + UEs with same time-frequency allocation using single C-Plane section description. + - If set to 'FALSE', the O-DU restriction to send all layers for a given user group UEs shall not apply."; + } + + leaf se-20-multi-sd-punc-pattern-enabled-rx { + if-feature feat:SE20-MULTI-SD-PUNC-PATTERN-SUPPORT; + if-feature feat:USER-GROUP-OPTIMIZATION; + type boolean; + default false; + description + "This flag is applicable for O-RU adevertised static-level-rx-endpoint advertises for which + 'se-20-multi-sd-punc-pattern-suported' = TRUE. The flag is configured by the O-DU to TRUE/FALSE. + - Value of the flag set to 'TRUE' indicates O-DU shall set the value 'multiSDScope' in SE-20 to TRUE/FALSE. + - Value of the flag set to 'FALSE' indicates O-DU shall not set the value 'multiSDScope' in SE-20. The corresponding + fields shall be interpretted as reserved field by the O-RU."; + } + + container combination-configuration { + leaf endpoint-type { + type leafref { + path "/user-plane-configuration/static-low-level-rx-endpoints[name=current()/../../name]/endpoint-type"; + require-instance false; + } + description + "Leafref to the endpoint-type of the endpoint."; + } + + leaf combination-id { + type leafref { + path "/user-plane-configuration/endpoint-types[id = current()/../endpoint-type]/supported-configuration-combinations/combination-id"; + require-instance false; + } + description + "A parameter describing which configuration combination will be used by the endpoint."; + } + + list configurations { + key id; + + leaf id { + type uint32; + description + "Identifier of an entry in the list"; + } + + leaf set-id { + type leafref { + path "/user-plane-configuration/endpoint-types[id = current()/../../endpoint-type]/supported-configuration-combinations[combination-id = current()/../../combination-id]/set/set-id"; + require-instance false; + } + description + "A parameter describing which set will be used by the endpoint."; + } + + leaf config-id { + type leafref { + path "/user-plane-configuration/endpoint-types[id = current()/../../endpoint-type]/supported-configuration-combinations[combination-id = current()/../../combination-id]/set[set-id = current()/../set-id]/config/config-id"; + require-instance false; + } + description + "A parameter describing which config will be used by the endpoint."; + } + + description + "An optional list describing which configurations will be used by the endpoint. + The list can be provided by an O-DU, so that an O-RU can optimize the processing in the endpoint."; + + } + + description + "An optional container describing which configuration combination will be used by the endpoint. + The container can be provided by an O-DU, so that an O-RU can optimize the processing in the endpoint."; + } + + leaf configured-bf-profile-id { + if-feature feat:BF-DELAY-PROFILE; + type leafref { + path "/user-plane-configuration/endpoint-types/bf-profile-group"; + require-instance false; + } + description + "Configure which beamforming method list and related delay profile will be used by the endpoint. + The configured-bf-profile-id must be one of the supported bf-profile-id(s) reported by the endpiont-type related to the endpoint. + If O-DU does not set configured-bf-profile-id, the O-RU shall not support the feature."; + } + + leaf sinr-reporting-enabled { + if-feature feat:DMRS-BF-EQ; + type boolean; + default false; + description + "Enables SINR reporting for the endpoint."; + } + + container sinr-reporting-configuration { + when "../sinr-reporting-enabled = 'true'"; + if-feature feat:DMRS-BF-EQ; + description + "Container collecting parameters related to configuration of SINR reporting"; + + container sinr-resolution { + description + "Container collecting parameters related to configuration of SINR reporting + resolution. + Supported values are constrained by the values reported in endpoint-types/supported-sinr-resolutions. + + Please refer to CUS specification clause 7.2.10 for more details."; + leaf sinr-per-prb { + type uint8; + description + "Number of SINR values per PRB."; + } + leaf sinr-slot-mask { + type uint16; + description + "Pattern of SINR reports represented as a bitmask."; + } + } + container sinr-compression { + description + "Configuration of SINR compression."; + uses cf:sinr-compression-details; + leaf sinr-reference-level-config { + type decimal64 { + fraction-digits 4; + } + units dB; + description + "SINR reference level."; + } + + leaf sinr-reference-level-used { + type decimal64 { + fraction-digits 4; + } + units dB; + config false; + description + "SINR reference level used. This parameter shall be the O-RU's best approximation of the sinr-reference-level-config."; + } + } + } + leaf se26-usage-enabled { + type boolean; + default false; + description + "When se26-usage-enabled = FALSE, O-DU shall not use SE 26 and shall not convey UE frequency offset information. + When se26-usage-enabled = TRUE, O-DU may convey UE frequency offset information via SE 26."; + } + + leaf-list bf-methods { + if-feature "feat:SELECTED-BF-METHOD-CONFIGURATION and feat:BF-DELAY-PROFILE"; + type beamforming-method-type; + description + "Beamforming method(s) configured on the endpoint. + + If the value of configured-bf-profile-id is set, then beamforming method(s) configured on the endpoint shall + be selected from the set of supported beamforming methods of the selected endpoint-bf-profile-group + Otherwise, the beamforming method(s) configured on the endpoint shall be selected from the set of supported + beamforming methods reported in the related endpoint-bf-profile-group(s)."; + } + leaf eq-scale-offset-config { + if-feature "feat:DMRS-BF-EQ"; + type decimal64 { + fraction-digits 4; + } + units dB; + description + "scale offset is configured by the O-DU to avoid overflow when encoding data for the fronthaul interface.Refer to cus specification clause 8.1.3.4 for details."; + } + leaf eq-scale-offset-used { + if-feature "feat:DMRS-BF-EQ"; + type decimal64 { + fraction-digits 4; + } + units dB; + config false; + description + "scale offset is used by the O-RU to avoid overflow when encoding data for the fronthaul interface.Refer to cus specification clause 8.1.3.4 for details."; + } + leaf-list rrm-meas-enabled { + if-feature feat:RRM-MEAS-REPORTING; + type rrm-meas-types; + description + "This parameter indicates which rrm measurements are enabled for this low level endpoint"; + } + + leaf se-27-odu-controlled-dimensionality-reduction-enabled { + if-feature feat:SE27-ODU-CONTROLLED-DIMENSIONALITY-REDUCTION; + type enumeration { + enum DISABLED { + description + "O-DU controlled dimnesionality reduction disabled."; + } + enum STATIC { + description + "Static O-DU controlled dimnesionality reduction enabled."; + } + enum DYNAMIC { + description + "Dynamic O-DU controlled dimnesionality reduction enabled."; + } + } + default DISABLED; + description + "Parameter indicating if O-DU controlled dimnesionality reduction is enabled. + Please refer to CUS specification clause 7.7.27 for more details."; + } + leaf se-27-num-elements { + when "../se-27-odu-controlled-dimensionality-reduction-enabled = 'STATIC'"; + if-feature feat:SE27-ODU-CONTROLLED-DIMENSIONALITY-REDUCTION; + type uint16; + description + "Configured value of numElements in SE27. + Please refer to CUS specification clause 7.7.27 for more details."; + } } list tx-array-carriers { @@ -1829,10 +4806,49 @@ module o-ran-uplane-conf { leaf name { type string; - description "Unique name of tx-array-carriers object."; } + list odu-ids { + if-feature feat:SHARED-ORU-MULTI-ODU; + key odu-id; + description + "An optional list of o-du identities associated with this list entry. + When present, indicates that the list entry corresponds to a tx-array-carrier + associated with one or more particular odu-id(s). + + This list is used to enable enhanced watchdog operation when operating + in a single operator environment, where watchdog supervision is performed + on a per odu-id basis and supervision failure only results in a sub-set + of carriers being de-activated."; + + leaf odu-id { + type string; + description "an o-du identity "; + } + } + + list sro-ids-and-odu-ids { + if-feature "feat:SHARED-ORU-MULTI-ODU and feat:SHARED-ORU-MULTI-OPERATOR"; + key "odu-id sro-id"; + description + "An optional list of sro and o-du identities associated with this list entry. + When present, indicates that the list entry corresponds to a tx-array-carrier + associated with one or more particular odu-id(s) operated by a particular sro-id. + + This list is used to enable enhanced watchdog operation when operating + in a multi operator environment, where watchdog supervision is performed on + a per odu-id basis and supervision failure only results in a sub-set of + carriers being de-activated."; + leaf odu-id { + type string; + description "an o-du identity "; + } + leaf sro-id { + type string; + description "an sro identity "; + } + } uses tx-common-array-carrier-elements; @@ -1845,7 +4861,7 @@ module o-ran-uplane-conf { description "This parameter informing which frequency band particular antenna array is serving for. - Intended use is to deal with multiband solutions."; + Intended use is to deal with multi-band solutions."; } container lte-tdd-frame { @@ -1943,7 +4959,9 @@ module o-ran-uplane-conf { mandatory true; description - "Transmission gain in dB. Value applicable to each array element carrier belonging to array carrier."; + "Transmission gain in dB. Value applicable to each array element carrier belonging to array carrier. + + The value of transmission gain shall meet the constraints defined in CUS-Plane, clause 8.1.3.3."; } leaf downlink-radio-frame-offset { @@ -1954,8 +4972,8 @@ module o-ran-uplane-conf { description "This parameter is used for offsetting the starting position of 10ms radio frame. - Note: The value should have same value within DU to all tx-array-carrierss that have same frequency and bandwidth. - Note2: Unit is 1/1.2288e9 Hz and accuracy is 1/4 Tc. Then, its range is calculated 0..12288000."; + Note: The value should have same value within DU to all tx-array-carriers that have same frequency and bandwidth. + Note2: Unit is 1/1.2288e9 s. Then, its range is calculated 0..12288000."; } leaf downlink-sfn-offset { @@ -1967,7 +4985,22 @@ module o-ran-uplane-conf { description "This parameter is used for offsetting SFN value. Unit is in 10ms. - Note: The value should have same value within DU to all tx-array-carrierss that have same frequency and bandwidth."; + Note: The value should have same value within DU to all tx-array-carriers that have same frequency and bandwidth."; + } + + leaf t-da-offset { + if-feature "feat:EXT-ANT-DELAY-CONTROL"; + type uint32; + units Tc; + default 0; + description + "the time difference between the output of DL signal at the + antenna connector of O-RU and the transmission over the air. + units are Tc=~0.5ns=1/1.96608GHz. + An O-RU with 'ext-ant-delay-capability' = 'PER-O-RU' shall reject any configuration + where different values of t-da-offset are configured on tx-array-carriers; + An O-RU with 'ext-ant-delay-capability' = 'PER-ARRAY' shall reject any configuration + where different values of t-da-offset are configured on tx-array-carriers associated with the same tx-array."; } leaf reference-level { @@ -1992,6 +5025,22 @@ module o-ran-uplane-conf { The leaf may exist under tx-array-carrier only in case O-RU supports feature 'CONFIGURABLE-TDD-PATTERN-SUPPORTED' AND all low-level-tx-endpoints linked to this tx-array-carrier have configurable-tdd-pattern-supported = 'true'"; } + + leaf crb-to-prb-grid-offset { + if-feature "feat:PRG-SIZE-SUPP-SE-21-WITH-ST6 or feat:PRG-SIZE-SUPP-SE-21-WITH-ST5"; + type uint16 { + range 0..2199; + } + default 0; + description + "This parameter specifies the value of 'offsetToCarier' parameter as specified in the 3GPP Specification TS 38.331. + This value is the offset in frequency domain, between lowest subcarrier of common RB 0, which is the start of + carrier resource Block(CRB grid) and the lowest usable subcarrier on this array-carrier, which is (RE0 of PRB0), + in units of number of PRBs (using the SCS defined for associated array-carrier). This parameter shall be used with + 'prgSize' in Section Extension 21 by the O-RU to correctly derive Channel Information Based Beamforming weights. + Value of this parameter is not interpretted by the O-RU, if either 'prg-size-supp-se-21-with-st6-enabled' + or 'prg-size-supp-se-21-with-st5-enabled' parameter is not set to TRUE by the O-DU."; + } } list rx-array-carriers { @@ -2005,6 +5054,47 @@ module o-ran-uplane-conf { "Unique name of rx-array-carriers object."; } + list odu-ids { + if-feature feat:SHARED-ORU-MULTI-ODU; + key odu-id; + description + "An optional list of o-du identities associated with this list entry. + When present, indicates that the list entry corresponds to a rx-array-carrier + associated with one or more particular odu-id(s). + + This list is used to enable enhanced watchdog operation when operating + in a single operator environment, where watchdog supervision is performed + on a per odu-id basis and supervision failure only results in a sub-set + of carriers beign de-activated."; + + leaf odu-id { + type string; + description "an o-du identity "; + } + } + + list sro-ids-and-odu-ids { + if-feature "feat:SHARED-ORU-MULTI-ODU and feat:SHARED-ORU-MULTI-OPERATOR"; + key "odu-id sro-id"; + description + "An optional list of sro and o-du identities associated with this list entry. + When present, indicates that the list entry corresponds to a rx-array-carrier + associated with one or more particular odu-id(s) operated by a particular sro-id. + + This list is used to enable enhanced watchdog operation when operating + in a multi operator environment, where watchdog supervision is performed on + a per odu-id basis and supervision failure only results in a sub-set of + carriers beign de-activated."; + leaf odu-id { + type string; + description "an o-du identity "; + } + leaf sro-id { + type string; + description "an sro identity "; + } + } + uses rx-common-array-carrier-elements; leaf downlink-radio-frame-offset { @@ -2015,8 +5105,8 @@ module o-ran-uplane-conf { description "This parameter is used for offsetting the starting position of 10ms radio frame. - Note: The value should have same value within DU to all tx-array-carrierss that have same frequency and bandwidth. - Note2: Unit is 1/1.2288e9 Hz and accuracy is 1/4 Tc. Then, its range is calculated 0..12288000."; + Note: The value should have same value within DU to all tx-array-carriers that have same frequency and bandwidth. + Note2: Unit is 1/1.2288e9 s. Then, its range is calculated 0..12288000."; } leaf downlink-sfn-offset { @@ -2028,7 +5118,7 @@ module o-ran-uplane-conf { description "This parameter is used for offsetting SFN value. Unit is in 10ms. - Note: The value should have same value within DU to all tx-array-carrierss that have same frequency and bandwidth."; + Note: The value should have same value within DU to all tx-array-carriers that have same frequency and bandwidth."; } leaf gain-correction { @@ -2053,6 +5143,21 @@ module o-ran-uplane-conf { units are Tc=~0.5ns=1/1.96608GHz"; } + leaf t-au-offset { + if-feature "feat:EXT-ANT-DELAY-CONTROL"; + type uint32; + units Tc; + default 0; + description + "the time difference between the reception over the air and + the input of UL signal at the antenna connector of O-RU. + units are Tc=~0.5ns=1/1.96608GHz. + An O-RU with 'ext-ant-delay-capability' = 'PER-O-RU' shall reject any configuration + where different values of t-au-offset are configured on rx-array-carriers; + An O-RU with 'ext-ant-delay-capability' = 'PER-ARRAY' shall reject any configuration + where different values of t-au-offset are configured on rx-array-carriers associated with the same rx-array."; + } + leaf configurable-tdd-pattern { when "not(/user-plane-configuration/low-level-rx-endpoints[name = string(/user-plane-configuration/low-level-rx-links[rx-array-carrier = current()/../name]/rx-array-carrier)]/configurable-tdd-pattern-supported = 'false')"; if-feature mcap:CONFIGURABLE-TDD-PATTERN-SUPPORTED; @@ -2064,6 +5169,53 @@ module o-ran-uplane-conf { The leaf may exist under rx-array-carrier only in case O-RU supports feature 'CONFIGURABLE-TDD-PATTERN-SUPPORTED' AND all low-level-rx-endpoints linked to this rx-array-carrier have configurable-tdd-pattern-supported = 'true'"; } + + leaf user-group-mode { + if-feature "feat:USER-GROUP-SELF-ASSEMBLY"; + type enumeration { + enum NO-SELF-ASSEMBLY { + description "No user group self assembly"; + } + enum SELF-ASSEMBLY-WITH-GROUP-ID { + description "User group self assembly with group id."; + } + enum SELF-ASSEMBLY-WITHOUT-GROUP-ID { + description "User group self assembly without group id."; + } + } + default NO-SELF-ASSEMBLY; + description + "Configuration of user group self assembly."; + } + + leaf point-a-offset-to-absolute-frequency-center { + if-feature "(feat:DMRS-BF-EQ or feat:DMRS-BF-NEQ) and feat:POINT-A-OFFSET-TO-ABSOLUTE-FREQUENCY-CENTER"; + type int32; + description + "An offset between center-of-channel-bandwidth and Point A. + If the value is not provided, then Point A coincides with RE#0 of PRB#0 indicated by offset-to-absolute-frequency-center. + The unit of this parameter is 1/2 of reference SCS, where reference SCS is 15 kHz for FR1 and 60 kHz for FR2. + + Please refer to CUS specification clause 7.7.24 for more details."; + } + + leaf continuity-block-size-configured { + if-feature "(feat:DMRS-BF-EQ or feat:DMRS-BF-NEQ) and feat:CONTINUITY-BLOCK-SIZE"; + type uint8; + description + "This parameter specified the O-DU configured value of continuity block size per rx-array-carrier. + The O-DU shall pick one of the values advertised by the O-RU in 'continuity-block-size-list'"; + } + + leaf port-reduced-dmrs-data-sending-enabled { + if-feature "feat:DMRS-BF-EQ or feat:DMRS-BF-NEQ"; + type boolean; + description + "This parameter value if set to TRUE indicates O-DU request to O-RU for sending port reduced DMRS + sending from O-RU. If the flag is set to FALSE indicates O-RU shall not send port reduced DMRS + from O-RU to O-DU."; + } + } list tx-arrays { @@ -2074,6 +5226,16 @@ module o-ran-uplane-conf { uses parameters; + leaf min-gain { + type decimal64 { + fraction-digits 4; + + } + units dB; + description + "Min gain of RF path linked with array element (maximum over elements of array) or array layers"; + } + leaf max-gain { type decimal64 { fraction-digits 4; @@ -2090,13 +5252,38 @@ module o-ran-uplane-conf { mandatory true; description "If true then every element of array has own, power budget independent from power budget of other elements. - Else all elements of array that are at same row and column and have same polarization share power budget"; + Else all elements of array that are at same row and column and have same polarisation share power budget"; } list capabilities { description "List of capabilities related to this tx-array"; uses mcap:support-for-dl; + + container supported-energy-saving-capabilities-dl { + description + "Network energy saving capabilities supported in DL path"; + + container trx-control-capability-info { + if-feature feat:TRX-CONTROL; + description + "A container with parameters for TRX-CONTROL capabilities supported by the O-RU."; + uses mcap:trx-control-capability-info; + } + + container asm-capability-info { + if-feature feat:ADVANCED-SLEEP-MODE; + description + "A container with parameters for advanced sleep mode capabilities supported by the O-RU."; + uses mcap:asm-capability-info; + } + } + } + container mplane-trx-control-txarr-capability-info { + if-feature feat:MPLANE-TRX-CONTROL; + description + "A container with parameters for MPLANE-TRX-CONTROL feature supported by the O-RU."; + uses mplane-trx-control-supp-antenna-masks; } } @@ -2132,6 +5319,32 @@ module o-ran-uplane-conf { description "List of capabilities related to this rx-array"; uses mcap:support-for-ul; + + container supported-energy-saving-capabilities-ul { + description + "Network energy saving capabilities supported in UL path"; + + container trx-control-capability-info { + if-feature feat:TRX-CONTROL; + description + "A container with parameters for TRX-CONTROL capabilities supported by the O-RU"; + uses mcap:trx-control-capability-info; + } + + container asm-capability-info { + if-feature feat:ADVANCED-SLEEP-MODE; + description + "A container with parameters for advanced sleep mode capabilities supported by the O-RU"; + uses mcap:asm-capability-info; + } + } + } + container mplane-trx-control-rxarr-capability-info { + if-feature feat:MPLANE-TRX-CONTROL; + description + "A container with parameters for MPLANE-TRX-CONTROL feature supported by the O-RU."; + + uses mplane-trx-control-supp-antenna-masks; } } @@ -2151,13 +5364,13 @@ module o-ran-uplane-conf { uses array-choice; description - "Defnes name for first array"; + "Defines name for first array"; } container array2 { uses array-choice; description - "Defnes name for second array"; + "Defines name for second array"; } list types { key "relation-type"; @@ -2248,7 +5461,7 @@ module o-ran-uplane-conf { type uint16; description "This parameter contains eAxC_ID that populates content of C-Plane section - extension 11 to eAxC_IDs configured in the group as 'member-tx-eaxc-id'(s)."; + extension 10 to eAxC_IDs configured in the group as 'member-tx-eaxc-id'(s)."; } leaf-list member-tx-eaxc-id { type uint16; @@ -2258,9 +5471,13 @@ module o-ran-uplane-conf { must "current()!=../representative-tx-eaxc-id" { error-message "the representative eaxcid does not need to be a list member"; } + ordered-by user; description "This is a list of member eAxC IDs, which together with the representative-tx-eaxc-id, - are assigned to low-level-tx-endpoints in the group."; + are assigned to low-level-tx-endpoints in the group. + This list is defined as 'ordered-by user', because the order of the eaxc-id list should be maintained, + and the parameters in the SE10 are applied to the eaxc-ids based on the order of eaxc-ids in the list + when the section extension conveys unique parameters (beamId/ueId) per eaxc-id."; } } @@ -2282,7 +5499,7 @@ module o-ran-uplane-conf { type uint16; description "This parameter contains eAxC_ID that populates content of C-Plane section - extension 11 to eAxC_IDs configured in the group as 'member-rx-eaxc-id'(s)."; + extension 10 to eAxC_IDs configured in the group as 'member-rx-eaxc-id'(s)."; } leaf-list member-rx-eaxc-id { @@ -2291,10 +5508,14 @@ module o-ran-uplane-conf { error-message "too many rx-eaxcid-id members"; } must "current()!=../representative-rx-eaxc-id" { - error-message "the representative eaxcid does not need to be a list member"; + error-message "the representative eaxc-id does not need to be a list member"; } + ordered-by user; description - "This is a list of member eAxC IDs assigned to low-level-rx-endpoints in the group."; + "This is a list of member eAxC IDs assigned to low-level-rx-endpoints in the group. + This list is defined as 'ordered-by user', because the order of the eaxc-id list should be maintained, + and the parameters in the SE10 are applied to the eaxc-ids based on the order of eaxc-ids in the list + when the section extension conveys unique parameters (beamId/ueId) per eaxc-id."; } } } @@ -2320,6 +5541,17 @@ module o-ran-uplane-conf { description "Set of parameters related to static PRACH configuration"; + leaf sfn-offset { + type uint32; + + description + "This parameter is used for offsetting SFN value. + The SFN that results from the offset is used as the reference for the start of pattern periods. + If the value is not set, then pattern periods follow the reference set in a related rx-array-carrier. + The value is primarily used for cases in which pattern-period is not a power of 2. + Unit is in 10ms."; + } + leaf pattern-period { type uint16 { range 1..1024; @@ -2357,20 +5589,23 @@ module o-ran-uplane-conf { units Ts; mandatory true; description - "Duration of single sequence of the PRACH. Sequence may be considered as 'single PRACH symbol'"; + "Duration of single PRACH Symbol. Refer to definition of PRACH Symbol in CUS specification clause 3.1."; } list prach-patterns { key prach-pattern-id; min-elements 1; description - "Provides a PRACH pattern. Each record in the list represents a single PRACH occasion. Number of list entries cannot exceed max-prach-patterns"; + "Provides a PRACH pattern. Each record in the list represents a single PRACH occasion. Number of list entries cannot + exceed max-prach-patterns or extended-max-prach-patterns if EXTENDED-PRACH-CONFIGURATION is supported."; leaf prach-pattern-id { - type uint16; + type uint32; mandatory true; description - "Supplementary parameter acting as key for prach-pattern list."; + "Supplementary parameter acting as key for prach-pattern list. + NETCONF client shall limit its maximum value to 65535 when configuring + any O-RU that does not support the EXTENDED-PRACH-CONFIGURATION feature."; } leaf number-of-repetitions { @@ -2379,15 +5614,17 @@ module o-ran-uplane-conf { } mandatory true; description - "This parameter defines number of PRACH repetitions in PRACH occasion, - to which the section control is applicable."; + "This parameter defines number of PRACH PRACH Symbol repetitions in PRACH occasion, + to which the section control is applicable. Refer to definition of PRACH Symbol in CUS specification clause 3.1."; } leaf number-of-occasions { - type uint8; + type uint32; mandatory true; description - "This parameter informs how many consecutive PRACH occasions is described by the PRACH pattern."; + "This parameter informs how many consecutive PRACH occasions is described by the PRACH pattern. + NETCONF client shall limit its maximum value to 255 when configuring any O-RU that does not + support the EXTENDED-PRACH-CONFIGURATION feature."; } leaf re-offset { @@ -2409,10 +5646,11 @@ module o-ran-uplane-conf { to value of parameter number-of-occasions."; leaf occasion-id { - type uint8; + type uint32; mandatory true; description - "Supplementary parameter acting as key in 'occasion-parameters' list"; + "Supplementary parameter acting as key in 'occasion-parameters' list. + The value is limited to 255 if EXTENDED-PRACH-CONFIGURATION is unsupported."; } leaf cp-length { @@ -2431,7 +5669,9 @@ module o-ran-uplane-conf { } leaf beam-id { - type uint16; + type uint16 { + range "min .. 32767"; + } mandatory true; description "This parameter defines the beam pattern to be applied to the U-Plane data. @@ -2520,7 +5760,9 @@ module o-ran-uplane-conf { } leaf beam-id { - type uint16; + type uint16 { + range "min .. 32767"; + } mandatory true; description "This parameter defines the beam pattern to be applied to the U-Plane data. @@ -2531,8 +5773,8 @@ module o-ran-uplane-conf { type uint16; mandatory true; description - "This parameter defines number of consecutive symbols covered by specific srs-pattern. - Single srs-pattern may address at least one symbol. However, possible optimizations + "This parameter defines number of consecutive symbols covered by specific srs-pattern. + Single srs-pattern may address at least one symbol. However, possible optimisations could allow for several (up to 14) symbols."; } @@ -2596,7 +5838,7 @@ module o-ran-uplane-conf { type uint32; mandatory true; description - "Offset from DL air frame boundary transmitted at RF connector to the point in time that is characteristic to the operation on RF switches. Unit is 1/1.2288e9 Hz and accuracy is 1/4 Tc."; + "Offset from DL air frame boundary transmitted at RF connector to the point in time that is characteristic to the operation on RF switches. Unit is 1/1.2288e9 s."; } } } @@ -2630,6 +5872,79 @@ module o-ran-uplane-conf { uses configurable-tdd-pattern; } + + list endpoint-bf-profile-group { + if-feature feat:BF-DELAY-PROFILE; + key "bf-profile-id"; + config false; + description + "An optional list of list(s) containing beamforming methods supported by the O-RU with the associated delay-profile-id + per each beamforming method list"; + leaf bf-profile-id { + type uint16; + description + "Identifies the beamforming method list."; + } + leaf-list supported-bf-methods { + type beamforming-method-type; + min-elements 1; + description + "Beamforming methods can be supported simultaneously."; + } + leaf supported-delay-profile { + type leafref { + path "/or-dm:delay-management/or-dm:non-default-ru-delay-profile/or-dm:delay-profile-id"; + require-instance false; + } + description + "The delay-profile-id associated with the supported beamforming method list. + If not configured or the value is set to 0, all delay parameters shall be equivalent to the default value."; + } + } + list se10-member-candidate-tx-lists { + if-feature feat:SE10-MEMBER-CANDIDATE-LIST; + key "id"; + config false; + description + "List of leaf-lists, where each leaf-list is a member-candidate list comprising of endpoints + which can be associated to one or more static-low-level-tx-endpoint(s)."; + leaf id { + type uint8; + description + "Index into the list se10-member-candidate-tx-lists."; + } + leaf-list member-candidates { + type leafref { + path "/user-plane-configuration/static-low-level-tx-endpoints/name"; + require-instance false; + } + description + "Each leaf-list comprises one or more static-low-elevel-tx-endpoint(s). Endpoints in this list can + be representative endpoint or member endpoint."; + } + } + list se10-member-candidate-rx-lists { + if-feature feat:SE10-MEMBER-CANDIDATE-LIST; + key "id"; + config false; + description + "List of leaf-lists, where each leaf-list is a member-candidate list comprising of endpoints + which can be associated to one or more static-low-level-rx-endpoint(s)."; + leaf id { + type uint8; + description + "Index into the list se10-member-candidate-rx-lists."; + } + leaf-list member-candidates { + type leafref { + path "/user-plane-configuration/static-low-level-rx-endpoints/name"; + require-instance false; + } + description + "Each leaf-list comprises one or more static-low-elevel-rx-endpoint(s). Endpoints in this list can + be representative endpoint or member endpoint."; + } + } } grouping tx-array-notification-group { @@ -2651,8 +5966,15 @@ module o-ran-uplane-conf { type leafref{ path "/user-plane-configuration/tx-array-carriers/state"; } - description - "state of tx-array-carriers is notified at state change"; + description + "state of tx-array-carriers is notified at state change"; + } + leaf availability-status{ + type leafref{ + path "/user-plane-configuration/tx-array-carriers/availability-status"; + } + description + "availability-status of tx-array-carriers is notified at availability-status change"; } } } @@ -2679,16 +6001,23 @@ module o-ran-uplane-conf { description "state of rx-array-carriers is notified at state change"; } + leaf availability-status{ + type leafref{ + path "/user-plane-configuration/rx-array-carriers/availability-status"; + } + description + "availability-status of rx-array-carriers is notified at availability-status change"; + } } } -// top level container - + // top level container container user-plane-configuration { description "top level container for user plane configuration"; uses uplane-conf-group; uses general-config; + uses array-config-group; } //notification statement @@ -2703,4 +6032,68 @@ module o-ran-uplane-conf { uses rx-array-notification-group; } + + notification data-layer-control-wakeup-notification { + if-feature feat:MPLANE-DATA-LAYER-CONTROL; + description + "Notification used to inform about wake-up completion after M-Plane based data layer control sleep is terminated"; + leaf-list energy-sharing-group { + type uint8; + description + "Notification for wake-up completion of underlying HW components corresponding to energy sharing group. + This value shall be specific values from the list of 'energy-sharing-group-id' inside static-low-level-[tr]x-endpoints."; + } + } + notification mplane-trx-control-ant-mask-update { + if-feature feat:MPLANE-TRX-CONTROL; + + leaf array-name { + type union { + type leafref { + path "/user-plane-configuration/rx-arrays/name"; + } + type leafref { + path "/user-plane-configuration/tx-arrays/name"; + } + } + description + "The array name for which antenna mask has been updated using + MPLANE-TRX-CONTROL feature."; + } + leaf array-type { + type enumeration { + enum TX-ARRAY { + description + "this array is of type TX array"; + } + enum RX-ARRAY { + description + "this array is of type RX array"; + } + } + description + "The type of array for which antenna mask has been updated using + MPLANE-TRX-CONTROL feature."; + } + leaf updated-antenna-mask { + type binary; + description + "The updated antenna mask value after processing M-Plane based TRX control command at the O-RU. + If mplane-supported-trx-control-masks list is not advertised as part of capability for the referred [tr]x-array, + the parameter antenna-bitmask shall be used for notification. + The parameter antenna-bitmask-index shall not be used for notification."; + } + leaf updated-antenna-mask-index { + type uint8; + description + "The parameter indicates updated antenna mask index value after processing M-Plane based TRX control command at the O-RU. + If mplane-supported-trx-control-masks list is advertised as part of capability for the referred [tr]x-array, + the parameter antenna-bitmask-index shall be used for notification to refer to the index of advertised mplane-supported-trx-control-masks. + The parameter antenna-bitmask shall not be used for notification."; + } + description + "M-Plane notification event to notify that the commanded mask has been activated via M-plane. This notification + is required to be sent for either tx-array or rx-array for which the antenna mask was + commanded to be changed by the O-DU."; + } } diff --git a/radio/fhi_72/mplane/yang/models/o-ran-usermgmt.yang b/radio/fhi_72/mplane/yang/models/o-ran-usermgmt.yang new file mode 100644 index 0000000000..e8ac1fdeff --- /dev/null +++ b/radio/fhi_72/mplane/yang/models/o-ran-usermgmt.yang @@ -0,0 +1,331 @@ +module o-ran-usermgmt { + yang-version 1.1; + namespace "urn:o-ran:user-mgmt:1.0"; + prefix "o-ran-usermgmt"; + + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + + import o-ran-wg4-features { + prefix "feat"; + } + + import ietf-crypto-types { + prefix ct; + } + + organization "O-RAN Alliance"; + + contact + "www.o-ran.org"; + + description + "This module defines the user management model for the O-RAN Equipment. + + Copyright 2024 the O-RAN Alliance. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the above disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the above disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the Members of the O-RAN Alliance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission."; + + revision "2024-08-12" { + description + "version 16.0.0 + + 1) add SSH Public Key."; + + reference "ORAN-WG4.M.0-v16.00"; + } + + revision "2023-12-11" { + description + "version 10.1.0 + + 1) clarify handling of User-Names."; + + reference "ORAN-WG4.M.0-v10.00"; + } + + revision "2022-08-15" { + description + "version 10.0.0 + + 1) introduced SHARED-ORU-MULTI-OPERATOR feature."; + + reference "ORAN-WG4.M.0-v10.00"; + } + + + revision "2021-12-01" { + description + "version 1.3.0 + + 1) typographical corrections in descriptions"; + + reference "ORAN-WG4.M.0-v01.00"; + } + + revision "2020-12-10" { + description + "version 1.2.0 + + 1) updated description for enabled leaf"; + + reference "ORAN-WG4.M.0-v01.00"; + } + + revision "2019-07-03" { + description + "version 1.1.0 + + 1) change name leaf to type nacm:user-name-type + 2) added account-type to qualify when password is required "; + + reference "ORAN-WG4.M.0-v01.00"; + } + + revision "2019-02-04" { + description + "version 1.0.0 + + 1) imported model from xRAN + 2) changed namespace and reference from xran to o-ran"; + + reference "ORAN-WG4.M.0-v01.00"; + } + + typedef password-type { + type string { + length "8..128"; + pattern "[a-zA-Z0-9!$%\\^()\\[\\]_\\-~{}.+]*" { + error-message "Password content does not meet the requirements"; + } + } + description + "The password for this entry. + The Password must contain at least 2 characters from + each of the following groups: + a) Lower case alphabetic (a-z) + b) Upper case alphabetic (A-Z) + c) Numeric 0-9 + d) Special characters Allowed !$%^()[]_-~{}.+ + Password must not contain Username. + + When a password is received by a NETCONF server, + it should be securely stored in the O-RU's configuration datastore. + How to securely store the password in the datastore is implementation-specific + and out of scope of WG4 M-Plane specification."; + } + + grouping user-list { + description "a user list grouping"; + list user { + key "name"; + description + "The list of local users configured on this device."; + leaf name { + type nacm:user-name-type; + description + "The user name string identifying this entry. + + NOTE: o-ran-usermgmt:user-profile/user/name is + identical to nacm:nacm/groups/group/user-name + but the current schema is preserved for backwards + compatibility."; + } + leaf account-type { + type enumeration { + enum PASSWORD { + description "the user-name is for password based authentication"; + } + enum CERTIFICATE { + description "the user-name is for certificate based authentication"; + } + enum SSHPUBLICKEY { + if-feature feat:CLIENT-AUTH-SSH-PUBLIC-KEY; + description "the user-name is for public key based authentication"; + } + } + default "PASSWORD"; + description "the account type"; + } + + leaf password { + nacm:default-deny-all; + type password-type; + description + "The password for this entry. + + This field is only valid when account-type is PASSWORD, + i.e., when account-type is NOT present or present and set to + PASSWORD."; + } + + container ssh-public-key { + nacm:default-deny-all; + when "../account-type='SSHPUBLICKEY'"; + if-feature feat:CLIENT-AUTH-SSH-PUBLIC-KEY; + uses ct:public-key-grouping; + description + "The SSH pubic key for this entry. + + This field is valid only when account-type is SSHPUBLICKEY."; + } + + leaf enabled { + type boolean; + description + "Indicates whether an account is enabled or disabled. + + A NETCONF Server shall reject a configuration that attempts to + enable a Password account for an account where the password leaf + is not configured. + + This validation statement is included in the YANG description and + not in a MUST statement to preserve backwards compatibility."; + } + leaf-list sro-id { + if-feature feat:SHARED-ORU-MULTI-OPERATOR; + type string; + description + "An optional list if Shared Resource Operator identities associated with the + user-account. Used to realize enhanced access privileges in a shared O-RU. + When an sro-id is configured in the O-RU, the O-RU shall + implement additional sro-id based NETCONF access control + as specified in O-RAN.WG4.MP.0-v10.00. + The O-RU does not further interpret the specific value of sro-id."; + } + } + } + + container users { + must "user/enabled='true'" { + error-message "At least one account needs to be enabled."; + } + //TAKE NOTE - any configuration with zero enabled users is invalid. + //This will typically be the case when using a simulated NETCONF Server + //and so this constraint should be removed when operating in those scenarios + + //The config data base of the O-RAN equipment should ensure that the user + //default account is enabled on factory restart + + description "list of user accounts"; + uses user-list; + } + + rpc chg-password { + description "the RPC used to change a password"; + nacm:default-deny-all; + input { + leaf currentPassword { + type password-type; + mandatory true; + description + "provide the current password"; + } + leaf newPassword { + type password-type; + mandatory true; + description + "provide a new password"; + } + leaf newPasswordConfirm { + type password-type; + mandatory true; + description + "re-enter the new password "; + } + } + output { + leaf status { + type enumeration { + enum "Successful" { + value 1; + description "change password operation is successful"; + } + enum "Failed" { + value 2; + description "change password operation failed"; + } + } + mandatory true; + description + "Successful or Failed"; + } + leaf status-message { + type string; + description + "Gives a more detailed reason for success / failure"; + } + } + } + + rpc chg-ssh-public-key { + if-feature feat:CLIENT-AUTH-SSH-PUBLIC-KEY; + description "the RPC used to change SSH public key"; + nacm:default-deny-all; + input { + container current-ssh-public-key { + uses ct:public-key-grouping; + description + "provide the current SSH public algorithm and key"; + } + container new-ssh-public-key { + uses ct:public-key-grouping; + description + "provide a new SSH public algorithm and key"; + } + container new-ssh-public-key-confirm { + uses ct:public-key-grouping; + description + "re-enter the new SSH public algorithm and key"; + } + } + output { + leaf status { + type enumeration { + enum "Successful" { + value 1; + description "change SSH public key operation is successful"; + } + enum "Failed" { + value 2; + description "change SSH public key operation failed"; + } + } + mandatory true; + description + "Successful or Failed"; + } + leaf status-message { + type string; + description + "Gives a more detailed reason for success / failure"; + } + } + } + +} diff --git a/radio/fhi_72/mplane/yang/models/o-ran-wg4-features.yang b/radio/fhi_72/mplane/yang/models/o-ran-wg4-features.yang new file mode 100644 index 0000000000..1f186ea7d6 --- /dev/null +++ b/radio/fhi_72/mplane/yang/models/o-ran-wg4-features.yang @@ -0,0 +1,532 @@ +module o-ran-wg4-features { + yang-version 1.1; + namespace "urn:o-ran:wg4feat:1.0"; + prefix "o-ran-feat"; + + + organization "O-RAN Alliance"; + + contact + "www.o-ran.org"; + + description + "This module defines the set of re-usable type definitions for WG4 specific + features. + + Copyright 2024 the O-RAN Alliance. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the above disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the above disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the Members of the O-RAN Alliance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission."; + + revision "2024-08-12" { + description + "version 16.0.0 + new features: + + 1) PRG-SIZE-SUPP-SE-21-WITH-ST6 + 2) PRG-SIZE-SUPP-SE-21-WITH-ST5 + 3) USER-GROUP-OPTIMIZATION + 4) BF-DELAY-PROFILE + 5) DMRS-BF-EQ + 6) DMRS-BF-NEQ + 7) RRM-MEAS-REPORTING + 8) USER-GROUP-SELF-ASSEMBLY + 9) UEID-PERSISTENCE + 10) CONTINUITY-BLOCK-SIZE + 11) SELECTED-BF-METHOD-CONFIGURATION + 12) DMRS-BF-NEQ-UNALTERED-FREQ-OFFSET + 13) DMRS-BF-NEQ-UNALTERED-TAE + 14) SE10-MEMBER-CANDIDATE-LIST + 15) MACSEC-BYPASS-POLICY + 16) SE27-ODU-CONTROLLED-DIMENSIONALITY-REDUCTION + 17) SE20-MULTI-SD-PUNC-PATTERN-SUPPORT + 18) CONFIGURABLE-VSWR-THRESHOLDS + 19) CLIENT-AUTH-SSH-PUBLIC-KEY + 20) POINT-A-OFFSET-TO-ABSOLUTE-FREQUENCY-CENTER"; + + reference "ORAN-WG4.M.0-v16.00"; + } + + revision "2024-04-15" { + description + "version 15.0.0 + new features: + + 1) PRG-SIZE-SUPP-SE-21-WITH-ST6 + 2) PRG-SIZE-SUPP-SE-21-WITH-ST5 + 3) BF-DELAY-PROFILE + 4) USER-GROUP-OPTIMIZATION + 5) MULTIPLE-BEAMID-TABLES-SUPPORTED + 6) PER-PORT-PTP-CONFIG + 7) PER-PORT-SYNCE-CONFIG + 8) MPLANE-TRX-CONTROL + 9) PER-INT-TCP-MSS + 10) DEEP-HIBERNATE"; + + reference "ORAN-WG4.M.0-v15.00"; + } + + revision "2023-12-11" { + description + "version 14.0.0 + new features: + + 1) SE23-PRB-BLOCK-MODE-SUPPORT + 2) MPLANE-DATA-LAYER-CONTROL + 3) SHARED-CELL-STATS"; + + reference "ORAN-WG4.M.0-v14.00"; + } + + revision "2023-08-14" { + description + "version 13.0.0 + new features: + + 1) CALL-HOME-REACTIVATION-SUPPORTED + 2) SEQ-ID-CHECKING-CONFIGURABLE + 3) TRX-CONTROL + 4) ADVANCED-SLEEP-MODE"; + + reference "ORAN-WG4.M.0-v13.00"; + } + + revision "2023-04-10" { + description + "version 12.0.0 + new features: + + 1) EXTENDED-PRACH-CONFIGURATION + 2) NON-SCHEDULED-UEID + 3) SE11-WITH-CONTINUITY-BIT-SUPPORT + 4) UPLANE-MESSAGE-PROCESSING-LIMITS"; + + reference "ORAN-WG4.M.0-v12.00"; + } + + revision "2022-12-05" { + description + "version 11.0.0 + new features: + + 1) UPLANE-ONLY-DL-MODE + 2) SUPERVISION-WITH-SESSION-ID + 3) INTEGRITY-CHECK-AT-SW-DOWNLOAD + 4) ST4-SLOT-CONFIG-MSG-SUPPORT"; + + reference "ORAN-WG4.M.0-v11.00"; + } + + + revision "2022-08-15" { + description + "version 10.0.0 + new features: + 1) SHARED-ORU-MULTI-ODU + 2) SHARED-ORU-MULTI-OPERATOR"; + reference "ORAN-WG4.M.0-v10.00"; + } + + revision "2021-12-01" { + description + "version 8.0.0 + new features: + 1) Typographical corrections in descriptions + 2) Add new features: + BEAM-UPDATE-CONTENTION-CONTROL + MULTIPLE-SCS-IN-EAXC + ENHANCED-T-COMBINE + CHANNEL-INFORMATION-COMPRESSION + CHANNEL-INFORMATION-PRB-GROUP + MULTIPLE-TRANSPORT-SESSION-TYPE"; + reference "ORAN-WG4.M.0-v08.00"; + } + + revision "2021-07-26" { + description + "version 7.0.0 + + new features: + 1) EXT-ANT-DELAY-CONTRO + 2) CPLANE-MESSAGE-PROCESSING-LIMITS"; + + reference "ORAN-WG4.M.0-v07.00"; + } + + revision "2021-03-22" { + description + "version 6.0.0 + + 1) Features added to handle updated transmission window control: + STATIC-TRANSMISSION-WINDOW-CONTROL + DYNAMIC-TRANSMISSION-WINDOW-CONTROL + UNIFORMLY-DISTRIBUTED-TRANSMISSION + ORDERED-TRANSMISSION + INDEPENDENT-TRANSMISSION-WINDOW-CONTROL + "; + + reference "ORAN-WG4.M.0-v06.00"; + } + + revision "2020-12-10" { + description + "version 5.0.0 + + 1) initial version."; + + reference "ORAN-WG4.M.0-v05.00"; + } + + feature NON-PERSISTENT-MPLANE { + description + "Indicates that the Radio Unit supports the optional + capability to improve the operation with a SMO using a non- + persistent NETCONF session."; + } + + feature STATIC-TRANSMISSION-WINDOW-CONTROL { + description + "O-RU supports U-plane transmission window control (scheduled transmission + and optionally uniformly distributed transmission) configuration over M-plane"; + } + + feature DYNAMIC-TRANSMISSION-WINDOW-CONTROL { + description + "O-RU supports U-plane transmission window control (scheduled transmission + and optionally uniformly distributed transmission) configuration over C-plane"; + } + + feature UNIFORMLY-DISTRIBUTED-TRANSMISSION { + if-feature "STATIC-TRANSMISSION-WINDOW-CONTROL or DYNAMIC-TRANSMISSION-WINDOW-CONTROL"; + description + "O-RU supports transmission of UL U-plane messages distributed uniformly over transmission window."; + } + + feature ORDERED-TRANSMISSION { + description + "O-RU supports ordered transmission"; + } + + feature INDEPENDENT-TRANSMISSION-WINDOW-CONTROL { + if-feature "STATIC-TRANSMISSION-WINDOW-CONTROL or DYNAMIC-TRANSMISSION-WINDOW-CONTROL"; + description + "O-RU supports independent U-plane transmission window per endpoint. + + If this feature is not supported then for all endpoints with transmission control enabled + (see u-plane-transmission-control/transmission-window-control) must be configured for transmission + window offsets and window sizes that coincide for each symbol. + + If INDEPENDENT-TRANSMISSION-WINDOW-CONTROL feature is not supported then all endpoints with + transmission control enabled that handle the same carrier type and SCS must be configured + (via M-plane or C-plane) with parameter values resulting in transmission windows that coincide + between the endpoints. That is, for every symbol N, the effective transmission window for symbol N + must be shared (start at the same time and end at the same time) by all the endpoints handling + same carrier type and SCS. This restriction applies only to endpoints that have transmission + control enabled. + + If INDEPENDENT-TRANSMISSION-WINDOW-CONTROL feature is supported then restriction described + above does not apply and a different transmission window (window offset and window size) + can be used for every endpoint capable of transmission window control."; + } + + feature EXT-ANT-DELAY-CONTROL { + description + "This feature indicates that the O-RU supports external antenna delay control"; + } + + feature CPLANE-MESSAGE-PROCESSING-LIMITS { + description + "Feature to indicate O-RU limitation of C-Plane message processing. Refer CUS-Plane specification + section 'O-RU C-Plane message limits' for more details on this feature."; + } + + feature CHANNEL-INFORMATION-COMPRESSION { + description + "This feature indicates that the O-RU supports channel information compression."; + } + + feature CHANNEL-INFORMATION-PRB-GROUP { + description + "Feature to indicate O-RU supports receiving and processing channel + information (e.g., ST6) with PRB group size greater than one PRB"; + } + + feature BEAM-UPDATE-CONTENTION-CONTROL { + description + "Feature to indicate O-RU requirements for beam weight update for a given beamId, to avoid beam update contentions. + Refer CUS-Plane specification section 'Weight-based dynamic beamforming' for more details on this feature."; + } + + feature MULTIPLE-SCS-IN-EAXC { + description + "Presence of feature indicates that FHM supports combining for multiple SCS + or multiple c-plane-types/frameStructure in a single eAxC-id in UL direction."; + } + + feature MULTIPLE-TRANSPORT-SESSION-TYPE { + description + "Feature to indicate O-RU supports to be configured with multiple transport-session-type + (Ethernet, ALIASMAC, UDP/IP)"; + } + + feature ENHANCED-T-COMBINE { + description + "Presence of feature indicates that FHM/Cascade O-RU can support t-combine-net and tx-duration"; + } + + feature SHARED-ORU-MULTI-OPERATOR { + description + "Feature to indicate the O-RU supports shared operation with one or more shared + resource operators (i.e., multiple MNOs) and implements enhanced NACM privileges per shared + resource operator. + + Note, there is no linkage or dependency between the SHARED-CELL feature and the SHARED-ORU feature."; + } + + feature SHARED-ORU-MULTI-ODU { + description + "Feature to indicate the O-RU supports independent supervision qualified based on odu-id, where + loss of supervision triggers selective carrier deactivation of carriers associated with odu-id."; + } + + feature INTEGRITY-CHECK-AT-SW-DOWNLOAD { + description "Feature indicates that radio unit support performing integrity check at software download"; + } + + feature SUPERVISION-WITH-SESSION-ID { + description + "O-RUs supporting this feature reuse the session-id generated for each NETCONF + session in supervision-notification. The session-id is defined in RFC 6241. + For these O-RUs, the O-RU controller participating in the NETCONF supervision + procedure can subscribe to supervision-notification notification events, + filtering for the supervision-notification/session-id matching + session-id in the Hello Message received from NETCONF Server earlier."; + } + + feature UPLANE-ONLY-DL-MODE { + description + "Presence of feature indicates that O-RU supports U-Plane-only DL mode."; + } + + feature ST4-SLOT-CONFIG-MSG-SUPPORT { + description + "Feature to indicate O-RU support for Section Type 4 slot configuration message"; + } + feature NON-SCHEDULED-UEID { + description + "Feature to indicate that O-RU supports 'non-scheduled-ueid' to indicate the ports in the section which are not scheduled for a given eAxcid"; + } + + feature EXTENDED-PRACH-CONFIGURATION { + description + "Presence of the feature indicates that O-RU supports extended number of PRACH patterns and occasions + provided by means of static PRACH."; + } + + feature SE11-WITH-CONTINUITY-BIT-SUPPORT { + description + "Feature to indicate O-RU support for handling 'continuity' bit information in Section Extension 11"; + } + + feature UPLANE-MESSAGE-PROCESSING-LIMITS { + description + "Feature to indicate O-RU limitation of U-Plane message processing. Refer M-Plane specification + clause 15.10 for more details on this feature."; + } + + feature CALL-HOME-REACTIVATION-SUPPORTED { + description + "Presence of the feature indicates that O-RU supports re-activation of timed out call home flows."; + } + + feature SEQ-ID-CHECKING-CONFIGURABLE { + description + "Feature to indicate O-RU supports configuration of sequence number checking functionality."; + } + + feature TRX-CONTROL { + description + "Feature to indicate O-RU support for handling RF channel reconfiguration by TRX Control."; + } + + feature ADVANCED-SLEEP-MODE { + description + "Feature to indicate O-RU support for handling Advanced Sleep Modes."; + } + + feature SE23-PRB-BLOCK-MODE-SUPPORT { + description + "Presence of the feature indicates that O-RU supports PRB-BLOCK mode of SE-23 as defined in CUS-Plane + specification Clause 7.7.23.1."; + } + + feature MPLANE-DATA-LAYER-CONTROL { + description + "Feature to indicate O-RU support for M-Plane based data layer control energy saving feature."; + } + + feature SHARED-CELL-STATS { + description + "Feature to indicate FHM/Cascade O-RU support for shared-cell-stats."; + } + + feature PER-PORT-PTP-CONFIG { + description + "This feature indicates that the equipment supports per-port PTP configuration functionality"; + } + + feature PER-PORT-SYNCE-CONFIG { + description + "This feature indicates that the equipment supports per-port SYNCE configuration functionality"; + } + + feature PER-INT-TCP-MSS { + description + "This feature indicates that the RU supports a per-interfacece MSS + configuration for TCP."; + } + + feature MULTIPLE-BEAMID-TABLES-SUPPORTED { + description + "Feature to indicate O-RU support for multiple beamId tables."; + } + + feature MPLANE-TRX-CONTROL { + description + "Feature to indicate O-RU support for M-plane based TRX control for Network Energy Saving."; + } + + feature DEEP-HIBERNATE { + description + "Feature to indicate O-RU support for M-Plane based deep-hibernate energy saving feature."; + } + + feature PRG-SIZE-SUPP-SE-21-WITH-ST6 { + description + "Feature to indicate O-RU support for receiving prgSize information when Section Extension 21 is + sent with Section Type 6."; + } + + feature PRG-SIZE-SUPP-SE-21-WITH-ST5 { + description + "Feature to indicate O-RU support for receiving prgSize information when Section Extension 21 is + sent with Section Type 5."; + } + + feature USER-GROUP-OPTIMIZATION { + description + "Feature to indicate O-DU requirement to send all layers for a given user group i.e., UEs with same + time-frequency allocation using single C-Plane section description."; + } + + feature BF-DELAY-PROFILE { + description + "Feature to indicate O-RU support for beamforming list and delay profiles(s) per endpoint."; + } + + feature DMRS-BF-EQ { + description + "DMRS beamforming with equalization supported. See CUS-plane clause 12.6"; + } + + feature DMRS-BF-NEQ { + description + "DMRS beamforming without equalization supported. See CUS-plane clause 12.6"; + } + + feature RRM-MEAS-REPORTING { + description + "Reporting of RRM measurmenets supported. See CUS-plane clause 12.6.1.6"; + } + + feature USER-GROUP-SELF-ASSEMBLY { + description + "Feature to indicate user group self assembly. See CUS-Plane clause 7.7.24.7."; + } + + feature UEID-PERSISTENCE { + description + "Feature to indicate if ueId persistance over multiple slots is supported. See CUS-Plane clause 7.7.24.10."; + } + + feature CONTINUITY-BLOCK-SIZE { + description + "The O-RUs capability for reporting one or more values of continuity block sizes. See CUS-plane clause 12.6.1.2.1"; + } + + feature SELECTED-BF-METHOD-CONFIGURATION { + description + "Feature to indicate O-RU support for configuration of a subset of beamforming methods from an + endpoint-bf-profile-group that shall be used by a specific endpoint."; + } + + feature DMRS-BF-NEQ-UNALTERED-FREQ-OFFSET { + description + "Feature to indicate that DMRS-BF-NEQ beamforming performed by the O-RU does not change frequency offset, + hence it can be measured by the O-DU."; + } + + feature DMRS-BF-NEQ-UNALTERED-TAE { + description + "Feature to indicate that DMRS-BF-NEQ beamforming performed by the O-RU does not change TAE, + hence TAE can be measured by the O-DU. Refer CUS specification clause 9.2.2.1 for details."; + } + + feature SE10-MEMBER-CANDIDATE-LIST { + description + "Reporting of member-list of endpoints for SE-10 supporting endpoint. See CUS-plane clause 7.7.10.1"; + } + + feature MACSEC-BYPASS-POLICY { + description + "O-RU supports MACsec bypass policy"; + } + + feature SE27-ODU-CONTROLLED-DIMENSIONALITY-REDUCTION { + description + "Presence of the feature indicates that O-RU supports O-DU controlled dimensionality reduction using SE27 as defined in CUS-Plane + specification Clause 7.7.27."; + } + + feature SE20-MULTI-SD-PUNC-PATTERN-SUPPORT { + description + "Feature to indicate O-RU support for handling multiple section description puncturing pattern in + Section Extension 20 per endpoint."; + } + + feature CONFIGURABLE-VSWR-THRESHOLDS { + description + "Feature to indicate O-RU support for configurable VSWR thresholds."; + } + + feature CLIENT-AUTH-SSH-PUBLIC-KEY { + description + "O-RU supports an optional feature to authenticate SSH client via SSHPUBLICKEY method."; + } + + feature POINT-A-OFFSET-TO-ABSOLUTE-FREQUENCY-CENTER { + description + "Feature to indicate if leaf rx-array-carriers/point-a-offset-to-absolute-frequency-center is supported. See CUS-Plane clause 7.7.24.1."; + } +} From 96b944e5c96f06fc518f224660d6d2b51eec24b9 Mon Sep 17 00:00:00 2001 From: Teodora Date: Thu, 28 Aug 2025 13:24:36 +0200 Subject: [PATCH 119/275] Update the FHI M-plane doc for v16.01 support --- doc/ORAN_FHI7.2_Tutorial.md | 867 +++++++++++++++++++++++++++++++++++- 1 file changed, 862 insertions(+), 5 deletions(-) diff --git a/doc/ORAN_FHI7.2_Tutorial.md b/doc/ORAN_FHI7.2_Tutorial.md index 1d7169f29f..5f95921a64 100644 --- a/doc/ORAN_FHI7.2_Tutorial.md +++ b/doc/ORAN_FHI7.2_Tutorial.md @@ -1243,8 +1243,8 @@ In OAI gNB, we support: ``` The reference specifications: -* `O-RAN.WG4.MP.0-v05.00` -* `O-RAN.WG4.MP-YANGs-v04.00` +* `O-RAN.WG4.MP.0-R004-v16.01` +* `O-RAN.WG4.MP-YANGs-R004-v16.01` ## M-plane prerequisites Before proceeding, please make sure you have a support for 7.2 interface, as described in [Prerequisites](#prerequisites). @@ -1366,7 +1366,7 @@ sudo ldconfig If you would like to install these libraries in the custom path, please replace `/usr/local` default path to e.g. `/opt/mplane-v2`. ## Benetel O-RU -Note: Only v1.2.2 RAN550 and RAN650 have been successfully tested. +Note: RAN550/650 v1.2.2 and v1.4.1 have been successfully tested. ### One time steps Connect to the RU as user `root`, enable the mplane service, and reboot: @@ -1400,6 +1400,8 @@ The reference DU configuration file for two Benetel RAN650: [gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf) In order to run gNB/DU with M-plane, we need to modify Tx gain `att_tx` in RU section, as well as the `fhi_72` section in the configuration file. + +**Important note: Benetel v1.4.1 requires only one VF if the same VLAN tag is used for both C-/U-planes. Otherwise, C-plane will not be properly configured. Benetel v1.2.2 will receive the C-plane packets no matter the DU MAC address value.** Example for one RU: ```bash fhi_72 = { @@ -1573,14 +1575,14 @@ sequenceDiagram
-4x4 MIMO and 100MHz BW with Benetel 550 RU example run +4x4 MIMO and 100MHz BW with Benetel 550 RU v1.2.2 example run ``` [HW] [MPLANE] RPC request to RU "192.168.80.9" = with username "oranbenetel" and port ID "830". [HW] [MPLANE] Successfuly connected to RU "192.168.80.9" with username "oranbenetel" and port ID "830". [HW] [MPLANE] RPC request to RU "192.168.80.9" = operational datastore. [HW] [MPLANE] Successfully retrieved operational datastore from RU "192.168.80.9". -[HW] [MPLANE] RU is already PTP synchronized. +[HW] [MPLANE] RU is in "LOCKED" sync state. [HW] [MPLANE] RPC request to RU "192.168.80.9" = with stream "NETCONF" and filter "(null)". [HW] [MPLANE] RPC reply = OK. [HW] [MPLANE] Successfully subscribed to all notifications from RU "192.168.80.9". @@ -2223,6 +2225,861 @@ sequenceDiagram ```
+
+4x4 MIMO and 100MHz BW with Benetel 550 RU v1.4.1 example run + +``` +[HW] [MPLANE] RPC request to RU "192.168.81.4" = with username "oranbenetel" and port ID "830". +[HW] [MPLANE] Successfuly connected to RU "192.168.81.4" with username "oranbenetel" and port ID "830". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = operational datastore. +[HW] [MPLANE] Successfully retrieved operational datastore from RU "192.168.81.4". +[HW] [MPLANE] RU is already PTP synchronized. +[HW] [MPLANE] RPC request to RU "192.168.81.4" = with stream "NETCONF" and filter "(null)". +[HW] [MPLANE] RPC reply = OK. +[HW] [MPLANE] Successfully subscribed to all notifications from RU "192.168.81.4". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = " +65535 +65535 +". +[HW] [MPLANE] Successfully updated supervision timer to (65535+65535)[s] for RU "192.168.81.4". +[HW] [MPLANE] Watchdog timer answer: + 2025-08-29T06:49:32+02:00 + +[HW] [MPLANE] Interface MTU 1500 unreliable/not correctly reported by Benetel O-RU, hardcoding to 9600. +[HW] [MPLANE] IQ bitwidth 16 unreliable/not correctly reported by Benetel O-RU, hardcoding to 9. +[HW] [MPLANE] Storing the following information to forward to xran: + RU MAC address 70:b3:d5:e1:5b:81 + MTU 9600 + IQ bitwidth 9 + PRACH offset 4 + DU port bitmask 61440 + Band sector bitmask 3840 + CC ID bitmask 240 + RU port ID bitmask 15 + DU port ID 0 + Band sector ID 0 + CC ID 0 + RU port ID 0 + max Tx gain 24.0 +[HW] [MPLANE] Successfully retrieved all the U-plane info - interface name, TX/RX carrier names, and TX/RX endpoint names. +[HW] [MPLANE] Successfully retreived all performance measurement names. +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-yang-metadata". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "yang". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-inet-types". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-yang-types". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-yang-schema-mount". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-yang-structure-ext". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-datastores". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "sysrepo". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-netconf-acm". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-factory-default". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "sysrepo-factory-default". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-yang-library". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "sysrepo-monitoring". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "sysrepo-plugind". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-netconf". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-netconf-with-defaults". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-netconf-notifications". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-origin". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-netconf-monitoring". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "ietf-netconf-nmda". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = for module "nc-notifications". +[HW] [MPLANE] [LIBYANG] ERROR: Data model "notifications" not found in local searchdirs. (path: (null)). +[HW] [MPLANE] [LIBYANG] ERROR: Loading "notifications" module failed. (path: (null)). +[HW] [MPLANE] [LIBYANG] ERROR: Parsing module "nc-notifications" failed. (path: (null)). +[HW] [MPLANE] Unable to load module "nc-notifications" from RU "192.168.81.4". +[HW] [MPLANE] Unable to load all yang modules from operational datastore for RU "192.168.81.4". Using yang models present in "models" subfolder. +[HW] [MPLANE] Successfully loaded all yang modules for RU "192.168.81.4". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = : + + + INTERFACE_0 + ianaift:l2vlan + true + 70:b3:d5:e1:5b:81 + eth0 + 3 + + + + 10 + 10 + 10 + + RX_ON_TIME + false + RU + COUNT + + + RX_EARLY + false + RU + COUNT + + + RX_LATE + false + RU + COUNT + + + RX_TOTAL + false + RU + COUNT + + + RX_ON_TIME_C + false + RU + COUNT + + + RX_EARLY_C + false + RU + COUNT + + + RX_LATE_C + false + RU + COUNT + + + TX_TOTAL + false + RU + COUNT + + + + ETH-INTERFACE + + PLANE_0 + + INTERFACE_0 + + 70:b3:d5:e1:5b:81 + 3 + 00:11:22:33:44:66 + + + + + + + PdschLink0 + PLANE_0 + TxArray0 + LowLevelTxEndpoint0 + + + PdschLink1 + PLANE_0 + TxArray1 + LowLevelTxEndpoint1 + + + PdschLink2 + PLANE_0 + TxArray2 + LowLevelTxEndpoint2 + + + PdschLink3 + PLANE_0 + TxArray3 + LowLevelTxEndpoint3 + + + PuschLink0 + PLANE_0 + RxArray0 + LowLevelRxEndpoint0 + + + PrachLink0 + PLANE_0 + RxArray0 + LowLevelRxPrachEndpoint0 + + + PuschLink1 + PLANE_0 + RxArray1 + LowLevelRxEndpoint1 + + + PrachLink1 + PLANE_0 + RxArray1 + LowLevelRxPrachEndpoint1 + + + PuschLink2 + PLANE_0 + RxArray2 + LowLevelRxEndpoint2 + + + PrachLink2 + PLANE_0 + RxArray2 + LowLevelRxPrachEndpoint2 + + + PuschLink3 + PLANE_0 + RxArray3 + LowLevelRxEndpoint3 + + + PrachLink3 + PLANE_0 + RxArray3 + LowLevelRxPrachEndpoint3 + + + LowLevelTxEndpoint0 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + 61440 + 3840 + 240 + 15 + 0 + + + + LowLevelTxEndpoint1 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + 61440 + 3840 + 240 + 15 + 1 + + + + LowLevelTxEndpoint2 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + 61440 + 3840 + 240 + 15 + 2 + + + + LowLevelTxEndpoint3 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + 61440 + 3840 + 240 + 15 + 3 + + + + LowLevelRxEndpoint0 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + KHZ_30 + 0 + + + 61440 + 3840 + 240 + 15 + 0 + + true + + + LowLevelRxPrachEndpoint0 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + KHZ_30 + 0 + + + 61440 + 3840 + 240 + 15 + 4 + + true + + + LowLevelRxEndpoint1 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + KHZ_30 + 0 + + + 61440 + 3840 + 240 + 15 + 1 + + true + + + LowLevelRxPrachEndpoint1 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + KHZ_30 + 0 + + + 61440 + 3840 + 240 + 15 + 5 + + true + + + LowLevelRxEndpoint2 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + KHZ_30 + 0 + + + 61440 + 3840 + 240 + 15 + 2 + + true + + + LowLevelRxPrachEndpoint2 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + KHZ_30 + 0 + + + 61440 + 3840 + 240 + 15 + 6 + + true + + + LowLevelRxEndpoint3 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + KHZ_30 + 0 + + + 61440 + 3840 + 240 + 15 + 3 + + true + + + LowLevelRxPrachEndpoint3 + + 9 + STATIC + + 193 + NORMAL + 352 + 288 + 0 + + KHZ_30 + 0 + + + 61440 + 3840 + 240 + 15 + 7 + + true + + + TxArray0 + 623334 + 3350010000 + 100000000 + ACTIVE + 24.0 + 0 + 0 + + + TxArray1 + 623334 + 3350010000 + 100000000 + ACTIVE + 24.0 + 0 + 0 + + + TxArray2 + 623334 + 3350010000 + 100000000 + ACTIVE + 24.0 + 0 + 0 + + + TxArray3 + 623334 + 3350010000 + 100000000 + ACTIVE + 24.0 + 0 + 0 + + + RxArray0 + 623334 + 3350010000 + 100000000 + ACTIVE + 0 + 0 + 0.0 + 0 + + + RxArray1 + 623334 + 3350010000 + 100000000 + ACTIVE + 0 + 0 + 0.0 + 0 + + + RxArray2 + 623334 + 3350010000 + 100000000 + ACTIVE + 0 + 0 + 0.0 + 0 + + + RxArray3 + 623334 + 3350010000 + 100000000 + ACTIVE + 0 + 0 + 0.0 + 0 + + + +[HW] [MPLANE] RPC reply = OK. +[HW] [MPLANE] Successfully edited the candidate datastore for RU "192.168.81.4". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = candidate datastore. +[HW] [MPLANE] RPC reply = OK. +[HW] [MPLANE] Successfully validated candidate datastore for RU "192.168.81.4". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = candidate datastore. +[HW] [MPLANE] RPC reply = OK. +[HW] [MPLANE] Successfully commited configuration into running datastore for RU "192.168.81.4". +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:19.335228846+00:00) +{ + "o-ran-uplane-conf:rx-array-carriers-state-change": { + "rx-array-carriers": [ + { + "name": "RxArray0", + "state": "BUSY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:19.368360865+00:00) +{ + "o-ran-uplane-conf:rx-array-carriers-state-change": { + "rx-array-carriers": [ + { + "name": "RxArray1", + "state": "BUSY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:19.414703766+00:00) +{ + "o-ran-uplane-conf:rx-array-carriers-state-change": { + "rx-array-carriers": [ + { + "name": "RxArray2", + "state": "BUSY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:19.452921618+00:00) +{ + "o-ran-uplane-conf:rx-array-carriers-state-change": { + "rx-array-carriers": [ + { + "name": "RxArray3", + "state": "BUSY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:19.471419021+00:00) +{ + "ietf-netconf-notifications:netconf-config-change": { + "changed-by": { + "username": "root", + "session-id": 0 + }, + "datastore": "running", + "edit": [ + { + "target": "/ietf-interfaces:interfaces/interface[name='INTERFACE_0']", + "operation": "create" + }, + { + "target": "/ietf-interfaces:interfaces/interface[name='INTERFACE_0']/name", + "operation": "create" + }, +... +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:19.678251110+00:00) +{ + "o-ran-uplane-conf:tx-array-carriers-state-change": { + "tx-array-carriers": [ + { + "name": "TxArray0", + "state": "BUSY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:19.715498360+00:00) +{ + "o-ran-uplane-conf:tx-array-carriers-state-change": { + "tx-array-carriers": [ + { + "name": "TxArray1", + "state": "BUSY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:19.830268973+00:00) +{ + "o-ran-uplane-conf:tx-array-carriers-state-change": { + "tx-array-carriers": [ + { + "name": "TxArray2", + "state": "BUSY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:19.853387657+00:00) +{ + "o-ran-uplane-conf:tx-array-carriers-state-change": { + "tx-array-carriers": [ + { + "name": "TxArray3", + "state": "BUSY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:24.515262267+00:00) +{ + "o-ran-uplane-conf:rx-array-carriers-state-change": { + "rx-array-carriers": [ + { + "name": "RxArray0", + "state": "READY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:24.535692962+00:00) +{ + "o-ran-uplane-conf:rx-array-carriers-state-change": { + "rx-array-carriers": [ + { + "name": "RxArray1", + "state": "READY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:24.557423764+00:00) +{ + "o-ran-uplane-conf:rx-array-carriers-state-change": { + "rx-array-carriers": [ + { + "name": "RxArray2", + "state": "READY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:24.579066867+00:00) +{ + "o-ran-uplane-conf:rx-array-carriers-state-change": { + "rx-array-carriers": [ + { + "name": "RxArray3", + "state": "READY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:24.604192995+00:00) +{ + "o-ran-uplane-conf:tx-array-carriers-state-change": { + "tx-array-carriers": [ + { + "name": "TxArray0", + "state": "READY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:24.629830498+00:00) +{ + "o-ran-uplane-conf:tx-array-carriers-state-change": { + "tx-array-carriers": [ + { + "name": "TxArray1", + "state": "READY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:24.651062685+00:00) +{ + "o-ran-uplane-conf:tx-array-carriers-state-change": { + "tx-array-carriers": [ + { + "name": "TxArray2", + "state": "READY" + } + ] + } +} + +[HW] [MPLANE] Received notification from RU "192.168.81.4" at (2025-08-28T10:37:24.672865767+00:00) +{ + "o-ran-uplane-conf:tx-array-carriers-state-change": { + "tx-array-carriers": [ + { + "name": "TxArray3", + "state": "READY" + } + ] + } +} + +[HW] [MPLANE] RU "192.168.81.4" is now ready. +[HW] [MPLANE] RPC request to RU "192.168.81.4" = : + + 10 + 10 + 10 + + RX_ON_TIME + true + RU + COUNT + + + RX_EARLY + true + RU + COUNT + + + RX_LATE + true + RU + COUNT + + + RX_TOTAL + true + RU + COUNT + + + RX_ON_TIME_C + true + RU + COUNT + + + RX_EARLY_C + true + RU + COUNT + + + RX_LATE_C + true + RU + COUNT + + + TX_TOTAL + true + RU + COUNT + + + +[HW] [MPLANE] RPC reply = OK. +[HW] [MPLANE] Successfully edited the candidate datastore for RU "192.168.81.4". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = candidate datastore. +[HW] [MPLANE] RPC reply = OK. +[HW] [MPLANE] Successfully validated candidate datastore for RU "192.168.81.4". +[HW] [MPLANE] RPC request to RU "192.168.81.4" = candidate datastore. +[HW] [MPLANE] RPC reply = OK. +[HW] [MPLANE] Successfully commited configuration into running datastore for RU "192.168.81.4". +[HW] [MPLANE] Sucessfully activated PM after start-up procedure for RU "192.168.81.4". +``` +
Note: If you wish to run the fronthaul without M-plane, no need for recompilation, as the library `oran_fhlib_5g` already exists. The only mandatory step is to link `oran_fhlib_5g` to `oai_transpro` library. From 163c8de371099247a62597d769665face012cac2 Mon Sep 17 00:00:00 2001 From: Teodora Date: Tue, 28 Oct 2025 16:20:52 +0100 Subject: [PATCH 120/275] Do not use ru_session_list on the stack This struct is used in multiple source files. Previously, when stopping the nr-softmodem, the ru_session_list "disappears" and the M-plane wasn't terminated properly (num_rus = 0 even though DU connected to at least one RU). --- radio/fhi_72/oran_isolate.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/radio/fhi_72/oran_isolate.c b/radio/fhi_72/oran_isolate.c index 8a2ac0ae46..7860b61354 100644 --- a/radio/fhi_72/oran_isolate.c +++ b/radio/fhi_72/oran_isolate.c @@ -85,6 +85,7 @@ int trx_oran_stop(openair0_device *device) #ifdef OAI_MPLANE printf("[MPLANE] Stopping M-plane.\n"); disconnect_mplane(s->mplane_priv); + free(s->mplane_priv); #endif return (0); } @@ -308,40 +309,41 @@ __attribute__((__visibility__("default"))) int transport_init(openair0_device *d bool success = false; #ifdef OAI_MPLANE - ru_session_list_t ru_session_list = {0}; - success = init_mplane(&ru_session_list); + ru_session_list_t *ru_session_list = calloc(1, sizeof(*ru_session_list)); + assert(ru_session_list != NULL && "Memory exhausted"); + success = init_mplane(ru_session_list); AssertFatal(success, "[MPLANE] Cannot initialize M-plane.\n"); - bool ru_configured[ru_session_list.num_rus]; - for (size_t i = 0; i < ru_session_list.num_rus; i++) { - ru_session_t *ru_session = &ru_session_list.ru_session[i]; + bool ru_configured[ru_session_list->num_rus]; + for (size_t i = 0; i < ru_session_list->num_rus; i++) { + ru_session_t *ru_session = &ru_session_list->ru_session[i]; ru_configured[i] = connect_mplane(ru_session); if (!ru_configured[i]) { continue; } - ru_configured[i] = manage_ru(ru_session, openair0_cfg, ru_session_list.num_rus); + ru_configured[i] = manage_ru(ru_session, openair0_cfg, ru_session_list->num_rus); } bool all_ok = true; - bool ru_ready[ru_session_list.num_rus]; - for (size_t i = 0; i < ru_session_list.num_rus; i++) { + bool ru_ready[ru_session_list->num_rus]; + for (size_t i = 0; i < ru_session_list->num_rus; i++) { if (!ru_configured[i]) { - MP_LOG_I("RU with IP %s couldn't be configured.\n", ru_session_list.ru_session[i].ru_ip_add); + MP_LOG_I("RU with IP %s couldn't be configured.\n", ru_session_list->ru_session[i].ru_ip_add); all_ok = false; } ru_ready[i] = false; } if (!all_ok) { - disconnect_mplane((void *)&ru_session_list); + disconnect_mplane(ru_session_list); AssertFatal(false, "[MPLANE] Stopping M-plane.\n"); } while (true) { sleep(1); bool all_rus_ready = true; - for (int i = 0; i < ru_session_list.num_rus; i++) { - ru_session_t *ru_session = &ru_session_list.ru_session[i]; + for (int i = 0; i < ru_session_list->num_rus; i++) { + ru_session_t *ru_session = &ru_session_list->ru_session[i]; if (!ru_ready[i] && ru_session->ru_notif.config_change && !ru_session->ru_notif.rx_carrier_state && !ru_session->ru_notif.tx_carrier_state) { MP_LOG_I("RU \"%s\" is now ready.\n", ru_session->ru_ip_add); ru_ready[i] = true; @@ -360,9 +362,9 @@ __attribute__((__visibility__("default"))) int transport_init(openair0_device *d } } - eth->mplane_priv = (void *)&ru_session_list; + eth->mplane_priv = ru_session_list; - success = get_xran_config((void *)&ru_session_list, openair0_cfg, &fh_init, fh_config); + success = get_xran_config(ru_session_list, openair0_cfg, &fh_init, fh_config); AssertFatal(success, "[MPLANE] Cannot configure xran with M-plane info.\n"); #else success = get_xran_config(NULL, openair0_cfg, &fh_init, fh_config); From 19bb4630497727cbe16d4b9d47c97c62d861ba0f Mon Sep 17 00:00:00 2001 From: Teodora Date: Thu, 30 Oct 2025 17:08:40 +0100 Subject: [PATCH 121/275] Properly free the memory 1. Create a free_ru_session_list() function to free the memory allocated for the connected RUs before closing the nr-softmodem. 2. xmlReadMemory() and xmlNodeGetContent() were causing serious memory leakages, and now all is properly freed using xmlFreeDoc() and xmlFree(). --- radio/fhi_72/mplane/connect-mplane.c | 19 +--- radio/fhi_72/mplane/ru-mplane-api.c | 121 +++++++++++++++++++++++-- radio/fhi_72/mplane/ru-mplane-api.h | 2 + radio/fhi_72/mplane/subscribe-mplane.c | 5 + radio/fhi_72/mplane/xml/get-xml.c | 21 +++-- radio/fhi_72/mplane/yang/get-yang.c | 33 +++++-- 6 files changed, 163 insertions(+), 38 deletions(-) diff --git a/radio/fhi_72/mplane/connect-mplane.c b/radio/fhi_72/mplane/connect-mplane.c index 9744ce01e8..9d00d26bdf 100644 --- a/radio/fhi_72/mplane/connect-mplane.c +++ b/radio/fhi_72/mplane/connect-mplane.c @@ -59,24 +59,7 @@ bool connect_mplane(ru_session_t *ru_session) void disconnect_mplane(void *rus_disconnect) { ru_session_list_t *ru_session_list = (ru_session_list_t *)rus_disconnect; - - for (size_t i = 0; i num_rus; i++) { - ru_session_t *ru_session = &ru_session_list->ru_session[i]; - if (ru_session->session == NULL) - continue; - MP_LOG_I("Sending PM de-activation request for RU \"%s\".\n", ru_session->ru_ip_add); - bool success = pm_conf(ru_session, "false"); - if (success) - MP_LOG_I("Successfully de-activated PM for RU \"%s\".\n", ru_session->ru_ip_add); - MP_LOG_I("Disconnecting from RU \"%s\".\n", ru_session->ru_ip_add); - nc_session_free(ru_session->session, NULL); - ru_session->session = NULL; -#ifdef MPLANE_V1 - ly_ctx_destroy((struct ly_ctx *)ru_session->ctx, NULL); -#elif defined MPLANE_V2 - ly_ctx_destroy((struct ly_ctx *)ru_session->ctx); -#endif - } + free_ru_session_list(ru_session_list); nc_client_destroy(); } diff --git a/radio/fhi_72/mplane/ru-mplane-api.c b/radio/fhi_72/mplane/ru-mplane-api.c index 649cf34a20..f04e294dfc 100644 --- a/radio/fhi_72/mplane/ru-mplane-api.c +++ b/radio/fhi_72/mplane/ru-mplane-api.c @@ -20,11 +20,110 @@ */ #include "ru-mplane-api.h" +#include "init-mplane.h" #include "xml/get-xml.h" #include "common/utils/assertions.h" #include +#include +#include + +static void free_pm_stats(pm_stats_t *src) +{ + // free Rx window measurements + for (size_t i = 0; i < src->rx_num; i++) { + free(src->rx_window_meas[i]); + } + free(src->rx_window_meas); + + // free Tx measurements + for (size_t i = 0; i < src->tx_num; i++) { + free(src->tx_meas[i]); + } + free(src->tx_meas); +} + +static void free_uplane_info(uplane_info_t *src) +{ + for (size_t i = 0; i < src->num; i++) { + free(src->name[i]); + } + free(src->name); +} + +static void free_ru_mplane_config(ru_mplane_config_t *src) +{ + // free DU MAC address(es) and VLAN tag(s) + for (size_t i = 0; i < src->num_cu_planes; i++) { + free(src->du_mac_addr[i]); + } + free(src->du_mac_addr); + free(src->vlan_tag); + + // free interface name + free(src->interface_name); + + // free endpoints and carriers + free_uplane_info(&src->tx_endpoints); + free_uplane_info(&src->rx_endpoints); + free_uplane_info(&src->tx_carriers); + free_uplane_info(&src->rx_carriers); +} + +static void free_ru_session(ru_session_t *src) +{ + // username + free(src->username); + + // if no RU connected to, free only the initialized RU IP address + if (src->session == NULL) { + free(src->ru_ip_add); + return; + } + + // disconnect from the RU + MP_LOG_I("Sending PM de-activation request for RU \"%s\".\n", src->ru_ip_add); + bool success = pm_conf(src, "false"); + if (success) + MP_LOG_I("Successfully de-activated PM for RU \"%s\".\n", src->ru_ip_add); + MP_LOG_I("Disconnecting from RU \"%s\".\n", src->ru_ip_add); + + // free RU IP address + free(src->ru_ip_add); + // free RU M-plane config + free_ru_mplane_config(&src->ru_mplane_config); + // free NETCONF session + nc_session_free(src->session, NULL); + src->session = NULL; + // free libyang context +#ifdef MPLANE_V1 + ly_ctx_destroy((struct ly_ctx *)src->ctx, NULL); +#elif defined MPLANE_V2 + ly_ctx_destroy((struct ly_ctx *)src->ctx); +#endif + // free only the RU MAC addressin xran M-plane info + free(src->xran_mplane.ru_mac_addr); + // nothing to free in ru_notif + // free PM stats + free_pm_stats(&src->pm_stats); +} + +void free_ru_session_list(ru_session_list_t *src) +{ + // DU key pair + for (size_t i = 0; i < 2; i++) { + free(src->du_key_pair[i]); + } + free(src->du_key_pair); + + // RU session + for (size_t i = 0; i < src->num_rus; i++) { + free_ru_session(&src->ru_session[i]); + } + free(src->ru_session); +} + oper_state_e str_to_enum_oper(const char* value) { #define X(name, str) if (value != NULL && strcmp(value, str) == 0) return name; OPER_STATE @@ -98,13 +197,15 @@ static void fix_benetel_setting(xran_mplane_t *xran_mplane, const uint32_t inter bool get_config_for_xran(const char *buffer, const int max_num_ant, xran_mplane_t *xran_mplane) { /* some O-RU vendors are not fully compliant as per M-plane specifications */ - const char *ru_vendor = get_ru_xml_node(buffer, "mfg-name"); + char *ru_vendor = get_ru_xml_node(buffer, "mfg-name"); // RU MAC xran_mplane->ru_mac_addr = get_ru_xml_node(buffer, "mac-address"); // TODO: support for VVDN, as it defines multiple MAC addresses // MTU - const uint32_t interface_mtu = (uint32_t)atoi(get_ru_xml_node(buffer, "l2-mtu")); + char *int_mtu_str = get_ru_xml_node(buffer, "l2-mtu"); + const uint32_t interface_mtu = (uint32_t)atoi(int_mtu_str); + free(int_mtu_str); // IQ bitwidth char **match_list = NULL; @@ -142,14 +243,17 @@ bool get_config_for_xran(const char *buffer, const int max_num_ant, xran_mplane_ free_match_list(match_list, count); // Managed delay support - const char *managed_delay = get_ru_xml_node(buffer, "managed-delay-support"); + char *managed_delay = get_ru_xml_node(buffer, "managed-delay-support"); xran_mplane->managed_delay = (strcasecmp(managed_delay, "NON_MANAGED") == 0) ? false : true; + free(managed_delay); // Store the max gain - xran_mplane->max_tx_gain = (double)atof(get_ru_xml_node(buffer, "max-gain")); + char *max_tx_gain_str = get_ru_xml_node(buffer, "max-gain"); + xran_mplane->max_tx_gain = (double)atof(max_tx_gain_str); + free(max_tx_gain_str); // Model name - const char *model_name = get_ru_xml_node(buffer, "model-name"); + char *model_name = get_ru_xml_node(buffer, "model-name"); if (strcasecmp(ru_vendor, "BENETEL") == 0 /* || strcmp(ru_vendor, "VVDN-LPRU") == 0 || strcmp(ru_vendor, "Metanoia") == 0 */) { fix_benetel_setting(xran_mplane, interface_mtu, first_iq_width, max_num_ant, model_name); @@ -185,6 +289,9 @@ bool get_config_for_xran(const char *buffer, const int max_num_ant, xran_mplane_ xran_mplane->ru_port, xran_mplane->max_tx_gain); + free(ru_vendor); + free(model_name); + return true; } @@ -220,7 +327,7 @@ bool get_uplane_info(const char *buffer, ru_mplane_config_t *ru_mplane_config) bool get_pm_object_list(const char *buffer, pm_stats_t *pm_stats) { - const char *ru_vendor = get_ru_xml_node(buffer, "mfg-name"); + char *ru_vendor = get_ru_xml_node(buffer, "mfg-name"); if (strcasecmp(ru_vendor, "BENETEL") == 0) { pm_stats->start_up_timing = false; } else { @@ -235,5 +342,7 @@ bool get_pm_object_list(const char *buffer, pm_stats_t *pm_stats) MP_LOG_I("Successfully retreived all performance measurement names.\n"); + free(ru_vendor); + return true; } diff --git a/radio/fhi_72/mplane/ru-mplane-api.h b/radio/fhi_72/mplane/ru-mplane-api.h index f221f7117c..f568bbaf3e 100644 --- a/radio/fhi_72/mplane/ru-mplane-api.h +++ b/radio/fhi_72/mplane/ru-mplane-api.h @@ -188,6 +188,8 @@ typedef struct { } ru_session_list_t; +void free_ru_session_list(ru_session_list_t *src); + bool get_config_for_xran(const char *buffer, const int max_num_ant, xran_mplane_t *xran_mplane); bool get_uplane_info(const char *buffer, ru_mplane_config_t *ru_mplane_config); diff --git a/radio/fhi_72/mplane/subscribe-mplane.c b/radio/fhi_72/mplane/subscribe-mplane.c index ffb77265e5..38c770848d 100644 --- a/radio/fhi_72/mplane/subscribe-mplane.c +++ b/radio/fhi_72/mplane/subscribe-mplane.c @@ -47,6 +47,8 @@ static void notif_clb_v1(struct nc_session *session, const struct nc_notif *noti MP_LOG_I("\nReceived notification at (%s)\n%s\n", notif->datetime, subs_reply); recv_notif_v1(notif, answer); + + free(subs_reply); } #elif MPLANE_V2 static void log_v2_pm_info(const char *ru_ip_add, struct lyd_node_inner *stats) @@ -124,11 +126,14 @@ static void notif_clb_v2(struct nc_session *session, const struct lyd_node *envp struct lyd_node_inner *op_inner = (struct lyd_node_inner *)op; if (strcmp(op_inner->schema->name, "measurement-result-stats") == 0) { log_v2_pm_info(ru_ip_add, op_inner); + free(subs_reply); return; } MP_LOG_I("Received notification from RU \"%s\" at (%s)\n%s\n", ru_ip_add, ((struct lyd_node_opaq *)lyd_child(envp))->value, subs_reply); recv_notif_v2(op_inner, answer); + + free(subs_reply); } #endif diff --git a/radio/fhi_72/mplane/xml/get-xml.c b/radio/fhi_72/mplane/xml/get-xml.c index 8ebd783901..138896103f 100644 --- a/radio/fhi_72/mplane/xml/get-xml.c +++ b/radio/fhi_72/mplane/xml/get-xml.c @@ -25,7 +25,7 @@ #include #include -static char *find_ru_xml_node(xmlNode *node, const char *filter) +static xmlChar *find_ru_xml_node(xmlNode *node, const char *filter) { for (xmlNode *cur_node = node; cur_node; cur_node = cur_node->next) { if (cur_node->type != XML_ELEMENT_NODE) @@ -40,9 +40,9 @@ static char *find_ru_xml_node(xmlNode *node, const char *filter) break; } } - return (char *)xmlNodeGetContent(target_node); + return xmlNodeGetContent(target_node); } - char *answer = find_ru_xml_node(cur_node->children, filter); + xmlChar *answer = find_ru_xml_node(cur_node->children, filter); if (answer != NULL) { return answer; } @@ -57,7 +57,13 @@ char *get_ru_xml_node(const char *buffer, const char *filter) xmlDoc *doc = xmlReadMemory(buffer, len, NULL, NULL, 0); xmlNode *root_element = xmlDocGetRootElement(doc); - return find_ru_xml_node(root_element->children, filter); + xmlChar *content = find_ru_xml_node(root_element->children, filter); + + char *value = strdup((char *)content); + xmlFree(content); + xmlFreeDoc(doc); + + return value; } static void find_ru_xml_list(xmlNode *node, const char *filter, char ***match_list, size_t *count) @@ -75,11 +81,12 @@ static void find_ru_xml_list(xmlNode *node, const char *filter, char ***match_li break; } } - const char *content = (const char *)xmlNodeGetContent(name_node ? name_node : cur_node); + xmlChar *content = xmlNodeGetContent(name_node ? name_node : cur_node); if (content) { *match_list = realloc(*match_list, (*count + 1) * sizeof(char *)); - (*match_list)[*count] = strdup(content); + (*match_list)[*count] = strdup((char *)content); (*count)++; + xmlFree(content); } } find_ru_xml_list(cur_node->children, filter, match_list, count); @@ -94,4 +101,6 @@ void get_ru_xml_list(const char *buffer, const char *filter, char ***match_list, xmlNode *root_element = xmlDocGetRootElement(doc); find_ru_xml_list(root_element->children, filter, match_list, count); + + xmlFreeDoc(doc); } diff --git a/radio/fhi_72/mplane/yang/get-yang.c b/radio/fhi_72/mplane/yang/get-yang.c index 08abeb4896..2eb39aa1d6 100644 --- a/radio/fhi_72/mplane/yang/get-yang.c +++ b/radio/fhi_72/mplane/yang/get-yang.c @@ -42,20 +42,30 @@ static bool store_schemas(xmlNode *node, ru_session_t *ru_session, struct ly_ctx if (strcmp((const char *)cur_node->name, "schema") == 0) { xmlNode *name_node = xmlFirstElementChild(cur_node); - char *module_name = (char *)xmlNodeGetContent(name_node); + xmlChar *module_name = xmlNodeGetContent(name_node); xmlNode *revision_node = xmlNextElementSibling(name_node); - char *module_revision = (char *)xmlNodeGetContent(revision_node); + xmlChar *module_revision = xmlNodeGetContent(revision_node); xmlNode *format_node = xmlNextElementSibling(revision_node); - char *module_format = (char *)xmlNodeGetContent(format_node); + xmlChar *module_format = xmlNodeGetContent(format_node); - if (strcmp(module_format, "yang") != 0) + if (strcmp((char*)module_format, "yang") != 0) { + xmlFree(module_name); + xmlFree(module_revision); + xmlFree(module_format); continue; + } MP_LOG_I("RPC request to RU \"%s\" = for module \"%s\".\n", ru_session->ru_ip_add, module_name); - struct nc_rpc *get_schema_rpc = nc_rpc_getschema(module_name, module_revision, "yang", param); + struct nc_rpc *get_schema_rpc = nc_rpc_getschema((char*)module_name, (char*)module_revision, "yang", param); char *schema_data = NULL; bool success = rpc_send_recv((struct nc_session *)ru_session->session, get_schema_rpc, wd, timeout, &schema_data); - AssertError(success, return false, "[MPLANE] Unable to get schema for module \"%s\" from RU \"%s\".\n", module_name, ru_session->ru_ip_add); + if (!success) { + MP_LOG_W("[MPLANE] Unable to get schema for module \"%s\" from RU \"%s\".\n", module_name, ru_session->ru_ip_add); + xmlFree(module_name); + xmlFree(module_revision); + xmlFree(module_format); + return false; + } if (schema_data) { #ifdef MPLANE_V1 @@ -73,10 +83,16 @@ static bool store_schemas(xmlNode *node, ru_session_t *ru_session, struct ly_ctx #elif defined MPLANE_V2 ly_ctx_destroy(*ctx); #endif - return false; - } + xmlFree(module_name); + xmlFree(module_revision); + xmlFree(module_format); + return false; + } } nc_rpc_free(get_schema_rpc); + xmlFree(module_name); + xmlFree(module_revision); + xmlFree(module_format); } } @@ -109,6 +125,7 @@ bool load_yang_models(ru_session_t *ru_session, const char *buffer) xmlNode *root_element = xmlDocGetRootElement(doc); bool success = load_from_operational_ds(root_element->children, ru_session, ctx); + xmlFreeDoc(doc); if (success) { MP_LOG_I("Successfully loaded all yang modules from operational datastore for RU \"%s\".\n", ru_session->ru_ip_add); return true; From 09e1e6c6fdb3bc4fa4b0d4cfdeb175b874e093cc Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 17 Oct 2025 11:53:20 +0200 Subject: [PATCH 122/275] Rewrite NR_MAC UE_iterator() to limit variable scope Especially in pf_dl()/pf_ul(), we use a first loop using UE_iterator(), then iterate over a subset of UEs. It has happened a couple of times that I was using variable "UE" (from the iterator), when it should only be used in the first UE_iterator() loop. Rewrite UE_iterator() to effectively limit the scope of the iterator variables to the body of the UE_iterator. This should also make it possible to reuse multiple UE_iterator()s with the same variables in sequence. --- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 5bd786df90..f64b94fb47 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -819,7 +819,7 @@ typedef struct { nr_beam_mode_t beam_mode; } NR_beam_info_t; -#define UE_iterator(BaSe, VaR) NR_UE_info_t ** VaR##pptr=BaSe, *VaR; while ((VaR=*(VaR##pptr++))) +#define UE_iterator(BaSe, VaR) for (NR_UE_info_t **VaR##pptr=BaSe, *VaR=*VaR##pptr; VaR; VaR=*(++VaR##pptr)) typedef struct { /// current frame for DCI From 2f636037eec62b8217e6d2b2a60df48bfbacc0b6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 15 Oct 2025 15:42:00 +0200 Subject: [PATCH 123/275] Fixup: compute_PDU_length() --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index 8a81d8ae4f..540e2c6291 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -747,10 +747,10 @@ static void other_sib_sched_control(module_id_t module_idP, // Data to be transmitted memcpy(tx_req->TLVs[0].value.direct, sib_bcch_pdu, sched_pdsch_otherSI.tb_size); - tx_req->PDU_length = sched_pdsch_otherSI.tb_size; tx_req->PDU_index = pdu_index; tx_req->num_TLV = 1; - tx_req->TLVs[0].length = sched_pdsch_otherSI.tb_size + 2; + tx_req->TLVs[0].length = sched_pdsch_otherSI.tb_size; + tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, tx_req->TLVs[0].length); TX_req->Number_of_PDUs++; TX_req->SFN = frame; TX_req->Slot = slot; From ce744fd24f8eb35f5a1fb1e3d51dc29f91fb0463 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 21 Aug 2025 19:23:29 +0200 Subject: [PATCH 124/275] Refactor DLSCH post processor into post_process_dlsch() Introduce a struct with information on FAPI structures to save resource and data allocation into. It's in nr_mac_gNB.h because the next commits will reuse this for all preprocessors. --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 731 +++++++++--------- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 11 + 2 files changed, 383 insertions(+), 359 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 44c9072a15..7fe346ab7d 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -641,6 +641,11 @@ static void pf_dl(module_id_t module_id, const uint32_t b = UE->mac_stats.dl.current_bytes; UE->dl_thr_ue = (1 - a) * UE->dl_thr_ue + a * b; + /* Check if this UE should get TA (every 100 frames). If we add the CE, + * ta_apply will be reset */ + if (frame == ((sched_ctrl->ta_frame + 100) % 1024)) + sched_ctrl->ta_apply = true; + int total_rem_ues = 0; for (int i = 0; i < num_beams; i++) total_rem_ues += remainUEs[i]; @@ -1010,6 +1015,370 @@ nfapi_nr_dl_tti_pdsch_pdu_rel15_t *prepare_pdsch_pdu(nfapi_nr_dl_tti_request_pdu return pdsch_pdu; } +void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE_info_t *UE, NR_sched_pdsch_t *sched_pdsch) +{ + int CC_id = 0; + int module_id = 0; + frame_t frame = pdsch->frame; + slot_t slot = pdsch->slot; + + const NR_ServingCellConfigCommon_t *scc = nr_mac->common_channels[CC_id].ServingCellConfigCommon; + NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; + NR_UE_DL_BWP_t *current_BWP = &UE->current_DL_BWP; + + const rnti_t rnti = UE->rnti; + + /* POST processing */ + const uint8_t nrOfLayers = sched_pdsch->nrOfLayers; + const uint32_t TBS = sched_pdsch->tb_size; + int8_t current_harq_pid = sched_pdsch->dl_harq_pid; + + if (current_harq_pid < 0) { + /* PP has not selected a specific HARQ Process, get a new one */ + current_harq_pid = sched_ctrl->available_dl_harq.head; + AssertFatal(current_harq_pid >= 0, + "no free HARQ process available for UE %04x\n", + UE->rnti); + remove_front_nr_list(&sched_ctrl->available_dl_harq); + sched_pdsch->dl_harq_pid = current_harq_pid; + } else { + /* PP selected a specific HARQ process. Check whether it will be a new + * transmission or a retransmission, and remove from the corresponding + * list */ + if (sched_ctrl->harq_processes[current_harq_pid].round == 0) + remove_nr_list(&sched_ctrl->available_dl_harq, current_harq_pid); + else + remove_nr_list(&sched_ctrl->retrans_dl_harq, current_harq_pid); + } + + NR_tda_info_t *tda_info = &sched_pdsch->tda_info; + NR_pdsch_dmrs_t *dmrs_parms = &sched_pdsch->dmrs_parms; + NR_UE_harq_t *harq = &sched_ctrl->harq_processes[current_harq_pid]; + NR_sched_pucch_t *pucch = NULL; + DevAssert(!harq->is_waiting); + if (sched_pdsch->pucch_allocation < 0) { + finish_nr_dl_harq(sched_ctrl, current_harq_pid); + } else { + pucch = &sched_ctrl->sched_pucch[sched_pdsch->pucch_allocation]; + add_tail_nr_list(&sched_ctrl->feedback_dl_harq, current_harq_pid); + harq->feedback_frame = pucch->frame; + harq->feedback_slot = pucch->ul_slot; + harq->is_waiting = true; + } + UE->mac_stats.dl.rounds[harq->round]++; + LOG_D(NR_MAC, + "%4d.%2d [DLSCH/PDSCH/PUCCH] RNTI %04x DCI L %d start %3d RBs %3d startSymbol %2d nb_symbol %2d dmrspos %x MCS %2d nrOfLayers %d TBS %4d HARQ PID %2d round %d RV %d NDI %d dl_data_to_ULACK %d (%d.%d) PUCCH allocation %d TPC %d\n", + frame, + slot, + rnti, + sched_ctrl->aggregation_level, + sched_pdsch->rbStart, + sched_pdsch->rbSize, + tda_info->startSymbolIndex, + tda_info->nrOfSymbols, + dmrs_parms->dl_dmrs_symb_pos, + sched_pdsch->mcs, + nrOfLayers, + TBS, + current_harq_pid, + harq->round, + nr_get_rv(harq->round % 4), + harq->ndi, + pucch ? pucch->timing_indicator : 0, + pucch ? pucch->frame : 0, + pucch ? pucch->ul_slot : 0, + sched_pdsch->pucch_allocation, + sched_ctrl->tpc1); + + const int bwp_id = current_BWP->bwp_id; + const int coresetid = sched_ctrl->coreset->controlResourceSetId; + + /* look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it */ + nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = nr_mac->pdcch_pdu_idx[CC_id][coresetid]; + + if (!pdcch_pdu) { + LOG_D(NR_MAC, "creating pdcch pdu, pdcch_pdu = NULL. \n"); + nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &pdsch->dl_req->dl_tti_pdu_list[pdsch->dl_req->nPDUs]; + memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); + dl_tti_pdcch_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE; + dl_tti_pdcch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu)); + pdsch->dl_req->nPDUs += 1; + pdcch_pdu = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; + LOG_D(NR_MAC,"Trying to configure DL pdcch for UE %04x, bwp %d, cs %d\n", UE->rnti, bwp_id, coresetid); + NR_ControlResourceSet_t *coreset = sched_ctrl->coreset; + nr_configure_pdcch(pdcch_pdu, coreset, &sched_ctrl->sched_pdcch); + nr_mac->pdcch_pdu_idx[CC_id][coresetid] = pdcch_pdu; + } + + nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &pdsch->dl_req->dl_tti_pdu_list[pdsch->dl_req->nPDUs]; + memset(dl_tti_pdsch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); + dl_tti_pdsch_pdu->PDUType = NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE; + dl_tti_pdsch_pdu->PDUSize = (uint8_t)(2 + sizeof(nfapi_nr_dl_tti_pdsch_pdu)); + pdsch->dl_req->nPDUs += 1; + + /* SCF222: PDU index incremented for each PDSCH PDU sent in TX control + * message. This is used to associate control information to data and is + * reset every slot. */ + const int pduindex = nr_mac->pdu_index[CC_id]++; + // TODO: verify the case where maxMIMO_Layers is NULL, in which case + // in principle maxMIMO_layers should be given by the maximum number of layers + // for PDSCH supported by the UE for the serving cell (5.4.2.1 of 38.212) + long maxMIMO_Layers = UE->sc_info.maxMIMO_Layers_PDSCH ? *UE->sc_info.maxMIMO_Layers_PDSCH : 1; + const int nl_tbslbrm = min(maxMIMO_Layers, 4); + nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu = prepare_pdsch_pdu(dl_tti_pdsch_pdu, + nr_mac, + UE, + sched_pdsch, + current_BWP->pdsch_Config, + false, + harq->round, + rnti, + UE->UE_beam_index, + nl_tbslbrm, + pduindex); + + + LOG_D(NR_MAC,"Configuring DCI/PDCCH in %d.%d at CCE %d, rnti %x\n", frame,slot,sched_ctrl->cce_index,rnti); + /* Fill PDCCH DL DCI PDU */ + nfapi_nr_dl_dci_pdu_t *dci_pdu = prepare_dci_pdu(pdcch_pdu, + scc, + sched_ctrl->search_space, + sched_ctrl->coreset, + sched_ctrl->aggregation_level, + sched_ctrl->cce_index, + UE->UE_beam_index, + rnti); + pdcch_pdu->numDlDci++; + + /* DCI payload */ + const int rnti_type = TYPE_C_RNTI_; + dci_pdu_rel15_t dci_payload = prepare_dci_dl_payload(nr_mac, + UE, + rnti_type, + sched_ctrl->search_space->searchSpaceType->present, + pdsch_pdu, + sched_pdsch, + pucch, + current_harq_pid, + 0, + false); + + // Reset TPC to 0 dB to not request new gain multiple times before computing new value for SNR + sched_ctrl->tpc1 = 1; + NR_PDSCH_Config_t *pdsch_Config = current_BWP->pdsch_Config; + AssertFatal(pdsch_Config == NULL + || pdsch_Config->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, + "Only frequency resource allocation type 1 is currently supported\n"); + + LOG_D(NR_MAC, + "%4d.%2d DCI type 1 payload: freq_alloc %d (%d,%d,%d), " + "nrOfLayers %d, time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d tpc %d ti %d\n", + frame, + slot, + dci_payload.frequency_domain_assignment.val, + pdsch_pdu->rbStart, + pdsch_pdu->rbSize, + pdsch_pdu->BWPSize, + pdsch_pdu->nrOfLayers, + dci_payload.time_domain_assignment.val, + dci_payload.vrb_to_prb_mapping.val, + dci_payload.mcs, + dci_payload.tb_scaling, + dci_payload.ndi, + dci_payload.rv, + dci_payload.tpc, + pucch ? pucch->timing_indicator : 0); + + fill_dci_pdu_rel15(&UE->sc_info, + current_BWP, + &UE->current_UL_BWP, + dci_pdu, + &dci_payload, + current_BWP->dci_format, + rnti_type, + bwp_id, + sched_ctrl->search_space, + sched_ctrl->coreset, + UE->pdsch_HARQ_ACK_Codebook, + nr_mac->cset0_bwp_size); + + LOG_D(NR_MAC, + "coreset params: FreqDomainResource %llx, start_symbol %d n_symb %d\n", + (unsigned long long)pdcch_pdu->FreqDomainResource, + pdcch_pdu->StartSymbolIndex, + pdcch_pdu->DurationSymbols); + + if (harq->round != 0) { /* retransmission */ + /* we do not have to do anything, since we do not require to get data + * from RLC or encode MAC CEs. The TX_req structure is filled below + * or copy data to FAPI structures */ + LOG_D(NR_MAC, + "%d.%2d DL retransmission RNTI %04x HARQ PID %d round %d NDI %d\n", + frame, + slot, + rnti, + current_harq_pid, + harq->round, + harq->ndi); + if (!get_softmodem_params()->phy_test) + AssertFatal(harq->sched_pdsch.tb_size == TBS, + "UE %04x mismatch between scheduled TBS and buffered TB for HARQ PID %d\n", + UE->rnti, + current_harq_pid); + else if (harq->sched_pdsch.tb_size != TBS) + LOG_E(NR_MAC, + "Mismatch between scheduled TBS and buffered TB for HARQ PID %d. No RTX control in phy-test mode. " + "Possible causes: presence of CSI-RS or DLSCH scheduled in the mixed slot.\n", + current_harq_pid); + T(T_GNB_MAC_RETRANSMISSION_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(rnti), + T_INT(frame), T_INT(slot), T_INT(current_harq_pid), T_INT(harq->round), T_BUFFER(harq->transportBlock.buf, TBS)); + UE->mac_stats.dl.total_rbs_retx += sched_pdsch->rbSize; + nr_mac->mac_stats.used_prb_aggregate += sched_pdsch->rbSize; + } else { /* initial transmission */ + LOG_D(NR_MAC, "Initial HARQ transmission in %d.%d\n", frame, slot); + uint8_t *buf = allocate_transportBlock_buffer(&harq->transportBlock, TBS); + /* first, write all CEs that might be there */ + int written = nr_write_ce_dlsch_pdu(module_id, + sched_ctrl, + (unsigned char *)buf, + 255, // no drx + NULL); // contention res id + buf += written; + uint8_t *bufEnd = buf + TBS - written; + DevAssert(TBS > written); + int dlsch_total_bytes = 0; + /* next, get RLC data */ + start_meas(&nr_mac->rlc_data_req); + int sdus = 0; + + if (sched_ctrl->num_total_bytes > 0) { + /* loop over all activated logical channels */ + for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); ++i) { + const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i); + const int lcid = c->lcid; + + if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0) + continue; // no data for this LC tbs_size_t len = 0; + + int lcid_bytes=0; + while (bufEnd-buf > sizeof(NR_MAC_SUBHEADER_LONG) + 1 ) { + // we do not know how much data we will get from RLC, i.e., whether it + // will be longer than 256B or not. Therefore, reserve space for long header, then + // fetch data, then fill real length + NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *) buf; + /* limit requested number of bytes to what preprocessor specified, or + * such that TBS is full */ + const rlc_buffer_occupancy_t ndata = min(sched_ctrl->rlc_status[lcid].bytes_in_buffer, + bufEnd-buf-sizeof(NR_MAC_SUBHEADER_LONG)); + tbs_size_t len = nr_mac_rlc_data_req(module_id, + rnti, + true, + lcid, + ndata, + (char *)buf+sizeof(NR_MAC_SUBHEADER_LONG)); + LOG_D(NR_MAC, + "%4d.%2d RNTI %04x: %d bytes from %s %d (ndata %d, remaining size %ld)\n", + frame, + slot, + rnti, + len, + lcid < 4 ? "DCCH" : "DTCH", + lcid, + ndata, + bufEnd-buf-sizeof(NR_MAC_SUBHEADER_LONG)); + + if (len == 0) + break; + + T(T_GNB_MAC_LCID_DL, T_INT(rnti), T_INT(frame), T_INT(slot), T_INT(lcid), T_INT(len * 8), T_INT(nr_rlc_tx_list_occupancy(rnti, lcid))); + header->R = 0; + header->F = 1; + header->LCID = lcid; + header->L = htons(len); + buf += len+sizeof(NR_MAC_SUBHEADER_LONG); + dlsch_total_bytes += len; + lcid_bytes += len; + sdus += 1; + } + + UE->mac_stats.dl.lc_bytes[lcid] += lcid_bytes; + } + } else if (get_softmodem_params()->phy_test || get_softmodem_params()->do_ra) { + /* we will need the large header, phy-test typically allocates all + * resources and fills to the last byte below */ + LOG_D(NR_MAC, "Configuring DL_TX in %d.%d: TBS %d of random data\n", frame, slot, TBS); + + if (bufEnd-buf > sizeof(NR_MAC_SUBHEADER_LONG) ) { + NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *) buf; + // fill dlsch_buffer with random data + header->R = 0; + header->F = 1; + header->LCID = DL_SCH_LCID_PADDING; + buf += sizeof(NR_MAC_SUBHEADER_LONG); + header->L = htons(bufEnd-buf); + + for (; ((intptr_t)buf) % 4; buf++) + *buf = lrand48() & 0xff; + for (; buf < bufEnd - 3; buf += 4) { + uint32_t *buf32 = (uint32_t *)buf; + *buf32 = lrand48(); + } + for (; buf < bufEnd; buf++) + *buf = lrand48() & 0xff; + sdus +=1; + } + } + + stop_meas(&nr_mac->rlc_data_req); + + // Add padding header and zero rest out if there is space left + if (bufEnd-buf > 0) { + NR_MAC_SUBHEADER_FIXED *padding = (NR_MAC_SUBHEADER_FIXED *) buf; + padding->R = 0; + padding->LCID = DL_SCH_LCID_PADDING; + buf += 1; + memset(buf,0,bufEnd-buf); + buf=bufEnd; + } + + UE->mac_stats.dl.total_bytes += TBS; + UE->mac_stats.dl.current_bytes = TBS; + UE->mac_stats.dl.total_rbs += sched_pdsch->rbSize; + UE->mac_stats.dl.num_mac_sdu += sdus; + UE->mac_stats.dl.current_rbs = sched_pdsch->rbSize; + UE->mac_stats.dl.total_sdu_bytes += dlsch_total_bytes; + nr_mac->mac_stats.used_prb_aggregate += sched_pdsch->rbSize; + + /* save retransmission information */ + harq->sched_pdsch = *sched_pdsch; + /* save which time allocation has been used, to be used on + * retransmissions */ + harq->sched_pdsch.time_domain_allocation = sched_pdsch->time_domain_allocation; + + // ta command is sent, values are reset + if (sched_ctrl->ta_apply) { + sched_ctrl->ta_apply = false; + sched_ctrl->ta_frame = frame; + LOG_D(NR_MAC, "%d.%2d UE %04x TA scheduled, resetting TA frame\n", frame, slot, UE->rnti); + } + + T(T_GNB_MAC_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(rnti), + T_INT(frame), T_INT(slot), T_INT(current_harq_pid), T_BUFFER(harq->transportBlock.buf, TBS)); + T(T_GNB_MAC_DL, T_INT(rnti), T_INT(frame), T_INT(slot), T_INT(sched_pdsch->mcs), T_INT(TBS)); + } + + const int ntx_req = pdsch->TX_req->Number_of_PDUs; + nfapi_nr_pdu_t *tx_req = &pdsch->TX_req->pdu_list[ntx_req]; + tx_req->PDU_index = pduindex; + tx_req->num_TLV = 1; + tx_req->TLVs[0].length = TBS; + tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, tx_req->TLVs[0].length); + memcpy(tx_req->TLVs[0].value.direct, harq->transportBlock.buf, TBS); + pdsch->TX_req->Number_of_PDUs++; + pdsch->TX_req->SFN = frame; + pdsch->TX_req->Slot = slot; +} + void nr_schedule_ue_spec(module_id_t module_id, frame_t frame, slot_t slot, @@ -1031,12 +1400,13 @@ void nr_schedule_ue_spec(module_id_t module_id, NR_UEs_t *UE_info = &gNB_mac->UE_info; nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body; + post_process_pdsch_t pdsch = { frame, slot, dl_req, TX_req }; + const NR_BWP_t *initialDL = &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; gNB_mac->mac_stats.total_prb_aggregate += NRRIV2BW(initialDL->locationAndBandwidth, MAX_BWP_SIZE); UE_iterator(UE_info->connected_ue_list, UE) { NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; - NR_UE_DL_BWP_t *current_BWP = &UE->current_DL_BWP; if (!nr_mac_ue_is_active(UE) && !get_softmodem_params()->phy_test) continue; @@ -1045,368 +1415,11 @@ void nr_schedule_ue_spec(module_id_t module_id, UE->mac_stats.dl.current_bytes = 0; UE->mac_stats.dl.current_rbs = 0; - /* update TA and set ta_apply every 100 frames. - * Possible improvement: take the periodicity from input file. - * If such UE is not scheduled now, it will be by the preprocessor later. - * If we add the CE, ta_apply will be reset */ - if (frame == ((sched_ctrl->ta_frame + 100) % 1024) && !nr_timer_is_active(&sched_ctrl->transm_interrupt)) { - sched_ctrl->ta_apply = true; /* the timer is reset once TA CE is scheduled */ - LOG_D(NR_MAC, "[UE %04x][%d.%d] UL timing alignment procedures: setting flag for Timing Advance command\n", UE->rnti, frame, slot); - } - if (sched_pdsch->rbSize <= 0) continue; - const rnti_t rnti = UE->rnti; + post_process_dlsch(gNB_mac, &pdsch, UE, sched_pdsch); - /* POST processing */ - const uint8_t nrOfLayers = sched_pdsch->nrOfLayers; - const uint32_t TBS = sched_pdsch->tb_size; - int8_t current_harq_pid = sched_pdsch->dl_harq_pid; - - if (current_harq_pid < 0) { - /* PP has not selected a specific HARQ Process, get a new one */ - current_harq_pid = sched_ctrl->available_dl_harq.head; - AssertFatal(current_harq_pid >= 0, - "no free HARQ process available for UE %04x\n", - UE->rnti); - remove_front_nr_list(&sched_ctrl->available_dl_harq); - sched_pdsch->dl_harq_pid = current_harq_pid; - } else { - /* PP selected a specific HARQ process. Check whether it will be a new - * transmission or a retransmission, and remove from the corresponding - * list */ - if (sched_ctrl->harq_processes[current_harq_pid].round == 0) - remove_nr_list(&sched_ctrl->available_dl_harq, current_harq_pid); - else - remove_nr_list(&sched_ctrl->retrans_dl_harq, current_harq_pid); - } - - NR_tda_info_t *tda_info = &sched_pdsch->tda_info; - NR_pdsch_dmrs_t *dmrs_parms = &sched_pdsch->dmrs_parms; - NR_UE_harq_t *harq = &sched_ctrl->harq_processes[current_harq_pid]; - NR_sched_pucch_t *pucch = NULL; - DevAssert(!harq->is_waiting); - if (sched_pdsch->pucch_allocation < 0) { - finish_nr_dl_harq(sched_ctrl, current_harq_pid); - } else { - pucch = &sched_ctrl->sched_pucch[sched_pdsch->pucch_allocation]; - add_tail_nr_list(&sched_ctrl->feedback_dl_harq, current_harq_pid); - harq->feedback_frame = pucch->frame; - harq->feedback_slot = pucch->ul_slot; - harq->is_waiting = true; - } - UE->mac_stats.dl.rounds[harq->round]++; - LOG_D(NR_MAC, - "%4d.%2d [DLSCH/PDSCH/PUCCH] RNTI %04x DCI L %d start %3d RBs %3d startSymbol %2d nb_symbol %2d dmrspos %x MCS %2d nrOfLayers %d TBS %4d HARQ PID %2d round %d RV %d NDI %d dl_data_to_ULACK %d (%d.%d) PUCCH allocation %d TPC %d\n", - frame, - slot, - rnti, - sched_ctrl->aggregation_level, - sched_pdsch->rbStart, - sched_pdsch->rbSize, - tda_info->startSymbolIndex, - tda_info->nrOfSymbols, - dmrs_parms->dl_dmrs_symb_pos, - sched_pdsch->mcs, - nrOfLayers, - TBS, - current_harq_pid, - harq->round, - nr_get_rv(harq->round % 4), - harq->ndi, - pucch ? pucch->timing_indicator : 0, - pucch ? pucch->frame : 0, - pucch ? pucch->ul_slot : 0, - sched_pdsch->pucch_allocation, - sched_ctrl->tpc1); - - const int bwp_id = current_BWP->bwp_id; - const int coresetid = sched_ctrl->coreset->controlResourceSetId; - - /* look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it */ - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][coresetid]; - - if (!pdcch_pdu) { - LOG_D(NR_MAC, "creating pdcch pdu, pdcch_pdu = NULL. \n"); - nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; - memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); - dl_tti_pdcch_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE; - dl_tti_pdcch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu)); - dl_req->nPDUs += 1; - pdcch_pdu = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15; - LOG_D(NR_MAC,"Trying to configure DL pdcch for UE %04x, bwp %d, cs %d\n", UE->rnti, bwp_id, coresetid); - NR_ControlResourceSet_t *coreset = sched_ctrl->coreset; - nr_configure_pdcch(pdcch_pdu, coreset, &sched_ctrl->sched_pdcch); - gNB_mac->pdcch_pdu_idx[CC_id][coresetid] = pdcch_pdu; - } - - nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs]; - memset(dl_tti_pdsch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t)); - dl_tti_pdsch_pdu->PDUType = NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE; - dl_tti_pdsch_pdu->PDUSize = (uint8_t)(2 + sizeof(nfapi_nr_dl_tti_pdsch_pdu)); - dl_req->nPDUs += 1; - /* SCF222: PDU index incremented for each PDSCH PDU sent in TX control - * message. This is used to associate control information to data and is - * reset every slot. */ - const int pduindex = gNB_mac->pdu_index[CC_id]++; - // TODO: verify the case where maxMIMO_Layers is NULL, in which case - // in principle maxMIMO_layers should be given by the maximum number of layers - // for PDSCH supported by the UE for the serving cell (5.4.2.1 of 38.212) - long maxMIMO_Layers = UE->sc_info.maxMIMO_Layers_PDSCH ? *UE->sc_info.maxMIMO_Layers_PDSCH : 1; - const int nl_tbslbrm = min(maxMIMO_Layers, 4); - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu = prepare_pdsch_pdu(dl_tti_pdsch_pdu, - gNB_mac, - UE, - sched_pdsch, - current_BWP->pdsch_Config, - false, - harq->round, - rnti, - UE->UE_beam_index, - nl_tbslbrm, - pduindex); - - - LOG_D(NR_MAC,"Configuring DCI/PDCCH in %d.%d at CCE %d, rnti %x\n", frame,slot,sched_ctrl->cce_index,rnti); - /* Fill PDCCH DL DCI PDU */ - nfapi_nr_dl_dci_pdu_t *dci_pdu = prepare_dci_pdu(pdcch_pdu, - scc, - sched_ctrl->search_space, - sched_ctrl->coreset, - sched_ctrl->aggregation_level, - sched_ctrl->cce_index, - UE->UE_beam_index, - rnti); - pdcch_pdu->numDlDci++; - - /* DCI payload */ - const int rnti_type = TYPE_C_RNTI_; - dci_pdu_rel15_t dci_payload = prepare_dci_dl_payload(gNB_mac, - UE, - rnti_type, - sched_ctrl->search_space->searchSpaceType->present, - pdsch_pdu, - sched_pdsch, - pucch, - current_harq_pid, - 0, - false); - - // Reset TPC to 0 dB to not request new gain multiple times before computing new value for SNR - sched_ctrl->tpc1 = 1; - NR_PDSCH_Config_t *pdsch_Config = current_BWP->pdsch_Config; - AssertFatal(pdsch_Config == NULL - || pdsch_Config->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1, - "Only frequency resource allocation type 1 is currently supported\n"); - - LOG_D(NR_MAC, - "%4d.%2d DCI type 1 payload: freq_alloc %d (%d,%d,%d), " - "nrOfLayers %d, time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d tpc %d ti %d\n", - frame, - slot, - dci_payload.frequency_domain_assignment.val, - pdsch_pdu->rbStart, - pdsch_pdu->rbSize, - pdsch_pdu->BWPSize, - pdsch_pdu->nrOfLayers, - dci_payload.time_domain_assignment.val, - dci_payload.vrb_to_prb_mapping.val, - dci_payload.mcs, - dci_payload.tb_scaling, - dci_payload.ndi, - dci_payload.rv, - dci_payload.tpc, - pucch ? pucch->timing_indicator : 0); - - fill_dci_pdu_rel15(&UE->sc_info, - current_BWP, - &UE->current_UL_BWP, - dci_pdu, - &dci_payload, - current_BWP->dci_format, - rnti_type, - bwp_id, - sched_ctrl->search_space, - sched_ctrl->coreset, - UE->pdsch_HARQ_ACK_Codebook, - gNB_mac->cset0_bwp_size); - - LOG_D(NR_MAC, - "coreset params: FreqDomainResource %llx, start_symbol %d n_symb %d\n", - (unsigned long long)pdcch_pdu->FreqDomainResource, - pdcch_pdu->StartSymbolIndex, - pdcch_pdu->DurationSymbols); - - if (harq->round != 0) { /* retransmission */ - /* we do not have to do anything, since we do not require to get data - * from RLC or encode MAC CEs. The TX_req structure is filled below - * or copy data to FAPI structures */ - LOG_D(NR_MAC, - "%d.%2d DL retransmission RNTI %04x HARQ PID %d round %d NDI %d\n", - frame, - slot, - rnti, - current_harq_pid, - harq->round, - harq->ndi); - if (!get_softmodem_params()->phy_test) - AssertFatal(harq->sched_pdsch.tb_size == TBS, - "UE %04x mismatch between scheduled TBS and buffered TB for HARQ PID %d\n", - UE->rnti, - current_harq_pid); - else if (harq->sched_pdsch.tb_size != TBS) - LOG_E(NR_MAC, - "Mismatch between scheduled TBS and buffered TB for HARQ PID %d. No RTX control in phy-test mode. " - "Possible causes: presence of CSI-RS or DLSCH scheduled in the mixed slot.\n", - current_harq_pid); - T(T_GNB_MAC_RETRANSMISSION_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(rnti), - T_INT(frame), T_INT(slot), T_INT(current_harq_pid), T_INT(harq->round), T_BUFFER(harq->transportBlock.buf, TBS)); - UE->mac_stats.dl.total_rbs_retx += sched_pdsch->rbSize; - gNB_mac->mac_stats.used_prb_aggregate += sched_pdsch->rbSize; - } else { /* initial transmission */ - LOG_D(NR_MAC, "Initial HARQ transmission in %d.%d\n", frame, slot); - uint8_t *buf = allocate_transportBlock_buffer(&harq->transportBlock, TBS); - /* first, write all CEs that might be there */ - int written = nr_write_ce_dlsch_pdu(module_id, - sched_ctrl, - (unsigned char *)buf, - 255, // no drx - NULL); // contention res id - buf += written; - uint8_t *bufEnd = buf + TBS - written; - DevAssert(TBS > written); - int dlsch_total_bytes = 0; - /* next, get RLC data */ - start_meas(&gNB_mac->rlc_data_req); - int sdus = 0; - - if (sched_ctrl->num_total_bytes > 0) { - /* loop over all activated logical channels */ - for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); ++i) { - const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i); - const int lcid = c->lcid; - - if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0) - continue; // no data for this LC tbs_size_t len = 0; - - int lcid_bytes=0; - while (bufEnd-buf > sizeof(NR_MAC_SUBHEADER_LONG) + 1 ) { - // we do not know how much data we will get from RLC, i.e., whether it - // will be longer than 256B or not. Therefore, reserve space for long header, then - // fetch data, then fill real length - NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *) buf; - /* limit requested number of bytes to what preprocessor specified, or - * such that TBS is full */ - const rlc_buffer_occupancy_t ndata = min(sched_ctrl->rlc_status[lcid].bytes_in_buffer, - bufEnd-buf-sizeof(NR_MAC_SUBHEADER_LONG)); - tbs_size_t len = nr_mac_rlc_data_req(module_id, - rnti, - true, - lcid, - ndata, - (char *)buf+sizeof(NR_MAC_SUBHEADER_LONG)); - LOG_D(NR_MAC, - "%4d.%2d RNTI %04x: %d bytes from %s %d (ndata %d, remaining size %ld)\n", - frame, - slot, - rnti, - len, - lcid < 4 ? "DCCH" : "DTCH", - lcid, - ndata, - bufEnd-buf-sizeof(NR_MAC_SUBHEADER_LONG)); - - if (len == 0) - break; - - T(T_GNB_MAC_LCID_DL, T_INT(rnti), T_INT(frame), T_INT(slot), T_INT(lcid), T_INT(len * 8), T_INT(nr_rlc_tx_list_occupancy(rnti, lcid))); - header->R = 0; - header->F = 1; - header->LCID = lcid; - header->L = htons(len); - buf += len+sizeof(NR_MAC_SUBHEADER_LONG); - dlsch_total_bytes += len; - lcid_bytes += len; - sdus += 1; - } - - UE->mac_stats.dl.lc_bytes[lcid] += lcid_bytes; - } - } else if (get_softmodem_params()->phy_test || get_softmodem_params()->do_ra) { - /* we will need the large header, phy-test typically allocates all - * resources and fills to the last byte below */ - LOG_D(NR_MAC, "Configuring DL_TX in %d.%d: TBS %d of random data\n", frame, slot, TBS); - - if (bufEnd-buf > sizeof(NR_MAC_SUBHEADER_LONG) ) { - NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *) buf; - // fill dlsch_buffer with random data - header->R = 0; - header->F = 1; - header->LCID = DL_SCH_LCID_PADDING; - buf += sizeof(NR_MAC_SUBHEADER_LONG); - header->L = htons(bufEnd-buf); - - for (; ((intptr_t)buf) % 4; buf++) - *buf = lrand48() & 0xff; - for (; buf < bufEnd - 3; buf += 4) { - uint32_t *buf32 = (uint32_t *)buf; - *buf32 = lrand48(); - } - for (; buf < bufEnd; buf++) - *buf = lrand48() & 0xff; - sdus +=1; - } - } - - stop_meas(&gNB_mac->rlc_data_req); - - // Add padding header and zero rest out if there is space left - if (bufEnd-buf > 0) { - NR_MAC_SUBHEADER_FIXED *padding = (NR_MAC_SUBHEADER_FIXED *) buf; - padding->R = 0; - padding->LCID = DL_SCH_LCID_PADDING; - buf += 1; - memset(buf,0,bufEnd-buf); - buf=bufEnd; - } - - UE->mac_stats.dl.total_bytes += TBS; - UE->mac_stats.dl.current_bytes = TBS; - UE->mac_stats.dl.total_rbs += sched_pdsch->rbSize; - UE->mac_stats.dl.num_mac_sdu += sdus; - UE->mac_stats.dl.current_rbs = sched_pdsch->rbSize; - UE->mac_stats.dl.total_sdu_bytes += dlsch_total_bytes; - gNB_mac->mac_stats.used_prb_aggregate += sched_pdsch->rbSize; - - /* save retransmission information */ - harq->sched_pdsch = *sched_pdsch; - /* save which time allocation has been used, to be used on - * retransmissions */ - harq->sched_pdsch.time_domain_allocation = sched_pdsch->time_domain_allocation; - - // ta command is sent, values are reset - if (sched_ctrl->ta_apply) { - sched_ctrl->ta_apply = false; - sched_ctrl->ta_frame = frame; - LOG_D(NR_MAC, "%d.%2d UE %04x TA scheduled, resetting TA frame\n", frame, slot, UE->rnti); - } - - T(T_GNB_MAC_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(rnti), - T_INT(frame), T_INT(slot), T_INT(current_harq_pid), T_BUFFER(harq->transportBlock.buf, TBS)); - T(T_GNB_MAC_DL, T_INT(rnti), T_INT(frame), T_INT(slot), T_INT(sched_pdsch->mcs), T_INT(TBS)); - } - - const int ntx_req = TX_req->Number_of_PDUs; - nfapi_nr_pdu_t *tx_req = &TX_req->pdu_list[ntx_req]; - tx_req->PDU_index = pduindex; - tx_req->num_TLV = 1; - tx_req->TLVs[0].length = TBS; - tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, tx_req->TLVs[0].length); - memcpy(tx_req->TLVs[0].value.direct, harq->transportBlock.buf, TBS); - TX_req->Number_of_PDUs++; - TX_req->SFN = frame; - TX_req->Slot = slot; /* mark UE as scheduled */ sched_pdsch->rbSize = 0; } diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index f64b94fb47..544e218e5b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -821,6 +821,17 @@ typedef struct { #define UE_iterator(BaSe, VaR) for (NR_UE_info_t **VaR##pptr=BaSe, *VaR=*VaR##pptr; VaR; VaR=*(++VaR##pptr)) +typedef struct { + /// current frame + frame_t frame; + /// current slot + slot_t slot; + /// FAPI DL req in which allocations are made + nfapi_nr_dl_tti_request_body_t *dl_req; + /// TX_data request holds the actual data + nfapi_nr_tx_data_request_t *TX_req; +} post_process_pdsch_t; + typedef struct { /// current frame for DCI frame_t frame; From 1a752a8433a72bdae1ef78b06bb008301c928527 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 22 Sep 2025 10:18:13 +0200 Subject: [PATCH 125/275] Remove unused/write-only code --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 7fe346ab7d..03abcbfd0c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -587,7 +587,6 @@ static bool allocate_dl_retransmission(module_id_t module_id, return true; } -static uint32_t pf_tbs[3][29]; // pre-computed, approximate TBS values for PF coefficient typedef struct UEsched_s { float coef; NR_UE_info_t * UE; @@ -909,31 +908,8 @@ static void nr_dlsch_preprocessor(module_id_t module_id, frame_t frame, slot_t s pf_dl(module_id, frame, slot, UE_info->connected_ue_list, max_sched_ues, num_beams, n_rb_sched); } -nr_pp_impl_dl nr_init_dlsch_preprocessor(int CC_id) { - /* during initialization: no mutex needed */ - /* in the PF algorithm, we have to use the TBsize to compute the coefficient. - * This would include the number of DMRS symbols, which in turn depends on - * the time domain allocation. In case we are in a mixed slot, we do not want - * to recalculate all these values just, and therefore we provide a look-up - * table which should approximately give us the TBsize */ - for (int mcsTableIdx = 0; mcsTableIdx < 3; ++mcsTableIdx) { - for (int mcs = 0; mcs < 29; ++mcs) { - if (mcs > 27 && mcsTableIdx == 1) - continue; - - const uint8_t Qm = nr_get_Qm_dl(mcs, mcsTableIdx); - const uint16_t R = nr_get_code_rate_dl(mcs, mcsTableIdx); - pf_tbs[mcsTableIdx][mcs] = nr_compute_tbs(Qm, - R, - 1, /* rbSize */ - 10, /* hypothetical number of slots */ - 0, /* N_PRB_DMRS * N_DMRS_SLOT */ - 0 /* N_PRB_oh, 0 for initialBWP */, - 0 /* tb_scaling */, - 1 /* nrOfLayers */) >> 3; - } - } - +nr_pp_impl_dl nr_init_dlsch_preprocessor(int CC_id) +{ return nr_dlsch_preprocessor; } From 16227243843d6759491972c24196557358616c38 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 22 Sep 2025 11:53:44 +0200 Subject: [PATCH 126/275] Store selected MCS in UE iterator PF uses MCS and average throughput to decide which UE to schedule. Thus, the first loop decides on MCS, whereas the second does the "main UE allocation" after sorting by the PF metric. A follow-up commit will remove sched_pusch from NR_UE_sched_ctrl_t. Thus, we cannot rely on sched_ctrl to store the MCS in the first loop, and look it up from there in the second. Instead, store it as part of the UE iterator data, and take it from there. See also: f3068caa3774 ("Store selected MCS in UE iterator", for UL) --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 03abcbfd0c..7b39e0eabd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -590,6 +590,7 @@ static bool allocate_dl_retransmission(module_id_t module_id, typedef struct UEsched_s { float coef; NR_UE_info_t * UE; + int selected_mcs; } UEsched_t; static int comparator(const void *p, const void *q) @@ -686,17 +687,16 @@ static void pf_dl(module_id_t module_id, const NR_bler_options_t *bo = &mac->dl_bler; const int max_mcs_table = current_BWP->mcsTableIdx == 1 ? 27 : 28; const int max_mcs = min(sched_ctrl->dl_max_mcs, max_mcs_table); + int selected_mcs; if (bo->harq_round_max == 1) { int new_mcs = min(bo->max_mcs, max_mcs); - sched_pdsch->mcs = max(bo->min_mcs, new_mcs); - sched_ctrl->dl_bler_stats.mcs = sched_pdsch->mcs; + selected_mcs = max(bo->min_mcs, new_mcs); + sched_ctrl->dl_bler_stats.mcs = selected_mcs; } else - sched_pdsch->mcs = get_mcs_from_bler(bo, stats, &sched_ctrl->dl_bler_stats, max_mcs, frame); - sched_pdsch->nrOfLayers = get_dl_nrOfLayers(sched_ctrl, current_BWP->dci_format); - sched_pdsch->pm_index = - get_pm_index(mac, UE, current_BWP->dci_format, sched_pdsch->nrOfLayers, mac->radio_config.pdsch_AntennaPorts.XP); - const uint8_t Qm = nr_get_Qm_dl(sched_pdsch->mcs, current_BWP->mcsTableIdx); - const uint16_t R = nr_get_code_rate_dl(sched_pdsch->mcs, current_BWP->mcsTableIdx); + selected_mcs = get_mcs_from_bler(bo, stats, &sched_ctrl->dl_bler_stats, max_mcs, frame); + int l = get_dl_nrOfLayers(sched_ctrl, current_BWP->dci_format); + const uint8_t Qm = nr_get_Qm_dl(selected_mcs, current_BWP->mcsTableIdx); + const uint16_t R = nr_get_code_rate_dl(selected_mcs, current_BWP->mcsTableIdx); uint32_t tbs = nr_compute_tbs(Qm, R, 1, /* rbSize */ @@ -704,7 +704,7 @@ static void pf_dl(module_id_t module_id, 0, /* N_PRB_DMRS * N_DMRS_SLOT */ 0 /* N_PRB_oh, 0 for initialBWP */, 0 /* tb_scaling */, - sched_pdsch->nrOfLayers) >> 3; + l) >> 3; float coeff_ue = (float) tbs / UE->dl_thr_ue; LOG_D(NR_MAC, "[UE %04x][%4d.%2d] b %d, thr_ue %f, tbs %d, coeff_ue %f\n", UE->rnti, @@ -717,6 +717,7 @@ static void pf_dl(module_id_t module_id, /* Create UE_sched list for UEs eligible for new transmission*/ UE_sched[numUE].coef = coeff_ue; UE_sched[numUE].UE = UE; + UE_sched[numUE].selected_mcs = selected_mcs; numUE++; } } @@ -843,6 +844,10 @@ static void pf_dl(module_id_t module_id, sched_ctrl->cce_index = CCEIndex; fill_pdcch_vrb_map(mac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, sched_ctrl->aggregation_level, beam.idx); + int l = get_dl_nrOfLayers(sched_ctrl, dl_bwp->dci_format); + sched_pdsch->nrOfLayers = l; + sched_pdsch->mcs = iterator->selected_mcs; + sched_pdsch->pm_index = get_pm_index(mac, iterator->UE, dl_bwp->dci_format, l, mac->radio_config.pdsch_AntennaPorts.XP); sched_pdsch->dmrs_parms = get_dl_dmrs_params(scc, dl_bwp, tda_info, sched_pdsch->nrOfLayers); sched_pdsch->Qm = nr_get_Qm_dl(sched_pdsch->mcs, dl_bwp->mcsTableIdx); sched_pdsch->R = nr_get_code_rate_dl(sched_pdsch->mcs, dl_bwp->mcsTableIdx); From a6d16a66d2fa2203197b6ddcd4b44ddf9a576b5c Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 22 Sep 2025 12:05:33 +0200 Subject: [PATCH 127/275] Reduce scope of NR_sched_pdsch in first DL sched loop We don't need NR_sched_pdsch here. Remove the use of it. --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 7b39e0eabd..8d47b98924 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -632,9 +632,8 @@ static void pf_dl(module_id_t module_id, continue; const NR_mac_dir_stats_t *stats = &UE->mac_stats.dl; - NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; /* get the PID of a HARQ process awaiting retrnasmission, or -1 otherwise */ - sched_pdsch->dl_harq_pid = sched_ctrl->retrans_dl_harq.head; + int harq_pid = sched_ctrl->retrans_dl_harq.head; /* Calculate Throughput */ const float a = 0.01f; @@ -653,12 +652,12 @@ static void pf_dl(module_id_t module_id, continue; /* retransmission */ - if (sched_pdsch->dl_harq_pid >= 0) { + if (harq_pid >= 0) { NR_beam_alloc_t beam = beam_allocation_procedure(&mac->beam_info, frame, slot, UE->UE_beam_index, slots_per_frame); bool sch_ret = beam.idx >= 0; /* Allocate retransmission */ if (sch_ret) - sch_ret = allocate_dl_retransmission(module_id, frame, slot, &n_rb_sched[beam.idx], UE, beam.idx, sched_pdsch->dl_harq_pid); + sch_ret = allocate_dl_retransmission(module_id, frame, slot, &n_rb_sched[beam.idx], UE, beam.idx, harq_pid); if (!sch_ret) { LOG_D(NR_MAC, "[UE %04x][%4d.%2d] DL retransmission could not be allocated\n", UE->rnti, frame, slot); reset_beam_status(&mac->beam_info, frame, slot, UE->UE_beam_index, slots_per_frame, beam.new_beam); From 761a70d199b7cd35e449924e38b4352ba3da1e16 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 22 Sep 2025 13:50:07 +0200 Subject: [PATCH 128/275] Remove use of curInfo in allocate_dl_retransmission() It seems to be assumed that curInfo actually has the currently active number of layers/PMI. However, I am not sure this would always be true (e.g., UE might not have been scheduled for some slots, but number of layers decreased as reported by CSI). Instead, recalculate the info from scratch when necessary. --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 8d47b98924..c763128d23 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -438,13 +438,14 @@ static bool allocate_dl_retransmission(module_id_t module_id, NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP; NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP; NR_sched_pdsch_t *retInfo = &sched_ctrl->harq_processes[current_harq_pid].sched_pdsch; - NR_sched_pdsch_t *curInfo = &sched_ctrl->sched_pdsch; + int layers = get_dl_nrOfLayers(sched_ctrl, dl_bwp->dci_format); + int pm_index = get_pm_index(nr_mac, UE, dl_bwp->dci_format, layers, nr_mac->radio_config.pdsch_AntennaPorts.XP); // If the RI changed between current rtx and a previous transmission // we need to verify if it is not decreased // othwise it wouldn't be possible to transmit the same TBS - int layers = (curInfo->nrOfLayers < retInfo->nrOfLayers) ? curInfo->nrOfLayers : retInfo->nrOfLayers; - int pm_index = (curInfo->nrOfLayers < retInfo->nrOfLayers) ? curInfo->pm_index : retInfo->pm_index; + layers = (layers < retInfo->nrOfLayers) ? layers : retInfo->nrOfLayers; + pm_index = (layers < retInfo->nrOfLayers) ? pm_index : retInfo->pm_index; const int coresetid = sched_ctrl->coreset->controlResourceSetId; const int tda = get_dl_tda(nr_mac, slot); From 2daa1dac185052c182074ec729f5d4ed208eaf83 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 22 Sep 2025 14:30:06 +0200 Subject: [PATCH 129/275] allocate_dl_retransmission(): refactor NR_sched_pdsch usage Assign NR_sched_pdsch to sched_ctrl->sched_pdsch in one place, where the post-processing functionality will be called in a follow-up commit. This should result in no functional change. --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index c763128d23..befe95b525 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -437,15 +437,15 @@ static bool allocate_dl_retransmission(module_id_t module_id, NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP; NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP; - NR_sched_pdsch_t *retInfo = &sched_ctrl->harq_processes[current_harq_pid].sched_pdsch; + NR_sched_pdsch_t new_sched = sched_ctrl->harq_processes[current_harq_pid].sched_pdsch; int layers = get_dl_nrOfLayers(sched_ctrl, dl_bwp->dci_format); int pm_index = get_pm_index(nr_mac, UE, dl_bwp->dci_format, layers, nr_mac->radio_config.pdsch_AntennaPorts.XP); // If the RI changed between current rtx and a previous transmission // we need to verify if it is not decreased // othwise it wouldn't be possible to transmit the same TBS - layers = (layers < retInfo->nrOfLayers) ? layers : retInfo->nrOfLayers; - pm_index = (layers < retInfo->nrOfLayers) ? pm_index : retInfo->pm_index; + layers = (layers < new_sched.nrOfLayers) ? layers : new_sched.nrOfLayers; + pm_index = (layers < new_sched.nrOfLayers) ? pm_index : new_sched.pm_index; const int coresetid = sched_ctrl->coreset->controlResourceSetId; const int tda = get_dl_tda(nr_mac, slot); @@ -464,11 +464,11 @@ static bool allocate_dl_retransmission(module_id_t module_id, if (!temp_tda.valid_tda) return false; - bool reuse_old_tda = (retInfo->tda_info.startSymbolIndex == temp_tda.startSymbolIndex) && (retInfo->tda_info.nrOfSymbols <= temp_tda.nrOfSymbols); + bool reuse_old_tda = (new_sched.tda_info.startSymbolIndex == temp_tda.startSymbolIndex) && (new_sched.tda_info.nrOfSymbols <= temp_tda.nrOfSymbols); LOG_D(NR_MAC, "[UE %x] %s old TDA, %s number of layers\n", UE->rnti, reuse_old_tda ? "reuse" : "do not reuse", - layers == retInfo->nrOfLayers ? "same" : "different"); + layers == new_sched.nrOfLayers ? "same" : "different"); uint16_t *rballoc_mask = nr_mac->common_channels[CC_id].vrb_map[beam_idx]; @@ -477,13 +477,13 @@ static bool allocate_dl_retransmission(module_id_t module_id, int rbStop = bwp_info.bwpStart + bwp_info.bwpSize - 1; int rbSize = 0; - if (reuse_old_tda && layers == retInfo->nrOfLayers) { + if (reuse_old_tda && layers == new_sched.nrOfLayers) { /* Check that there are enough resources for retransmission */ - while (rbSize < retInfo->rbSize) { + while (rbSize < new_sched.rbSize) { rbStart += rbSize; /* last iteration rbSize was not enough, skip it */ rbSize = 0; - const uint16_t slbitmap = SL_to_bitmap(retInfo->tda_info.startSymbolIndex, retInfo->tda_info.nrOfSymbols); + const uint16_t slbitmap = SL_to_bitmap(new_sched.tda_info.startSymbolIndex, new_sched.tda_info.nrOfSymbols); while (rbStart < rbStop && (rballoc_mask[rbStart] & slbitmap)) rbStart++; @@ -492,7 +492,7 @@ static bool allocate_dl_retransmission(module_id_t module_id, return false; } - while (rbStart + rbSize <= rbStop && !(rballoc_mask[rbStart + rbSize] & slbitmap) && rbSize < retInfo->rbSize) + while (rbStart + rbSize <= rbStop && !(rballoc_mask[rbStart + rbSize] & slbitmap) && rbSize < new_sched.rbSize) rbSize++; } } else { @@ -509,37 +509,37 @@ static bool allocate_dl_retransmission(module_id_t module_id, uint32_t new_tbs; uint16_t new_rbSize; - bool success = nr_find_nb_rb(retInfo->Qm, - retInfo->R, + bool success = nr_find_nb_rb(new_sched.Qm, + new_sched.R, 1, // no transform precoding for DL layers, temp_tda.nrOfSymbols, temp_dmrs.N_PRB_DMRS * temp_dmrs.N_DMRS_SLOT, - retInfo->tb_size, + new_sched.tb_size, 1, /* minimum of 1RB: need to find exact TBS, don't preclude any number */ rbSize, &new_tbs, &new_rbSize); - if (!success || new_tbs != retInfo->tb_size) { + if (!success || new_tbs != new_sched.tb_size) { LOG_D(NR_MAC, "[UE %04x][%4d.%2d] allocation of DL retransmission failed: new TBS %d of new TDA does not match old TBS %d\n", UE->rnti, frame, slot, new_tbs, - retInfo->tb_size); + new_sched.tb_size); return false; /* the maximum TBsize we might have is smaller than what we need */ } /* we can allocate it. Overwrite the time_domain_allocation, the number * of RBs, and the new TB size. The rest is done below */ - retInfo->tb_size = new_tbs; - retInfo->rbSize = new_rbSize; - retInfo->time_domain_allocation = tda; - retInfo->nrOfLayers = layers; - retInfo->pm_index = pm_index; - retInfo->dmrs_parms = temp_dmrs; - retInfo->tda_info = temp_tda; + new_sched.tb_size = new_tbs; + new_sched.rbSize = new_rbSize; + new_sched.time_domain_allocation = tda; + new_sched.nrOfLayers = layers; + new_sched.pm_index = pm_index; + new_sched.dmrs_parms = temp_dmrs; + new_sched.tda_info = temp_tda; } /* Find a free CCE */ @@ -574,16 +574,17 @@ static bool allocate_dl_retransmission(module_id_t module_id, sched_ctrl->cce_index = CCEIndex; fill_pdcch_vrb_map(nr_mac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, sched_ctrl->aggregation_level, beam_idx); - /* just reuse from previous scheduling opportunity, set new start RB */ - sched_ctrl->sched_pdsch = *retInfo; - sched_ctrl->sched_pdsch.rbStart = rbStart - bwp_info.bwpStart; - sched_ctrl->sched_pdsch.pucch_allocation = alloc; - sched_ctrl->sched_pdsch.bwp_info = bwp_info; - /* retransmissions: directly allocate */ - *n_rb_sched -= sched_ctrl->sched_pdsch.rbSize; - for (int rb = rbStart; rb < sched_ctrl->sched_pdsch.rbSize; rb++) - rballoc_mask[rb] |= SL_to_bitmap(retInfo->tda_info.startSymbolIndex, retInfo->tda_info.nrOfSymbols); + new_sched.rbStart = rbStart - bwp_info.bwpStart; + new_sched.pucch_allocation = alloc; + new_sched.bwp_info = bwp_info; + sched_ctrl->sched_pdsch = new_sched; + + /* retransmissions: directly allocate */ + *n_rb_sched -= new_sched.rbSize; + + for (int rb = rbStart; rb < new_sched.rbSize; rb++) + rballoc_mask[rb] |= SL_to_bitmap(new_sched.tda_info.startSymbolIndex, new_sched.tda_info.nrOfSymbols); return true; } From d4dc044d2dd3f556a132473cea1d71c33da00a98 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 22 Sep 2025 14:49:48 +0200 Subject: [PATCH 130/275] pf_dl(): refactor NR_pdsch_sched usage As in parent commit. --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 79 ++++++++++--------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index befe95b525..4a39969be4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -759,33 +759,28 @@ static void pf_dl(module_id_t module_id, continue; } - NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; - sched_pdsch->dl_harq_pid = sched_ctrl->available_dl_harq.head; - /* MCS has been set above */ - sched_pdsch->time_domain_allocation = get_dl_tda(mac, slot); - AssertFatal(sched_pdsch->time_domain_allocation>=0,"Unable to find PDSCH time domain allocation in list\n"); + int tda = get_dl_tda(mac, slot); + AssertFatal(tda >= 0,"Unable to find PDSCH time domain allocation in list\n"); const int coresetid = sched_ctrl->coreset->controlResourceSetId; - sched_pdsch->tda_info = get_dl_tda_info(dl_bwp, + NR_tda_info_t tda_info = get_dl_tda_info(dl_bwp, sched_ctrl->search_space->searchSpaceType->present, - sched_pdsch->time_domain_allocation, + tda, scc->dmrs_TypeA_Position, 1, TYPE_C_RNTI_, coresetid, false); - AssertFatal(sched_pdsch->tda_info.valid_tda, "Invalid TDA from get_dl_tda_info\n"); + AssertFatal(tda_info.valid_tda, "Invalid TDA from get_dl_tda_info\n"); - NR_tda_info_t *tda_info = &sched_pdsch->tda_info; - - const uint16_t slbitmap = SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols); + const uint16_t slbitmap = SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols); uint16_t *rballoc_mask = mac->common_channels[CC_id].vrb_map[beam.idx]; - sched_pdsch->bwp_info = get_pdsch_bwp_start_size(mac, iterator->UE); + bwp_info_t bwp_info = get_pdsch_bwp_start_size(mac, iterator->UE); int rbStart = 0; // WRT BWP start - int rbStop = sched_pdsch->bwp_info.bwpSize - 1; - int bwp_start = sched_pdsch->bwp_info.bwpStart; + int rbStop = bwp_info.bwpSize - 1; + int bwp_start = bwp_info.bwpStart; // Freq-demain allocation while (rbStart < rbStop && (rballoc_mask[rbStart + bwp_start] & slbitmap)) rbStart++; @@ -831,7 +826,7 @@ static void pf_dl(module_id_t module_id, * allocation after CCE alloc fail would be more complex) */ int alloc = -1; - if (!get_FeedbackDisabled(iterator->UE->sc_info.downlinkHARQ_FeedbackDisabled_r17, sched_pdsch->dl_harq_pid)) { + if (!get_FeedbackDisabled(iterator->UE->sc_info.downlinkHARQ_FeedbackDisabled_r17, sched_ctrl->available_dl_harq.head)) { int r_pucch = nr_get_pucch_resource(sched_ctrl->coreset, ul_bwp->pucch_Config, CCEIndex); alloc = nr_acknack_scheduling(mac, iterator->UE, frame, slot, iterator->UE->UE_beam_index, r_pucch, 0); if (alloc < 0) { @@ -846,40 +841,48 @@ static void pf_dl(module_id_t module_id, fill_pdcch_vrb_map(mac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, sched_ctrl->aggregation_level, beam.idx); int l = get_dl_nrOfLayers(sched_ctrl, dl_bwp->dci_format); - sched_pdsch->nrOfLayers = l; - sched_pdsch->mcs = iterator->selected_mcs; - sched_pdsch->pm_index = get_pm_index(mac, iterator->UE, dl_bwp->dci_format, l, mac->radio_config.pdsch_AntennaPorts.XP); - sched_pdsch->dmrs_parms = get_dl_dmrs_params(scc, dl_bwp, tda_info, sched_pdsch->nrOfLayers); - sched_pdsch->Qm = nr_get_Qm_dl(sched_pdsch->mcs, dl_bwp->mcsTableIdx); - sched_pdsch->R = nr_get_code_rate_dl(sched_pdsch->mcs, dl_bwp->mcsTableIdx); - sched_pdsch->pucch_allocation = alloc; - uint32_t TBS = 0; - uint16_t rbSize; + NR_sched_pdsch_t sched_pdsch = { + // rbSize below + .rbStart = rbStart, + .mcs = iterator->selected_mcs, + .R = nr_get_code_rate_dl(iterator->selected_mcs, dl_bwp->mcsTableIdx), + .Qm = nr_get_Qm_dl(iterator->selected_mcs, dl_bwp->mcsTableIdx), + // tb_size below + .dl_harq_pid = sched_ctrl->available_dl_harq.head, + .pucch_allocation = alloc, + .pm_index = get_pm_index(mac, iterator->UE, dl_bwp->dci_format, l, mac->radio_config.pdsch_AntennaPorts.XP), + .nrOfLayers = l, + .bwp_info = bwp_info, + .dmrs_parms = get_dl_dmrs_params(scc, dl_bwp, &tda_info, l), + .time_domain_allocation = tda, + .tda_info = tda_info, + }; + // Fix me: currently, the RLC does not give us the total number of PDUs // awaiting. Therefore, for the time being, we put a fixed overhead of 12 // (for 4 PDUs) and optionally + 2 for TA. Once RLC gives the number of // PDUs, we replace with 3 * numPDUs const int oh = 3 * 4 + 2 * (frame == (sched_ctrl->ta_frame + 100) % 1024); //const int oh = 3 * sched_ctrl->dl_pdus_total + 2 * (frame == (sched_ctrl->ta_frame + 100) % 1024); - nr_find_nb_rb(sched_pdsch->Qm, - sched_pdsch->R, + nr_find_nb_rb(sched_pdsch.Qm, + sched_pdsch.R, 1, // no transform precoding for DL - sched_pdsch->nrOfLayers, - tda_info->nrOfSymbols, - sched_pdsch->dmrs_parms.N_PRB_DMRS * sched_pdsch->dmrs_parms.N_DMRS_SLOT, + sched_pdsch.nrOfLayers, + tda_info.nrOfSymbols, + sched_pdsch.dmrs_parms.N_PRB_DMRS * sched_pdsch.dmrs_parms.N_DMRS_SLOT, sched_ctrl->num_total_bytes + oh, min_rbSize, max_rbSize, - &TBS, - &rbSize); - sched_pdsch->rbSize = rbSize; - sched_pdsch->rbStart = rbStart; - sched_pdsch->tb_size = TBS; - /* transmissions: directly allocate */ - n_rb_sched[beam.idx] -= sched_pdsch->rbSize; + &sched_pdsch.tb_size, + &sched_pdsch.rbSize); - for (int rb = bwp_start; rb < sched_pdsch->rbSize; rb++) - rballoc_mask[rb + sched_pdsch->rbStart] |= slbitmap; + sched_ctrl->sched_pdsch = sched_pdsch; + + /* transmissions: directly allocate */ + n_rb_sched[beam.idx] -= sched_pdsch.rbSize; + + for (int rb = bwp_start; rb < sched_pdsch.rbSize; rb++) + rballoc_mask[rb + sched_pdsch.rbStart] |= slbitmap; remainUEs[beam.idx]--; iterator++; From 8205503496d373d08de30714de6f156a31396ad7 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 22 Sep 2025 15:58:40 +0200 Subject: [PATCH 131/275] nr_preprocessor_phytest(): Refactor NR_sched_pdsch usage As in parent commit(s). --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 72 +++++++++---------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 5ec7bf781d..10b38272e8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -86,9 +86,6 @@ void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot) if(!tda_info.valid_tda) return; - sched_ctrl->sched_pdsch.tda_info = tda_info; - sched_ctrl->sched_pdsch.time_domain_allocation = tda; - /* find largest unallocated chunk */ const int bwpSize = dl_bwp->BWPSize; const int BWPStart = dl_bwp->BWPStart; @@ -142,12 +139,11 @@ void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot) 0); AssertFatal(CCEIndex >= 0, "Could not find CCE for UE %04x\n", UE->rnti); - NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; - if (sched_pdsch->dl_harq_pid == -1) - sched_pdsch->dl_harq_pid = sched_ctrl->available_dl_harq.head; - int alloc = -1; - if (!get_FeedbackDisabled(UE->sc_info.downlinkHARQ_FeedbackDisabled_r17, sched_pdsch->dl_harq_pid)) { + int harq_pid = sched_ctrl->retrans_dl_harq.head; + if (harq_pid < 0) + harq_pid = sched_ctrl->available_dl_harq.head; + if (!get_FeedbackDisabled(UE->sc_info.downlinkHARQ_FeedbackDisabled_r17, harq_pid)) { int r_pucch = nr_get_pucch_resource(sched_ctrl->coreset, UE->current_UL_BWP.pucch_Config, CCEIndex); alloc = nr_acknack_scheduling(mac, UE, frame, slot, 0, r_pucch, 0); if (alloc < 0) { @@ -165,42 +161,38 @@ void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot) sched_ctrl->aggregation_level, beam); - //AssertFatal(alloc, - // "could not find uplink slot for PUCCH (RNTI %04x@%d.%d)!\n", - // rnti, frame, slot); - - sched_pdsch->pucch_allocation = alloc; - sched_pdsch->rbStart = rbStart; - sched_pdsch->rbSize = rbSize; - sched_pdsch->bwp_info = get_pdsch_bwp_start_size(mac, UE); - sched_pdsch->dmrs_parms = get_dl_dmrs_params(scc, - dl_bwp, - &tda_info, - target_dl_Nl); - - sched_pdsch->mcs = target_dl_mcs; - sched_pdsch->nrOfLayers = target_dl_Nl; - sched_pdsch->Qm = nr_get_Qm_dl(sched_pdsch->mcs, dl_bwp->mcsTableIdx); - sched_pdsch->R = nr_get_code_rate_dl(sched_pdsch->mcs, dl_bwp->mcsTableIdx); + NR_sched_pdsch_t sched_pdsch = { + .rbSize = rbSize, + .rbStart = rbStart, + .mcs = target_dl_mcs, + .R = nr_get_code_rate_dl(target_dl_mcs, dl_bwp->mcsTableIdx), + .Qm = nr_get_Qm_dl(target_dl_mcs, dl_bwp->mcsTableIdx), + // tbSize further below + .dl_harq_pid = sched_ctrl->retrans_dl_harq.head, // PID of HARQ awaiting retransmission, or -1 otherwise + .pucch_allocation = alloc, + .pm_index = 0, + .nrOfLayers = target_dl_Nl, + .bwp_info = get_pdsch_bwp_start_size(mac, UE), + .dmrs_parms = get_dl_dmrs_params(scc, dl_bwp, &tda_info, target_dl_Nl), + .time_domain_allocation = tda, + .tda_info = tda_info, + }; sched_ctrl->dl_bler_stats.mcs = target_dl_mcs; /* for logging output */ - sched_pdsch->tb_size = nr_compute_tbs(sched_pdsch->Qm, - sched_pdsch->R, - sched_pdsch->rbSize, - tda_info.nrOfSymbols, - sched_pdsch->dmrs_parms.N_PRB_DMRS * sched_pdsch->dmrs_parms.N_DMRS_SLOT, - 0 /* N_PRB_oh, 0 for initialBWP */, - 0 /* tb_scaling */, - sched_pdsch->nrOfLayers) - >> 3; + sched_pdsch.tb_size = nr_compute_tbs(sched_pdsch.Qm, + sched_pdsch.R, + sched_pdsch.rbSize, + tda_info.nrOfSymbols, + sched_pdsch.dmrs_parms.N_PRB_DMRS * sched_pdsch.dmrs_parms.N_DMRS_SLOT, + 0 /* N_PRB_oh, 0 for initialBWP */, + 0 /* tb_scaling */, + target_dl_Nl) + >> 3; - /* get the PID of a HARQ process awaiting retransmission, or -1 otherwise */ - sched_pdsch->dl_harq_pid = sched_ctrl->retrans_dl_harq.head; + sched_ctrl->sched_pdsch = sched_pdsch; /* mark the corresponding RBs as used */ - for (int rb = 0; rb < sched_pdsch->rbSize; rb++) - vrb_map[rb + sched_pdsch->rbStart + BWPStart] = SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols); - - if ((frame&127) == 0) LOG_D(MAC,"phytest: %d.%d DL mcs %d, DL rbStart %d, DL rbSize %d\n", frame, slot, sched_pdsch->mcs, rbStart,rbSize); + for (int rb = 0; rb < sched_pdsch.rbSize; rb++) + vrb_map[rb + sched_pdsch.rbStart + BWPStart] = SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols); } uint32_t target_ul_mcs = 9; From 1900edb56d6f3b8c0b88c8947cbfd0c2268e5c6f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 22 Sep 2025 16:33:29 +0200 Subject: [PATCH 132/275] schedule_control_sib1(): Refactor NR_sched_pdsch usage Similarly to parent commit(s), refactor the use of NR_sched_pdsch. Instead of using nr_mac->sched_ctrlCommon, put the NR_sched_pdsch_t on the stack. This also refactors update_rb_mcs_tbs() to update the existing NR_sched_pdsch instead of updating individual fields. This also reduces the extend to which sched_ctrlCommon is used. --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c | 120 ++++++++---------- 1 file changed, 53 insertions(+), 67 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index 540e2c6291..1f6fb64181 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -274,35 +274,32 @@ void schedule_nr_mib(module_id_t module_idP, frame_t frameP, slot_t slotP, nfapi } } -static uint32_t get_tbs_bch(NR_sched_pdsch_t *pdsch, - uint32_t num_total_bytes, - uint16_t *vrb_map) +static void update_rb_mcs_tbs(NR_sched_pdsch_t *pdsch, uint32_t num_total_bytes, uint16_t *vrb_map) { - NR_tda_info_t *tda_info = &pdsch->tda_info; + const NR_tda_info_t *tda_info = &pdsch->tda_info; // Calculate number of PRB_DMRS uint8_t N_PRB_DMRS = pdsch->dmrs_parms.N_PRB_DMRS; LOG_D(MAC, "dlDmrsSymbPos %x\n", pdsch->dmrs_parms.dl_dmrs_symb_pos); int mcsTableIdx = 0; - uint32_t TBS = 0; const uint16_t slbitmap = SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols); int bwpSize = pdsch->bwp_info.bwpSize; int bwpStart = pdsch->bwp_info.bwpStart; int rbStop = bwpSize - 1; - int rbStart = 0; - uint16_t rbSize = 0; - while (rbStart < rbStop) { - if (vrb_map[rbStart + bwpStart] & slbitmap) - rbStart++; + while (pdsch->rbStart < rbStop) { + if (vrb_map[pdsch->rbStart + bwpStart] & slbitmap) + pdsch->rbStart++; else { int max_rbSize = 0; - while (rbStart + max_rbSize <= rbStop && !(vrb_map[rbStart + max_rbSize + bwpStart] & slbitmap)) + while (pdsch->rbStart + max_rbSize <= rbStop && !(vrb_map[pdsch->rbStart + max_rbSize + bwpStart] & slbitmap)) max_rbSize++; bool res = false; while (res == false && pdsch->mcs < 10) { - res = nr_find_nb_rb(nr_get_Qm_dl(pdsch->mcs, mcsTableIdx), - nr_get_code_rate_dl(pdsch->mcs, mcsTableIdx), + pdsch->Qm = nr_get_Qm_dl(pdsch->mcs, mcsTableIdx); + pdsch->R = nr_get_code_rate_dl(pdsch->mcs, mcsTableIdx); + res = nr_find_nb_rb(pdsch->Qm, + pdsch->R, 1, // no transform precoding for DL 1, // single layer tda_info->nrOfSymbols, @@ -310,59 +307,56 @@ static uint32_t get_tbs_bch(NR_sched_pdsch_t *pdsch, num_total_bytes, 1, // min_rbSize max_rbSize, - &TBS, - &rbSize); + &pdsch->tb_size, + &pdsch->rbSize); if (!res) pdsch->mcs++; } break; } } - AssertFatal(TBS >= num_total_bytes, + AssertFatal(pdsch->tb_size >= num_total_bytes, "Couldn't allocate enough resources for %d bytes in SIB PDSCH (rbStart %d, rbSize %d, bwpSize %d)\n", num_total_bytes, - rbStart, - rbSize, + pdsch->rbStart, + pdsch->rbSize, bwpSize); - pdsch->rbSize = rbSize; - pdsch->rbStart = rbStart; - LOG_D(NR_MAC, "mcs=%i, startSymbolIndex = %i, nrOfSymbols = %i, rbSize = %i, TBS = %i, dmrs_length %d, N_PRB_DMRS = %d, mappingtype = %d\n", pdsch->mcs, tda_info->startSymbolIndex, tda_info->nrOfSymbols, - rbSize, - TBS, + pdsch->rbSize, + pdsch->tb_size, pdsch->dmrs_parms.N_DMRS_SLOT, N_PRB_DMRS, tda_info->mapping_type); - return TBS; } -static uint32_t schedule_control_sib1(gNB_MAC_INST *gNB_mac, - int CC_id, - NR_sched_pdcch_t *pdcch, - NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config, - int time_domain_allocation, - NR_pdsch_dmrs_t *dmrs_parms, - NR_tda_info_t *tda_info, - uint8_t candidate_idx, - int beam, - uint16_t num_total_bytes) +static NR_sched_pdsch_t schedule_control_sib1(gNB_MAC_INST *gNB_mac, + int CC_id, + NR_sched_pdcch_t *pdcch, + NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config, + int time_domain_allocation, + NR_pdsch_dmrs_t *dmrs_parms, + NR_tda_info_t *tda_info, + uint8_t candidate_idx, + int beam, + uint16_t num_total_bytes) { AssertFatal(gNB_mac->sched_ctrlCommon, "sched_ctrlCommon is NULL\n"); NR_COMMON_channels_t *cc = &gNB_mac->common_channels[CC_id]; uint16_t *vrb_map = cc->vrb_map[beam]; - NR_sched_pdsch_t *pdsch = &gNB_mac->sched_ctrlCommon->sched_pdsch; - pdsch->bwp_info = get_pdsch_bwp_start_size(gNB_mac, NULL); - pdsch->time_domain_allocation = time_domain_allocation; - pdsch->dmrs_parms = *dmrs_parms; - pdsch->tda_info = *tda_info; - pdsch->nrOfLayers = 1; - pdsch->pm_index = 0; - pdsch->mcs = 0; // starting from mcs 0 + NR_sched_pdsch_t pdsch = { + .bwp_info = get_pdsch_bwp_start_size(gNB_mac, NULL), + .time_domain_allocation = time_domain_allocation, + .dmrs_parms = *dmrs_parms, + .tda_info = *tda_info, + .nrOfLayers = 1, + .pm_index = 0, + .mcs = 0, // starting from mcs 0 + }; gNB_mac->sched_ctrlCommon->num_total_bytes = num_total_bytes; uint8_t nr_of_candidates = 0; @@ -383,12 +377,7 @@ static uint32_t schedule_control_sib1(gNB_MAC_INST *gNB_mac, AssertFatal(gNB_mac->sched_ctrlCommon->cce_index >= 0, "Could not find CCE for coreset0\n"); - uint32_t TBS = get_tbs_bch(&gNB_mac->sched_ctrlCommon->sched_pdsch, - gNB_mac->sched_ctrlCommon->num_total_bytes, - vrb_map); - - pdsch->R = nr_get_code_rate_dl(pdsch->mcs, 0); - pdsch->Qm = nr_get_Qm_dl(pdsch->mcs, 0); + update_rb_mcs_tbs(&pdsch, gNB_mac->sched_ctrlCommon->num_total_bytes, vrb_map); // Mark the corresponding RBs as used fill_pdcch_vrb_map(gNB_mac, @@ -397,10 +386,10 @@ static uint32_t schedule_control_sib1(gNB_MAC_INST *gNB_mac, gNB_mac->sched_ctrlCommon->cce_index, gNB_mac->sched_ctrlCommon->aggregation_level, beam); - for (int rb = 0; rb < pdsch->rbSize; rb++) { + for (int rb = 0; rb < pdsch.rbSize; rb++) { vrb_map[rb + type0_PDCCH_CSS_config->cset_start_rb] |= SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols); } - return TBS; + return pdsch; } static void nr_fill_nfapi_dl_SIB_pdu(gNB_MAC_INST *gNB_mac, @@ -578,22 +567,21 @@ void schedule_nr_sib1(module_id_t module_idP, NR_COMMON_channels_t *cc = &gNB_mac->common_channels[0]; // Configure sched_ctrlCommon for SIB1 - uint32_t TBS = schedule_control_sib1(gNB_mac, - CC_id, - &sched_pdcch, - type0_PDCCH_CSS_config, - time_domain_allocation, - &dmrs_parms, - &tda_info, - candidate_idx, - beam.idx, - cc->sib1_bcch_length); + NR_sched_pdsch_t sched_pdsch = schedule_control_sib1(gNB_mac, + CC_id, + &sched_pdcch, + type0_PDCCH_CSS_config, + time_domain_allocation, + &dmrs_parms, + &tda_info, + candidate_idx, + beam.idx, + cc->sib1_bcch_length); - gNB_mac->sched_ctrlCommon->sched_pdsch.tb_size = TBS; nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body; int pdu_index = gNB_mac->pdu_index[0]++; nr_fill_nfapi_dl_SIB_pdu(gNB_mac, - &gNB_mac->sched_ctrlCommon->sched_pdsch, + &sched_pdsch, &sched_pdcch, gNB_mac->sched_ctrlCommon->search_space, gNB_mac->sched_ctrlCommon->coreset, @@ -609,11 +597,11 @@ void schedule_nr_sib1(module_id_t module_idP, nfapi_nr_pdu_t *tx_req = &TX_req->pdu_list[ntx_req]; // Data to be transmitted - memcpy(tx_req->TLVs[0].value.direct, cc->sib1_bcch_pdu, TBS); + memcpy(tx_req->TLVs[0].value.direct, cc->sib1_bcch_pdu, sched_pdsch.tb_size); tx_req->PDU_index = pdu_index; tx_req->num_TLV = 1; - tx_req->TLVs[0].length = TBS; + tx_req->TLVs[0].length = sched_pdsch.tb_size; tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, tx_req->TLVs[0].length); TX_req->Number_of_PDUs++; TX_req->SFN = frameP; @@ -718,9 +706,7 @@ static void other_sib_sched_control(module_id_t module_idP, uint16_t *vrb_map = cc->vrb_map[beam.idx]; uint8_t *sib_bcch_pdu = cc->other_sib_bcch_pdu[payload_idx]; int num_total_bytes = cc->other_sib_bcch_length[payload_idx]; - sched_pdsch_otherSI.tb_size = get_tbs_bch(&sched_pdsch_otherSI, num_total_bytes, vrb_map); - sched_pdsch_otherSI.R = nr_get_code_rate_dl(sched_pdsch_otherSI.mcs, 0); - sched_pdsch_otherSI.Qm = nr_get_Qm_dl(sched_pdsch_otherSI.mcs, 0); + update_rb_mcs_tbs(&sched_pdsch_otherSI, num_total_bytes, vrb_map); for (int rb = 0; rb < sched_pdsch_otherSI.rbSize; rb++) { vrb_map[rb + type0_PDCCH_CSS_config->cset_start_rb] |= SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols); From 4eb43f2c45c3500e7b009d9f9dcf2416412fbcac Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 1 Oct 2025 22:02:33 +0200 Subject: [PATCH 133/275] nr_dlsim_preprocessor(): Refactor NR_sched_pdsch usage Similarly to parent commit(s), assign sched_pdsch in a single place instead of relying on sched_ctrl. In a follow-up commit, the post-processor will be called in a single place. --- openair1/SIMULATION/NR_PHY/dlsim.c | 55 ++++++++++++++++-------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 6329d5849f..3a4df8d2fc 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -213,46 +213,47 @@ void nr_dlsim_preprocessor(module_id_t module_id, frame_t frame, slot_t slot) AssertFatal(CCEIndex>=0, "%4d.%2d could not find CCE for DL DCI UE %d/RNTI %04x\n", frame, slot, 0, UE_info->rnti); sched_ctrl->cce_index = CCEIndex; - NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; - sched_pdsch->rbStart = g_rbStart; - sched_pdsch->rbSize = g_rbSize; - sched_pdsch->bwp_info = get_pdsch_bwp_start_size(RC.nrmac[module_id], UE_info); - sched_pdsch->mcs = g_mcsIndex; - sched_pdsch->nrOfLayers = g_nrOfLayers; - sched_pdsch->pm_index = g_pmi; + NR_sched_pdsch_t sched_pdsch = { + .rbStart = g_rbStart, + .rbSize = g_rbSize, + .bwp_info = get_pdsch_bwp_start_size(RC.nrmac[module_id], UE_info), + .mcs = g_mcsIndex, + .nrOfLayers = g_nrOfLayers, + .pm_index = g_pmi, + }; /* the following might override the table that is mandated by RRC * configuration */ current_BWP->mcsTableIdx = g_mcsTableIdx; - sched_pdsch->time_domain_allocation = get_dl_tda(RC.nrmac[module_id], slot); - AssertFatal(sched_pdsch->time_domain_allocation >= 0,"Unable to find PDSCH time domain allocation in list\n"); + sched_pdsch.time_domain_allocation = get_dl_tda(RC.nrmac[module_id], slot); + AssertFatal(sched_pdsch.time_domain_allocation >= 0,"Unable to find PDSCH time domain allocation in list\n"); - sched_pdsch->tda_info = get_dl_tda_info(current_BWP, + sched_pdsch.tda_info = get_dl_tda_info(current_BWP, sched_ctrl->search_space->searchSpaceType->present, - sched_pdsch->time_domain_allocation, + sched_pdsch.time_domain_allocation, NR_MIB__dmrs_TypeA_Position_pos2, 1, TYPE_C_RNTI_, sched_ctrl->coreset->controlResourceSetId, false); - sched_pdsch->dmrs_parms = get_dl_dmrs_params(scc, + sched_pdsch.dmrs_parms = get_dl_dmrs_params(scc, current_BWP, - &sched_pdsch->tda_info, - sched_pdsch->nrOfLayers); + &sched_pdsch.tda_info, + sched_pdsch.nrOfLayers); - sched_pdsch->Qm = nr_get_Qm_dl(sched_pdsch->mcs, current_BWP->mcsTableIdx); - sched_pdsch->R = nr_get_code_rate_dl(sched_pdsch->mcs, current_BWP->mcsTableIdx); - sched_pdsch->tb_size = nr_compute_tbs(sched_pdsch->Qm, - sched_pdsch->R, - sched_pdsch->rbSize, - sched_pdsch->tda_info.nrOfSymbols, - sched_pdsch->dmrs_parms.N_PRB_DMRS * sched_pdsch->dmrs_parms.N_DMRS_SLOT, + sched_pdsch.Qm = nr_get_Qm_dl(sched_pdsch.mcs, current_BWP->mcsTableIdx); + sched_pdsch.R = nr_get_code_rate_dl(sched_pdsch.mcs, current_BWP->mcsTableIdx); + sched_pdsch.tb_size = nr_compute_tbs(sched_pdsch.Qm, + sched_pdsch.R, + sched_pdsch.rbSize, + sched_pdsch.tda_info.nrOfSymbols, + sched_pdsch.dmrs_parms.N_PRB_DMRS * sched_pdsch.dmrs_parms.N_DMRS_SLOT, 0 /* N_PRB_oh, 0 for initialBWP */, 0 /* tb_scaling */, - sched_pdsch->nrOfLayers) >> 3; + sched_pdsch.nrOfLayers) >> 3; /* the simulator assumes the HARQ PID is equal to the slot number */ - sched_pdsch->dl_harq_pid = slot; + sched_pdsch.dl_harq_pid = slot; /* The scheduler uses lists to track whether a HARQ process is * free/busy/awaiting retransmission, and updates the HARQ process states. @@ -266,10 +267,12 @@ void nr_dlsim_preprocessor(module_id_t module_id, frame_t frame, slot_t slot) else add_front_nr_list(&sched_ctrl->retrans_dl_harq, slot); // ... make PID retransmission sched_ctrl->harq_processes[slot].is_waiting = false; - AssertFatal(sched_pdsch->rbStart >= 0, "invalid rbStart %d\n", sched_pdsch->rbStart); - AssertFatal(sched_pdsch->rbSize > 0, "invalid rbSize %d\n", sched_pdsch->rbSize); - AssertFatal(sched_pdsch->mcs >= 0, "invalid mcs %d\n", sched_pdsch->mcs); + AssertFatal(sched_pdsch.rbStart >= 0, "invalid rbStart %d\n", sched_pdsch.rbStart); + AssertFatal(sched_pdsch.rbSize > 0, "invalid rbSize %d\n", sched_pdsch.rbSize); + AssertFatal(sched_pdsch.mcs >= 0, "invalid mcs %d\n", sched_pdsch.mcs); AssertFatal(current_BWP->mcsTableIdx >= 0 && current_BWP->mcsTableIdx <= 2, "invalid mcsTableIdx %d\n", current_BWP->mcsTableIdx); + + sched_ctrl->sched_pdsch = sched_pdsch; } nrUE_params_t nrUE_params; From 924f28ed70492919ee7a3fc1df4c4eb3a0917879 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 21 Aug 2025 19:21:33 +0200 Subject: [PATCH 134/275] Refactor to call post-processor in place Call the post-processor when the allocation is "fixed" (i.e., nothing changes anymore), instead of having a final loop across all UEs that might potentially be costly. In order to calculate the PF metric, note that the statistics "reset" is moved into pf_dl() instead of the post-processing loop, as it has to be done on each slot. See also: dd9803020227 ("Refactor to call post-processor in place") --- openair1/SIMULATION/NR_PHY/dlsim.c | 35 +++++---- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 73 ++++++++----------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 8 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 3 - openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 3 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 4 +- 6 files changed, 58 insertions(+), 68 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 3a4df8d2fc..9b3d4ac21b 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -182,13 +182,13 @@ extern void fix_scd(NR_ServingCellConfig_t *scd);// forward declaration /* specific dlsim DL preprocessor: uses rbStart/rbSize/mcs/nrOfLayers from command line of dlsim */ int g_mcsIndex = -1, g_mcsTableIdx = 0, g_rbStart = -1, g_rbSize = -1, g_nrOfLayers = 1, g_pmi = 0; -void nr_dlsim_preprocessor(module_id_t module_id, frame_t frame, slot_t slot) +void nr_dlsim_preprocessor(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pp_pdsch) { - NR_UE_info_t *UE_info = RC.nrmac[module_id]->UE_info.connected_ue_list[0]; - AssertFatal(RC.nrmac[module_id]->UE_info.connected_ue_list[1] == NULL, "Only single UE allowed in dlsim\n"); + NR_UE_info_t *UE_info = nr_mac->UE_info.connected_ue_list[0]; + AssertFatal(nr_mac->UE_info.connected_ue_list[1] == NULL, "Only single UE allowed in dlsim\n"); NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl; NR_UE_DL_BWP_t *current_BWP = &UE_info->current_DL_BWP; - NR_ServingCellConfigCommon_t *scc = RC.nrmac[0]->common_channels[0].ServingCellConfigCommon; + NR_ServingCellConfigCommon_t *scc = nr_mac->common_channels[0].ServingCellConfigCommon; uint8_t nr_of_candidates = 0; if (g_mcsIndex < 4) { @@ -201,8 +201,8 @@ void nr_dlsim_preprocessor(module_id_t module_id, frame_t frame, slot_t slot) &nr_of_candidates, sched_ctrl->search_space,4); } - uint32_t Y = get_Y(sched_ctrl->search_space, slot, UE_info->rnti); - int CCEIndex = find_pdcch_candidate(RC.nrmac[module_id], + uint32_t Y = get_Y(sched_ctrl->search_space, pp_pdsch->slot, UE_info->rnti); + int CCEIndex = find_pdcch_candidate(nr_mac, /* CC_id = */ 0, sched_ctrl->aggregation_level, nr_of_candidates, @@ -210,13 +210,18 @@ void nr_dlsim_preprocessor(module_id_t module_id, frame_t frame, slot_t slot) &sched_ctrl->sched_pdcch, sched_ctrl->coreset, Y); - AssertFatal(CCEIndex>=0, "%4d.%2d could not find CCE for DL DCI UE %d/RNTI %04x\n", frame, slot, 0, UE_info->rnti); + AssertFatal(CCEIndex >= 0, + "%4d.%2d could not find CCE for DL DCI UE %d/RNTI %04x\n", + pp_pdsch->frame, + pp_pdsch->slot, + 0, + UE_info->rnti); sched_ctrl->cce_index = CCEIndex; NR_sched_pdsch_t sched_pdsch = { .rbStart = g_rbStart, .rbSize = g_rbSize, - .bwp_info = get_pdsch_bwp_start_size(RC.nrmac[module_id], UE_info), + .bwp_info = get_pdsch_bwp_start_size(nr_mac, UE_info), .mcs = g_mcsIndex, .nrOfLayers = g_nrOfLayers, .pm_index = g_pmi, @@ -224,7 +229,7 @@ void nr_dlsim_preprocessor(module_id_t module_id, frame_t frame, slot_t slot) /* the following might override the table that is mandated by RRC * configuration */ current_BWP->mcsTableIdx = g_mcsTableIdx; - sched_pdsch.time_domain_allocation = get_dl_tda(RC.nrmac[module_id], slot); + sched_pdsch.time_domain_allocation = get_dl_tda(nr_mac, pp_pdsch->slot); AssertFatal(sched_pdsch.time_domain_allocation >= 0,"Unable to find PDSCH time domain allocation in list\n"); sched_pdsch.tda_info = get_dl_tda_info(current_BWP, @@ -253,7 +258,7 @@ void nr_dlsim_preprocessor(module_id_t module_id, frame_t frame, slot_t slot) sched_pdsch.nrOfLayers) >> 3; /* the simulator assumes the HARQ PID is equal to the slot number */ - sched_pdsch.dl_harq_pid = slot; + sched_pdsch.dl_harq_pid = pp_pdsch->slot; /* The scheduler uses lists to track whether a HARQ process is * free/busy/awaiting retransmission, and updates the HARQ process states. @@ -262,17 +267,17 @@ void nr_dlsim_preprocessor(module_id_t module_id, frame_t frame, slot_t slot) * Therefore, below lines just "repair" everything so that the scheduler * won't remark that there is no HARQ feedback */ sched_ctrl->feedback_dl_harq.head = -1; // always overwrite feedback HARQ process - if (sched_ctrl->harq_processes[slot].round == 0) // depending on round set in simulation ... - add_front_nr_list(&sched_ctrl->available_dl_harq, slot); // ... make PID available + if (sched_ctrl->harq_processes[pp_pdsch->slot].round == 0) // depending on round set in simulation ... + add_front_nr_list(&sched_ctrl->available_dl_harq, pp_pdsch->slot); // ... make PID available else - add_front_nr_list(&sched_ctrl->retrans_dl_harq, slot); // ... make PID retransmission - sched_ctrl->harq_processes[slot].is_waiting = false; + add_front_nr_list(&sched_ctrl->retrans_dl_harq, pp_pdsch->slot); // ... make PID retransmission + sched_ctrl->harq_processes[pp_pdsch->slot].is_waiting = false; AssertFatal(sched_pdsch.rbStart >= 0, "invalid rbStart %d\n", sched_pdsch.rbStart); AssertFatal(sched_pdsch.rbSize > 0, "invalid rbSize %d\n", sched_pdsch.rbSize); AssertFatal(sched_pdsch.mcs >= 0, "invalid mcs %d\n", sched_pdsch.mcs); AssertFatal(current_BWP->mcsTableIdx >= 0 && current_BWP->mcsTableIdx <= 2, "invalid mcsTableIdx %d\n", current_BWP->mcsTableIdx); - sched_ctrl->sched_pdsch = sched_pdsch; + post_process_dlsch(nr_mac, pp_pdsch, UE_info, &sched_pdsch); } nrUE_params_t nrUE_params; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 4a39969be4..aa184d8f85 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -422,17 +422,17 @@ bwp_info_t get_pdsch_bwp_start_size(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE) return bwp_info; } -static bool allocate_dl_retransmission(module_id_t module_id, - frame_t frame, - slot_t slot, +static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac, + post_process_pdsch_t *pp_pdsch, int *n_rb_sched, NR_UE_info_t *UE, int beam_idx, int current_harq_pid) { - + frame_t frame = pp_pdsch->frame; + slot_t slot = pp_pdsch->slot; int CC_id = 0; - gNB_MAC_INST *nr_mac = RC.nrmac[module_id]; + const NR_ServingCellConfigCommon_t *scc = nr_mac->common_channels->ServingCellConfigCommon; NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP; @@ -578,7 +578,8 @@ static bool allocate_dl_retransmission(module_id_t module_id, new_sched.rbStart = rbStart - bwp_info.bwpStart; new_sched.pucch_allocation = alloc; new_sched.bwp_info = bwp_info; - sched_ctrl->sched_pdsch = new_sched; + + post_process_dlsch(nr_mac, pp_pdsch, UE, &new_sched); /* retransmissions: directly allocate */ *n_rb_sched -= new_sched.rbSize; @@ -606,15 +607,16 @@ static int comparator(const void *p, const void *q) return 0; } -static void pf_dl(module_id_t module_id, - frame_t frame, - slot_t slot, +static void pf_dl(gNB_MAC_INST *mac, + post_process_pdsch_t *pp_pdsch, NR_UE_info_t **UE_list, int max_num_ue, int num_beams, int n_rb_sched[num_beams]) { - gNB_MAC_INST *mac = RC.nrmac[module_id]; + frame_t frame = pp_pdsch->frame; + slot_t slot = pp_pdsch->slot; + NR_ServingCellConfigCommon_t *scc=mac->common_channels[0].ServingCellConfigCommon; // UEs that could be scheduled UEsched_t UE_sched[MAX_MOBILES_PER_GNB + 1] = {0}; @@ -633,15 +635,18 @@ static void pf_dl(module_id_t module_id, if (!nr_mac_ue_is_active(UE)) continue; - const NR_mac_dir_stats_t *stats = &UE->mac_stats.dl; + NR_mac_dir_stats_t *stats = &UE->mac_stats.dl; /* get the PID of a HARQ process awaiting retrnasmission, or -1 otherwise */ int harq_pid = sched_ctrl->retrans_dl_harq.head; /* Calculate Throughput */ const float a = 0.01f; - const uint32_t b = UE->mac_stats.dl.current_bytes; + const uint32_t b = stats->current_bytes; UE->dl_thr_ue = (1 - a) * UE->dl_thr_ue + a * b; + stats->current_bytes = 0; + stats->current_rbs = 0; + /* Check if this UE should get TA (every 100 frames). If we add the CE, * ta_apply will be reset */ if (frame == ((sched_ctrl->ta_frame + 100) % 1024)) @@ -659,7 +664,7 @@ static void pf_dl(module_id_t module_id, bool sch_ret = beam.idx >= 0; /* Allocate retransmission */ if (sch_ret) - sch_ret = allocate_dl_retransmission(module_id, frame, slot, &n_rb_sched[beam.idx], UE, beam.idx, harq_pid); + sch_ret = allocate_dl_retransmission(mac, pp_pdsch, &n_rb_sched[beam.idx], UE, beam.idx, harq_pid); if (!sch_ret) { LOG_D(NR_MAC, "[UE %04x][%4d.%2d] DL retransmission could not be allocated\n", UE->rnti, frame, slot); reset_beam_status(&mac->beam_info, frame, slot, UE->UE_beam_index, slots_per_frame, beam.new_beam); @@ -876,7 +881,7 @@ static void pf_dl(module_id_t module_id, &sched_pdsch.tb_size, &sched_pdsch.rbSize); - sched_ctrl->sched_pdsch = sched_pdsch; + post_process_dlsch(mac, pp_pdsch, iterator->UE, &sched_pdsch); /* transmissions: directly allocate */ n_rb_sched[beam.idx] -= sched_pdsch.rbSize; @@ -889,9 +894,9 @@ static void pf_dl(module_id_t module_id, } } -static void nr_dlsch_preprocessor(module_id_t module_id, frame_t frame, slot_t slot) +static void nr_dlsch_preprocessor(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch) { - gNB_MAC_INST *mac = RC.nrmac[module_id]; + module_id_t module_id = 0; NR_UEs_t *UE_info = &mac->UE_info; if (UE_info->connected_ue_list[0] == NULL) @@ -905,7 +910,7 @@ static void nr_dlsch_preprocessor(module_id_t module_id, frame_t frame, slot_t s n_rb_sched[i] = bw; /* Retrieve amount of data to send for this UE */ - nr_store_dlsch_buffer(module_id, frame, slot); + nr_store_dlsch_buffer(module_id, pp_pdsch->frame, pp_pdsch->slot); int average_agg_level = 4; // TODO find a better estimation int max_sched_ues = bw / (average_agg_level * NR_NB_REG_PER_CCE); @@ -914,7 +919,7 @@ static void nr_dlsch_preprocessor(module_id_t module_id, frame_t frame, slot_t s max_sched_ues = min(max_sched_ues, MAX_DCI_CORESET); /* proportional fair scheduling algorithm */ - pf_dl(module_id, frame, slot, UE_info->connected_ue_list, max_sched_ues, num_beams, n_rb_sched); + pf_dl(mac, pp_pdsch, UE_info->connected_ue_list, max_sched_ues, num_beams, n_rb_sched); } nr_pp_impl_dl nr_init_dlsch_preprocessor(int CC_id) @@ -1003,7 +1008,7 @@ nfapi_nr_dl_tti_pdsch_pdu_rel15_t *prepare_pdsch_pdu(nfapi_nr_dl_tti_request_pdu void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE_info_t *UE, NR_sched_pdsch_t *sched_pdsch) { int CC_id = 0; - int module_id = 0; + int module_id = nr_mac->Mod_id; frame_t frame = pdsch->frame; slot_t slot = pdsch->slot; @@ -1371,6 +1376,8 @@ void nr_schedule_ue_spec(module_id_t module_id, nfapi_nr_tx_data_request_t *TX_req) { gNB_MAC_INST *gNB_mac = RC.nrmac[module_id]; + int CC_id = 0; + /* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */ AssertFatal(pthread_mutex_trylock(&gNB_mac->sched_lock) == EBUSY, "this function should be called with the scheduler mutex locked\n"); @@ -1378,34 +1385,14 @@ void nr_schedule_ue_spec(module_id_t module_id, if (!is_dl_slot(slot, &gNB_mac->frame_structure)) return; - /* PREPROCESSOR */ - gNB_mac->pre_processor_dl(module_id, frame, slot); - const int CC_id = 0; NR_ServingCellConfigCommon_t *scc = gNB_mac->common_channels[CC_id].ServingCellConfigCommon; - NR_UEs_t *UE_info = &gNB_mac->UE_info; - nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body; - - post_process_pdsch_t pdsch = { frame, slot, dl_req, TX_req }; - const NR_BWP_t *initialDL = &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; gNB_mac->mac_stats.total_prb_aggregate += NRRIV2BW(initialDL->locationAndBandwidth, MAX_BWP_SIZE); - UE_iterator(UE_info->connected_ue_list, UE) { - NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; + nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body; + post_process_pdsch_t pdsch = { frame, slot, dl_req, TX_req }; - if (!nr_mac_ue_is_active(UE) && !get_softmodem_params()->phy_test) - continue; + /* PREPROCESSOR */ + gNB_mac->pre_processor_dl(gNB_mac, &pdsch); - NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; - UE->mac_stats.dl.current_bytes = 0; - UE->mac_stats.dl.current_rbs = 0; - - if (sched_pdsch->rbSize <= 0) - continue; - - post_process_dlsch(gNB_mac, &pdsch, UE, sched_pdsch); - - /* mark UE as scheduled */ - sched_pdsch->rbSize = 0; - } } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index 10b38272e8..f9121b3125 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -55,9 +55,11 @@ uint32_t target_dl_bw = 50; uint64_t dlsch_slot_bitmap = (1<<1); /* schedules whole bandwidth for first user, all the time */ -void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot) +void nr_preprocessor_phytest(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch) { - gNB_MAC_INST *mac = RC.nrmac[module_id]; + frame_t frame = pp_pdsch->frame; + slot_t slot = pp_pdsch->slot; + /* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */ int slot_period = slot % mac->frame_structure.numb_slots_period; if (!is_xlsch_in_slot(dlsch_slot_bitmap, slot_period)) @@ -188,7 +190,7 @@ void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot) target_dl_Nl) >> 3; - sched_ctrl->sched_pdsch = sched_pdsch; + post_process_dlsch(mac, pp_pdsch, UE, &sched_pdsch); /* mark the corresponding RBs as used */ for (int rb = 0; rb < sched_pdsch.rbSize; rb++) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index d5cda66b5b..a81841d9c0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2974,9 +2974,6 @@ NR_UE_info_t *get_new_nr_ue_inst(uid_allocator_t *uia, rnti_t rnti, NR_CellGroup NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; sched_ctrl->ta_update = 31; - /* set illegal time domain allocation to force recomputation of all fields */ - sched_ctrl->sched_pdsch.time_domain_allocation = -1; - /* Set default BWPs */ AssertFatal(UE->sc_info.n_ul_bwp <= NR_MAX_NUM_BWP, "uplinkBWP_ToAddModList has %d BWP!\n", UE->sc_info.n_ul_bwp); diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index f04b28b8da..cfe02755b2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -145,7 +145,7 @@ uint16_t nr_mac_compute_RIV(uint16_t N_RB_DL, uint16_t RBstart, uint16_t Lcrbs); /* \brief preprocessor for phytest: schedules UE_id 0 with fixed MCS on all * freq resources */ -void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot); +void nr_preprocessor_phytest(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch); /* \brief UL preprocessor for phytest: schedules UE_id 0 with fixed MCS on a * fixed set of resources */ void nr_ul_preprocessor_phytest(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp_pusch); @@ -504,5 +504,6 @@ void prepare_du_configuration_update(gNB_MAC_INST *mac, void nr_mac_clean_cellgroup(NR_CellGroupConfig_t *cell_group); +void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE_info_t *UE, NR_sched_pdsch_t *sched_pdsch); void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE_info_t *UE, NR_sched_pusch_t *sched_pusch); #endif /*__LAYER2_NR_MAC_PROTO_H__*/ diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 544e218e5b..035ab6ad83 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -633,8 +633,6 @@ typedef struct { /// PHR info: nominal UE transmit power levels (dBm) int pcmax; - /// Sched PDSCH: scheduling decisions, copied into HARQ and cleared every TTI - NR_sched_pdsch_t sched_pdsch; /// UE-estimated maximum MCS (from CSI-RS) uint8_t dl_max_mcs; @@ -847,7 +845,7 @@ typedef struct { struct gNB_MAC_INST_s; typedef struct gNB_MAC_INST_s gNB_MAC_INST; -typedef void (*nr_pp_impl_dl)(module_id_t mod_id, frame_t frame, slot_t slot); +typedef void (*nr_pp_impl_dl)(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pp_pdsch); typedef void (*nr_pp_impl_ul)(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp_pusch); typedef struct f1_config_t { From 67ba4f67417a3c5abcc2e9b1c23223035918e767 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 25 Sep 2025 18:43:15 +0200 Subject: [PATCH 135/275] evaluate_cqi_report(): Add LOG_D for reported CQI --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index f3de970683..ed5f3183c7 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -680,6 +680,8 @@ static void evaluate_cqi_report(uint8_t *payload, const int cqi_idx = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb; const int mcs_table = UE->current_DL_BWP.mcsTableIdx; sched_ctrl->dl_max_mcs = get_mcs_from_cqi(mcs_table, cqi_Table, cqi_idx); + + LOG_D(MAC, "Reported CQI = %d, dl_max_mcs %d\n", temp_cqi, sched_ctrl->dl_max_mcs); } static uint8_t evaluate_pmi_report(uint8_t *payload, From 1292cd68b7d0fe830d0c78285ba6df7bee252189 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 29 Oct 2025 15:16:02 +0100 Subject: [PATCH 136/275] Remove the usage of global mem for rach rx process saves about 600MB of memory allocation Move the definition of PRACH items and the PRACH list to the defs_nr_commons.h file. Do not re-write the entire structure in nr_schedule_rx_prach(), as it is quite large now. --- executables/nr-gnb.c | 1 - executables/nr-ru.c | 3 +- openair1/PHY/INIT/nr_init_ru.c | 20 ----------- openair1/PHY/NR_TRANSPORT/nr_prach.c | 15 +++------ .../PHY/NR_TRANSPORT/nr_transport_proto.h | 1 - openair1/PHY/defs_RU.h | 2 -- openair1/PHY/defs_common.h | 33 ++----------------- openair1/PHY/defs_nr_common.h | 28 ++++++++++++++++ openair1/SIMULATION/NR_PHY/prachsim.c | 21 ++---------- radio/fhi_72/oaioran.c | 7 +++- radio/fhi_72/oran_isolate.c | 30 +++++++++-------- radio/fhi_72/oran_isolate.h | 5 +-- 12 files changed, 64 insertions(+), 102 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 0e4fba9812..b0509f158d 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -387,7 +387,6 @@ void init_eNB_afterRU(void) int aa = 0; for (int ru_id = 0; ru_id < gNB->num_RU; ru_id++) { AssertFatal(gNB->RU_list[ru_id]->common.rxdataF != NULL, "RU %d : common.rxdataF is NULL\n", gNB->RU_list[ru_id]->idx); - AssertFatal(gNB->RU_list[ru_id]->prach_rxsigF != NULL, "RU %d : prach_rxsigF is NULL\n", gNB->RU_list[ru_id]->idx); for (int i = 0; i < gNB->RU_list[ru_id]->nb_rx; aa++, i++) { LOG_I(PHY, "Attaching RU %d antenna %d to gNB antenna %d\n", gNB->RU_list[ru_id]->idx, i, aa); for (int b = 0; b < gNB->RU_list[ru_id]->num_beams_period; b++) { diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 9e4e6a5384..ea43ecdff1 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1216,8 +1216,7 @@ void *ru_thread(void *param) prach_item_t *p = find_nr_prach(&gNB->prach_list, proc->frame_rx, proc->tti_rx, SEARCH_EXIST); if (p) { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 1 ); - // this fills ru->prach_rxsigF for processing by gNB - p->rxsigF = (c16_t ***)ru->prach_rxsigF; + // need to extract RACH data for lqter processing by rx_nr_prach() rx_nr_prach_ru(p, ru->common.rxdata, ru->nr_frame_parms, ru->N_TA_offset); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 0); } // end if (prach_id >= 0) diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index 30ec333c70..71418bb1e3 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -112,25 +112,10 @@ void nr_phy_init_RU(RU_t *ru) LOG_D(PHY, "rxdataF[%d] %p for RU %d\n", i, ru->common.rxdataF[i], ru->idx); } - /* number of elements of an array X is computed as sizeof(X) / sizeof(X[0]) */ - // AssertFatal(ru->nb_rx <= sizeof(ru->prach_rxsigF) / sizeof(ru->prach_rxsigF[0]), - // "nb_antennas_rx too large"); - for (int j = 0; j < NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX; j++) { - ru->prach_rxsigF[j] = malloc(nb_rx_streams * sizeof(*ru->prach_rxsigF)); - - for (int i = 0; i < nb_rx_streams; i++) { - // largest size for PRACH FFT is 4x98304 (16*24576) - ru->prach_rxsigF[j][i] = malloc16_clear(4 * 98304 * 2 * sizeof(**ru->prach_rxsigF)); - LOG_D(PHY, "[INIT] prach_vars->rxsigF[%d] = %p\n", i, ru->prach_rxsigF[j][i]); - } - } - AssertFatal(ru->num_gNB <= NUMBER_OF_gNB_MAX, "gNB instances %d > %d\n", ru->num_gNB,NUMBER_OF_gNB_MAX); LOG_D(PHY, "[INIT] %s() ru->num_gNB:%d \n", __FUNCTION__, ru->num_gNB); } // !=IF5 - - init_prach_list(&ru->prach_list); } void nr_phy_free_RU(RU_t *ru) @@ -172,11 +157,6 @@ void nr_phy_free_RU(RU_t *ru) free_and_zero(ru->common.rxdataF[i]); free_and_zero(ru->common.rxdataF); - for (int j = 0; j < NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX; j++) { - for (int i = 0; i < nb_rx_streams; i++) - free_and_zero(ru->prach_rxsigF[j][i]); - free_and_zero(ru->prach_rxsigF[j]); - } for(int i = 0; i < ru->num_beams_period; ++i) free_and_zero(ru->common.beam_id[i]); free_and_zero(ru->common.beam_id); diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 101bd76463..0d6a4887ca 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -88,7 +88,10 @@ prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach } const int fmt = prach_pdu->prach_format; NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - *prach = (prach_item_t){.frame = SFN, .slot = Slot, .num_slots = (fmt < 4) ? get_long_prach_dur(fmt, fp->numerology_index) : 1}; + // 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; 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, @@ -123,16 +126,6 @@ prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach return prach; } -void nr_fill_prach_ru(RU_t *ru, prach_item_t *gnb_prach) -{ - prach_item_t *prach = find_nr_prach(&ru->prach_list, gnb_prach->frame, gnb_prach->slot, SEARCH_EXIST_OR_FREE); - if (!prach) { - LOG_E(PHY, "no prach memory found\n"); - return; - } - *prach = *gnb_prach; -} - static void rx_nr_prach_ru_internal(prach_item_t *p, int beam_id, int prachStartSymbol, diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index fd0a36536d..31a89e823b 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -211,7 +211,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); -void nr_fill_prach_ru(RU_t *ru,prach_item_t * ); prach_item_t *find_nr_prach(prach_list_t *, int frame, int slot, find_type_t type); void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, diff --git a/openair1/PHY/defs_RU.h b/openair1/PHY/defs_RU.h index b94d0b1c56..0e21f37fde 100644 --- a/openair1/PHY/defs_RU.h +++ b/openair1/PHY/defs_RU.h @@ -190,7 +190,6 @@ typedef struct { task_ans_t *ans; } feptx_cmd_t; -#define NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX 12 #define RU_RX_SLOT_DEPTH 4 typedef struct RU_proc_t_s { /// Pointer to associated RU descriptor @@ -592,7 +591,6 @@ typedef struct RU_t_s { int32_t *bw_list[NUMBER_OF_eNB_MAX+1]; /// beamforming weight vectors int32_t **beam_weights[NUMBER_OF_eNB_MAX+1][15]; - prach_list_t prach_list; /// received frequency-domain signal for PRACH (IF4p5 RRU) int16_t **prach_rxsigF[NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX]; /// received frequency-domain signal for PRACH BR (IF4p5 RRU) diff --git a/openair1/PHY/defs_common.h b/openair1/PHY/defs_common.h index efaccf2bf7..dbb07a9bbb 100644 --- a/openair1/PHY/defs_common.h +++ b/openair1/PHY/defs_common.h @@ -993,41 +993,14 @@ extern int sync_var; #define MBSFN_FDD_SF6 0x10 #define MBSFN_FDD_SF7 0x08 #define MBSFN_FDD_SF8 0x04 + +#define NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX 12 + typedef struct { pthread_mutex_t mutex_failure; bool failed; } decode_abort_t; -// TODO will be removed in a late commit -#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h" -#define NUMBER_OF_NR_PRACH_MAX 8 -typedef struct { - int frame; - int slot; - int num_slots; // prach duration in slots - int beams[NFAPI_MAX_NUM_BG_IF]; - nfapi_nr_prach_pdu_t pdu; - int rootSequenceIndex; - int numrootSequenceIndex; - int msg1_frequencystart; - int mu; - int prach_sequence_length; - int restricted_set; - int numerology_index; - int nb_rx; - c16_t ***rxsigF; - c16_t (*Xu)[839]; - time_stats_t *rx_prach; -} 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 *); - static inline void init_abort(decode_abort_t *ab) { int ret = pthread_mutex_init(&ab->mutex_failure, NULL); diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h index 0d1cab4045..f4681020eb 100644 --- a/openair1/PHY/defs_nr_common.h +++ b/openair1/PHY/defs_nr_common.h @@ -133,6 +133,34 @@ typedef struct { int resource_type; } nr_srs_info_t; +#define NUMBER_OF_NR_PRACH_MAX 8 +typedef struct { + int frame; + int slot; + int num_slots; // prach duration in slots + int beams[NFAPI_MAX_NUM_BG_IF]; + nfapi_nr_prach_pdu_t pdu; + int rootSequenceIndex; + int numrootSequenceIndex; + int msg1_frequencystart; + int mu; + int prach_sequence_length; + int restricted_set; + int numerology_index; + int nb_rx; + c16_t rxsigF[NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX][NB_ANTENNAS_RX][NR_PRACH_SEQ_LEN_L]; + c16_t (*Xu)[839]; + time_stats_t *rx_prach; +} 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 NR_DL_FRAME_PARMS NR_DL_FRAME_PARMS; typedef uint32_t (*get_samples_per_slot_t)(int slot, const NR_DL_FRAME_PARMS *fp); diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index 7d87ac54a7..aba532a80d 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -718,15 +718,6 @@ int main(int argc, char **argv){ if (n_frames == 1) printf("slot %d, rx_prach_start %d\n", slot, rx_prach_start); - c16_t **rxsigF[NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX]; - for (int j = 0; j < NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX; j++) { - rxsigF[j] = malloc(ru->nb_rx * sizeof(*rxsigF)); - for (int i = 0; i < ru->nb_rx; i++) { - // largest size for PRACH FFT is 4x98304 (16*24576) - rxsigF[j][i] = malloc16_clear(4 * 98304 * sizeof(**rxsigF)); - } - } - for (SNR = snr0; SNR < snr1 && !stop; SNR += .1) { for (ue_speed = ue_speed0; ue_speed < ue_speed1 && !stop; ue_speed += 10) { delay_avg = 0.0; @@ -792,7 +783,6 @@ int main(int argc, char **argv){ .restricted_set = cfg->restricted_set_config.value, .numerology_index = gNB->frame_parms.numerology_index, .nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value, - .rxsigF = rxsigF, .Xu = gNB->X_u, .rx_prach = &gNB->rx_prach}; rx_nr_prach_ru(&in, ru->common.rxdata, ru->nr_frame_parms, ru->N_TA_offset); @@ -824,7 +814,7 @@ int main(int argc, char **argv){ LOG_M("rxsig0.m","rxs0", &ru->common.rxdata[0][subframe*frame_parms->samples_per_subframe], frame_parms->samples_per_subframe, 1, 1); LOG_M("ru_rxsig0.m","rxs0", &ru->common.rxdata[0][subframe*frame_parms->samples_per_subframe], frame_parms->samples_per_subframe, 1, 1); LOG_M("ru_rxsigF0.m","rxsF0", ru->common.rxdataF[0], frame_parms->ofdm_symbol_size*frame_parms->symbols_per_slot, 1, 1); - LOG_M("ru_prach_rxsigF0.m", "rxsF0", rxsigF[0][0], N_ZC, 1, 1); + LOG_M("ru_prach_rxsigF0.m", "rxsF0", in.rxsigF[0][0], N_ZC, 1, 1); LOG_M("prach_preamble.m","prachp", &gNB->X_u[0], N_ZC, 1, 1); LOG_M("ue_prach_preamble.m","prachp", &UE->X_u[0], N_ZC, 1, 1); #endif @@ -844,14 +834,7 @@ int main(int argc, char **argv){ } if (input_fd) break; - } //SNR loop - for (int j = 0; j < NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX; j++) { - for (int i = 0; i < ru->nb_rx; i++) { - // largest size for PRACH FFT is 4x98304 (16*24576) - free(rxsigF[j][i]); - } - free(rxsigF[j]); - } + } // SNR loop free_channel_desc_scm(UE2gNB); nr_phy_free_RU(ru); diff --git a/radio/fhi_72/oaioran.c b/radio/fhi_72/oaioran.c index 37661d7ad1..493221d6a9 100644 --- a/radio/fhi_72/oaioran.c +++ b/radio/fhi_72/oaioran.c @@ -184,12 +184,17 @@ static int read_prach_data(ru_info_t *ru, int frame, int slot) int nb_rx_per_ru = ru->nb_rx / fh_init->xran_ports; /* If it is PRACH slot, copy prach IQ from XRAN PRACH buffer to OAI PRACH buffer */ if (is_prach_slot) { + if (!ru->prach_buf) { + LOG_W(HW, "we get rach data from ru, but it is not scheduled %d.%d\n", frame, slot); + return -1; + } for (sym_idx = 0; sym_idx < prach_sym; sym_idx++) { for (int aa = 0; aa < ru->nb_rx; aa++) { int16_t *dst, *src; int idx = 0; oran_buf_list_t *bufs = get_xran_buffers(aa / nb_rx_per_ru); - dst = ru->prach_buf[aa]; // + (sym_idx*576)); + // hardcoded to use only first prach occasion + dst = (int16_t *)ru->prach_buf[0][aa]; src = (int16_t *)bufs->prachdstdecomp[aa % nb_rx_per_ru][tti % XRAN_N_FE_BUF_LEN].pBuffers[sym_idx].pData; /* convert Network order to host order */ if (ru_conf->compMeth_PRACH == XRAN_COMPMETHOD_NONE) { diff --git a/radio/fhi_72/oran_isolate.c b/radio/fhi_72/oran_isolate.c index 48e95e7344..9158e468d4 100644 --- a/radio/fhi_72/oran_isolate.c +++ b/radio/fhi_72/oran_isolate.c @@ -31,6 +31,7 @@ #include "common/utils/LOG/log.h" #include "common/utils/LOG/vcd_signal_dumper.h" #include "openair1/PHY/defs_gNB.h" +#include "openair1/PHY/NR_TRANSPORT/nr_transport_proto.h" #include "oaioran.h" #include "oran-config.h" @@ -212,13 +213,15 @@ int trx_oran_ctlrecv(openair0_device *device, void *msg, ssize_t msg_len) void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot) { - ru_info_t ru_info; - ru_info.nb_rx = ru->nb_rx * ru->num_beams_period; - ru_info.nb_tx = ru->nb_tx * ru->num_beams_period; - ru_info.rxdataF = ru->common.rxdataF; - ru_info.beam_id = ru->common.beam_id; - ru_info.num_beams_period = ru->num_beams_period; - ru_info.prach_buf = ru->prach_rxsigF[0]; // index: [prach_oca][ant_id] + prach_item_t *prach_id = find_nr_prach(&ru->gNB_list[0]->prach_list, *frame, *slot, SEARCH_EXIST); + ru_info_t ru_info = { + .nb_rx = ru->nb_rx * ru->num_beams_period, + .nb_tx = ru->nb_tx * ru->num_beams_period, + .rxdataF = ru->common.rxdataF, + .beam_id = ru->common.beam_id, + .num_beams_period = ru->num_beams_period, + .prach_buf = prach_id ? prach_id->rxsigF : NULL, + }; RU_proc_t *proc = &ru->proc; int f, sl; @@ -269,12 +272,13 @@ void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot) void oran_fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { start_meas(&ru->tx_fhaul); - ru_info_t ru_info; - ru_info.nb_rx = ru->nb_rx * ru->num_beams_period; - ru_info.nb_tx = ru->nb_tx * ru->num_beams_period; - ru_info.txdataF_BF = ru->common.txdataF_BF; - ru_info.beam_id = ru->common.beam_id; - ru_info.num_beams_period = ru->num_beams_period; + ru_info_t ru_info = { + .nb_rx = ru->nb_rx * ru->num_beams_period, + .nb_tx = ru->nb_tx * ru->num_beams_period, + .txdataF_BF = ru->common.txdataF_BF, + .beam_id = ru->common.beam_id, + .num_beams_period = ru->num_beams_period, + }; // printf("south_out:\tframe=%d\tslot=%d\ttimestamp=%ld\n",frame,slot,timestamp); diff --git a/radio/fhi_72/oran_isolate.h b/radio/fhi_72/oran_isolate.h index 7dd61ad739..6ec011a24b 100644 --- a/radio/fhi_72/oran_isolate.h +++ b/radio/fhi_72/oran_isolate.h @@ -28,7 +28,8 @@ #include #include "xran_fh_o_du.h" - +#include "openair1/PHY/impl_defs_nr.h" +#include "openair1/PHY/TOOLS/tools_defs.h" /* * Structure added to bear the information needed from OAI RU */ @@ -50,7 +51,7 @@ typedef struct ru_info_s { int num_beams_period; // Needed for Prach - int16_t **prach_buf; + c16_t (*prach_buf)[NB_ANTENNAS_RX][NR_PRACH_SEQ_LEN_L]; } ru_info_t; /** @brief Reads RX data (PRACH/PUSCH) of next slot. From 80223d36d532e04bf05e250ea00a793963715cdf Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 29 Oct 2025 15:20:09 +0100 Subject: [PATCH 137/275] Rename nr_fill_prach to nr_schedule_rx_prach() --- doc/SW-archi-graph.md | 2 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 2 +- openair1/PHY/NR_TRANSPORT/nr_transport_proto.h | 2 +- openair1/SCHED/nfapi_lte_dummy.c | 10 ++++------ openair1/SCHED/nfapi_nr_dummy.c | 2 +- openair1/SCHED_NR/fapi_nr_l1.c | 2 +- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/doc/SW-archi-graph.md b/doc/SW-archi-graph.md index a3397ed297..cf826e6e43 100644 --- a/doc/SW-archi-graph.md +++ b/doc/SW-archi-graph.md @@ -39,7 +39,7 @@ rx_func_implem[rx_func] --> handle_nr_nfapi_xxx_pdu --> sendTxFilled((L1_tx_filled)) --> nr_fill_ul_xxx - --> nr_fill_prach + --> nr_schedule_rx_prach() end gNB_dlsch_ulsch_scheduler --> NR_Schedule_response end diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 0d6a4887ca..823af815d3 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -79,7 +79,7 @@ prach_item_t *find_nr_prach(prach_list_t *l, int frame, int slot, find_type_t ty return p; } -prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) +prach_item_t *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, SEARCH_EXIST_OR_FREE); if (!prach) { diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 31a89e823b..0714f3dde3 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -200,7 +200,7 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, int slot, nfapi_nr_pusch_pdu_t *ulsch_pdu); -prach_item_t *nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); +prach_item_t *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; diff --git a/openair1/SCHED/nfapi_lte_dummy.c b/openair1/SCHED/nfapi_lte_dummy.c index 5d9b5b7a76..be532a0c54 100644 --- a/openair1/SCHED/nfapi_lte_dummy.c +++ b/openair1/SCHED/nfapi_lte_dummy.c @@ -41,12 +41,10 @@ void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pucch_pdu_t *pucch_pdu){} -void nr_fill_prach(PHY_VARS_gNB *gNB, - int SFN, - int Slot, - nfapi_nr_prach_pdu_t *prach_pdu){} -void nr_fill_prach_ru(RU_t *ru,prach_item_t *gnb_prach){} - +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 install_nr_schedule_handlers(NR_IF_Module_t *if_inst){} diff --git a/openair1/SCHED/nfapi_nr_dummy.c b/openair1/SCHED/nfapi_nr_dummy.c index 87c1ea576f..7377a455dd 100644 --- a/openair1/SCHED/nfapi_nr_dummy.c +++ b/openair1/SCHED/nfapi_nr_dummy.c @@ -37,7 +37,7 @@ void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pucch_pdu_t *pucch_pdu){} -void nr_fill_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) { } diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 4ec87a1e5f..448af8c0e3 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -155,7 +155,7 @@ void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti UL_tti_req->Slot); nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; LOG_D(NR_PHY_RACH, "Add prach decoding request for %d.%d\n", UL_tti_req->SFN, UL_tti_req->Slot); - prach_item_t *prach = nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, 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"); /* the present design is not supporting multiple gNB in one RU From 0d083d83fcbf2b86170522f169e2309194e4045c Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 29 Oct 2025 15:18:46 +0100 Subject: [PATCH 138/275] Simplify code through lookup of PDU name, delete commented code --- .../nfapi/public_inc/nfapi_nr_interface_scf.h | 5 ++- openair1/SCHED_NR/fapi_nr_l1.c | 43 +++++-------------- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h index 754ef60e55..12cf081fdc 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h @@ -1481,7 +1481,10 @@ typedef enum { NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE, NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE, } nfapi_nr_ul_config_pdu_type_e; - +static const char* const txt_nfapi_nr_ul_config_pdu_type[] = {"NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE", + "NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE", + "NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE", + "NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE"}; typedef struct { uint16_t pdu_type;//0: PRACH PDU, 1: PUSCH PDU, 2: PUCCH PDU, 3: SRS PDU diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 448af8c0e3..9e4ab67efc 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -127,49 +127,28 @@ void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti int slot = UL_tti_req->Slot; for (int i = 0; i < UL_tti_req->n_pdus; i++) { - switch (UL_tti_req->pdus_list[i].pdu_type) { + int type = UL_tti_req->pdus_list[i].pdu_type; + LOG_D(NR_PHY, + "frame %d, slot %d got %s for %d.%d\n", + frame, + slot, + txt_nfapi_nr_ul_config_pdu_type[type], + UL_tti_req->SFN, + UL_tti_req->Slot); + switch (type) { case NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE: - LOG_D(NR_PHY, - "frame %d, slot %d, Got NFAPI_NR_UL_TTI_PUSCH_PDU_TYPE for %d.%d\n", - frame, - slot, - UL_tti_req->SFN, - UL_tti_req->Slot); nr_fill_ulsch(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].pusch_pdu); break; case NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE: - LOG_D(NR_PHY, - "frame %d, slot %d, Got NFAPI_NR_UL_TTI_PUCCH_PDU_TYPE for %d.%d\n", - frame, - slot, - UL_tti_req->SFN, - UL_tti_req->Slot); nr_fill_pucch(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].pucch_pdu); break; - case NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE: - LOG_D(NR_PHY, - "frame %d, slot %d, Got NFAPI_NR_UL_TTI_PRACH_PDU_TYPE for %d.%d\n", - frame, - slot, - UL_tti_req->SFN, - UL_tti_req->Slot); + case NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE: { nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; - LOG_D(NR_PHY_RACH, "Add prach decoding request for %d.%d\n", UL_tti_req->SFN, UL_tti_req->Slot); 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"); - /* the present design is not supporting multiple gNB in one RU - if (prach && (gNB->RU_list[0]->if_south == LOCAL_RF || gNB->RU_list[0]->if_south == REMOTE_IF5)) - nr_fill_prach_ru(gNB->RU_list[0], prach); - */ - break; + } break; case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: - LOG_D(NR_PHY, - "frame %d, slot %d, Got NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE for %d.%d\n", - frame, - slot, - UL_tti_req->SFN, - UL_tti_req->Slot); nr_fill_srs(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].srs_pdu); break; } From d8d15205805735c6d3c0c9f81b0098bfdc9c97ae Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 21 Oct 2025 21:56:58 +0200 Subject: [PATCH 139/275] reduce sl_ahead to catch the RACH Msg1 in time in 24PRB test a bit difficult to understand: sl_ahead=6 => we systematically miss the rach detection in the scheduler because the scheduler runs more than 6 slots ahead the rx slot processing (here > 11 slots because 6 (DL and UL slots) + 4 (RU_RX_SLOT_DEPTH, so UL only)) so, reducing sl_ahead works better than large sl_ahead because the idea behind is: the scheduler of slot+sl_ahead will never create work to do in a UL slot before slot+sl_ahead+X (as minimum k2 is X, in the 24PRB CI test: 6) --- ci-scripts/conf_files/gnb.sa.band78.24prb.rfsim.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/ci-scripts/conf_files/gnb.sa.band78.24prb.rfsim.conf b/ci-scripts/conf_files/gnb.sa.band78.24prb.rfsim.conf index 4a3d2852c4..afe40c733c 100644 --- a/ci-scripts/conf_files/gnb.sa.band78.24prb.rfsim.conf +++ b/ci-scripts/conf_files/gnb.sa.band78.24prb.rfsim.conf @@ -196,6 +196,7 @@ RUs = ( att_tx = 0 att_rx = 0; bands = [78]; + sl_ahead=3; max_pdschReferenceSignalPower = -27; max_rxgain = 114; sf_extension = 0; From 9ce9c16e368fcca70152c9d712e4cc3f82cc24f5 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 29 Oct 2025 16:25:42 +0100 Subject: [PATCH 140/275] Add documentation on PRACH processing --- doc/rach_processing_in_gNB.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 doc/rach_processing_in_gNB.md diff --git a/doc/rach_processing_in_gNB.md b/doc/rach_processing_in_gNB.md new file mode 100644 index 0000000000..5c15cf0812 --- /dev/null +++ b/doc/rach_processing_in_gNB.md @@ -0,0 +1,24 @@ +The gNB RACH processing + +1. Collect I/Q samples +The gNB/ru main loop, in ru_thread() +If there is rach occasions in this slot +this main loop calls rx_nr_prach_ru() that stores the I/Q samples in the prach_item_t context + +2.process the samples +L1_rx_thread() has received slot indication by msg on queue resp_L1, it calls rx_func() => L1_nr_prach_procedures() +L1_nr_prach_procedures() fills nfapi_nr_rach_indication_t structure from result of rx_nr_prach() that process the content of rxsigF[prachOccasion][aa] +rx_func() sends the decoded rach to upper processing by call to NR_UL_indication() that calls handle_nr_rach() => nr_initiate_ra_proc() + +nr_initiate_ra_proc() creates a UE context if it needs, then sets UE.ra.ra_state to what it should do + +3. send the RACH DL +tx_func() that is launched by L1_tx_thread() as it gets L1_tx_out msg (from main loop in 1.) +tx_func()=>run_scheduler_monolithic()=>gNB_dlsch_ulsch_scheduler()=>nr_scheduler_RA() +nr_scheduler_RA() process all existing UE contexts, to see it it has to schedule a RACH DL + +4. global synchronisation +resp_L1 msg for (2.) is created in tx_func() that is launched by L1_tx_thread() (see3.) when it receives slot indication as it gets L1_tx_out msg +this msg is sent by the main loop (see 1.) +tx_func()=>run_scheduler_monolithic()=>nr_schedule_response ()=>nr_schedule_ul_tti_req ()=> nr_schedule_rx_prach() +writes in the triggering structure to (1.) that it will have to run rx_nr_prach_ru() in a given future slot From 27a022d6f86950d6b624b4380efb46854c1383a8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 30 Oct 2025 23:13:46 +0100 Subject: [PATCH 141/275] Fix: avoid retransmission with 0 RBs Check and return, if necessary, that we have enough resources to make retransmissions. Without, we can asserts in L1, such as Assertion (NPRB>0 && (NPRB + RBstart <= BWPsize)) failed! In PRBalloc_to_locationandbandwidth0() openairinterface5g/common/utils/nr/nr_common.c:506 Illegal NPRB/RBstart Configuration (0,51) for BWPsize 51 which indicates that the scheduler requested a transmission with 0 PRBs, which does not make sense. --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 8 ++++++++ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index aa184d8f85..b04e0029dd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -494,6 +494,7 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac, while (rbStart + rbSize <= rbStop && !(rballoc_mask[rbStart + rbSize] & slbitmap) && rbSize < new_sched.rbSize) rbSize++; + DevAssert(rbSize > 0); } } else { /* the retransmission will use a different time domain allocation, check @@ -504,8 +505,14 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac, while (rbStart < rbStop && (rballoc_mask[rbStart] & slbitmap)) rbStart++; + if (rbStart >= rbStop) { + LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not allocate DL retransmission: no resources\n", UE->rnti, frame, slot); + return false; + } + while (rbStart + rbSize <= rbStop && !(rballoc_mask[rbStart + rbSize] & slbitmap)) rbSize++; + DevAssert(rbSize > 0); uint32_t new_tbs; uint16_t new_rbSize; @@ -1079,6 +1086,7 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE pucch ? pucch->ul_slot : 0, sched_pdsch->pucch_allocation, sched_ctrl->tpc1); + DevAssert(sched_pdsch->rbSize > 0); const int bwp_id = current_BWP->bwp_id; const int coresetid = sched_ctrl->coreset->controlResourceSetId; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index f923a2c9ec..5974edf1f8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1834,6 +1834,10 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac, const uint16_t slbitmap = SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols); while (rbStart < bwpSize && (rballoc_mask[rbStart + bwpStart] & slbitmap)) rbStart++; + if (rbStart >= bwpSize) { + LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not allocate UL retransmission: no resources\n", UE->rnti, frame, slot); + return false; + } int rbSize = 0; while (rbStart + rbSize < bwpSize && !(rballoc_mask[rbStart + bwpStart + rbSize] & slbitmap)) rbSize++; From 24399eac2d4126ba4eb55fc00082be85b4915193 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Oct 2025 19:53:26 +0200 Subject: [PATCH 142/275] update_dlsch_buffer() for individual UEs --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 70 +++++++++---------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index b04e0029dd..7a52198240 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -321,46 +321,41 @@ int nr_write_ce_dlsch_pdu(module_id_t module_idP, return offset; } -static void nr_store_dlsch_buffer(module_id_t module_id, frame_t frame, slot_t slot) +static uint32_t update_dlsch_buffer(frame_t frame, slot_t slot, NR_UE_info_t *UE) { - UE_iterator(RC.nrmac[module_id]->UE_info.connected_ue_list, UE) { - NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; - sched_ctrl->num_total_bytes = 0; - sched_ctrl->dl_pdus_total = 0; + NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; + sched_ctrl->num_total_bytes = 0; + sched_ctrl->dl_pdus_total = 0; - /* loop over all activated logical channels */ - // Note: DL_SCH_LCID_DCCH, DL_SCH_LCID_DCCH1, DL_SCH_LCID_DTCH - for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); ++i) { - const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i); - const int lcid = c->lcid; - const uint16_t rnti = UE->rnti; - LOG_D(NR_MAC, "In %s: UE %x: LCID %d\n", __FUNCTION__, rnti, lcid); - memset(&sched_ctrl->rlc_status[lcid], 0, sizeof(sched_ctrl->rlc_status[lcid])); - if (c->suspended) - continue; - if (lcid == DL_SCH_LCID_DTCH && nr_timer_is_active(&sched_ctrl->transm_interrupt)) - continue; - sched_ctrl->rlc_status[lcid] = nr_mac_rlc_status_ind(rnti, frame, lcid); + /* loop over all activated logical channels */ + for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); ++i) { + const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i); + const int lcid = c->lcid; + const uint16_t rnti = UE->rnti; + LOG_D(NR_MAC, "UE %x: LCID %d\n", rnti, lcid); + memset(&sched_ctrl->rlc_status[lcid], 0, sizeof(sched_ctrl->rlc_status[lcid])); + if (c->suspended) + continue; + if (lcid == DL_SCH_LCID_DTCH && nr_timer_is_active(&sched_ctrl->transm_interrupt)) + continue; + sched_ctrl->rlc_status[lcid] = nr_mac_rlc_status_ind(rnti, frame, lcid); - if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0) - continue; + if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0) + continue; - sched_ctrl->dl_pdus_total += sched_ctrl->rlc_status[lcid].pdus_in_buffer; - sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer; - LOG_D(MAC, - "[gNB %d][%4d.%2d] %s%d->DLSCH, RLC status for UE %d: %d bytes in buffer, total DL buffer size = %d bytes, %d total PDU bytes, %s TA command\n", - module_id, - frame, - slot, - lcid < 4 ? "DCCH":"DTCH", - lcid, - UE->rnti, - sched_ctrl->rlc_status[lcid].bytes_in_buffer, - sched_ctrl->num_total_bytes, - sched_ctrl->dl_pdus_total, - sched_ctrl->ta_apply ? "send":"do not send"); - } + sched_ctrl->dl_pdus_total += sched_ctrl->rlc_status[lcid].pdus_in_buffer; + sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer; + LOG_D(MAC, + "%4d.%2d UE %04x LCID %d status: %d bytes, total buffer %d bytes %d PDUs\n", + frame, + slot, + UE->rnti, + lcid, + sched_ctrl->rlc_status[lcid].bytes_in_buffer, + sched_ctrl->num_total_bytes, + sched_ctrl->dl_pdus_total); } + return sched_ctrl->num_total_bytes; } void finish_nr_dl_harq(NR_UE_sched_ctrl_t *sched_ctrl, int harq_pid) @@ -903,7 +898,6 @@ static void pf_dl(gNB_MAC_INST *mac, static void nr_dlsch_preprocessor(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch) { - module_id_t module_id = 0; NR_UEs_t *UE_info = &mac->UE_info; if (UE_info->connected_ue_list[0] == NULL) @@ -917,7 +911,9 @@ static void nr_dlsch_preprocessor(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pd n_rb_sched[i] = bw; /* Retrieve amount of data to send for this UE */ - nr_store_dlsch_buffer(module_id, pp_pdsch->frame, pp_pdsch->slot); + UE_iterator(mac->UE_info.connected_ue_list, UE) { + update_dlsch_buffer(pp_pdsch->frame, pp_pdsch->slot, UE); + } int average_agg_level = 4; // TODO find a better estimation int max_sched_ues = bw / (average_agg_level * NR_NB_REG_PER_CCE); From 916455f0279a2189b2580371c1bd51e71d0621eb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Oct 2025 19:55:06 +0200 Subject: [PATCH 143/275] Move update_dlsch_buffer() into pf_dl() This slightly speeds up the scheduler (it e.g., does not need to check for UEs that have retransmissions), and harmonizes with pf_ul(), which evaluates (UL) BSR at a similar place within the scheduler. --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 7a52198240..d848cf99c8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -687,6 +687,8 @@ static void pf_dl(gNB_MAC_INST *mac, continue; } + update_dlsch_buffer(pp_pdsch->frame, pp_pdsch->slot, UE); + /* Check DL buffer and skip this UE if no bytes and no TA necessary */ if (sched_ctrl->num_total_bytes == 0 && frame != (sched_ctrl->ta_frame + 100) % 1024) continue; @@ -910,11 +912,6 @@ static void nr_dlsch_preprocessor(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pd for (int i = 0; i < num_beams; i++) n_rb_sched[i] = bw; - /* Retrieve amount of data to send for this UE */ - UE_iterator(mac->UE_info.connected_ue_list, UE) { - update_dlsch_buffer(pp_pdsch->frame, pp_pdsch->slot, UE); - } - int average_agg_level = 4; // TODO find a better estimation int max_sched_ues = bw / (average_agg_level * NR_NB_REG_PER_CCE); From 9449f777ccd273b6ab4ecba61ab64c0b7ac2d689 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 28 Oct 2025 19:16:46 +0100 Subject: [PATCH 144/275] Remove TBS table and recompute on demand --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 41 ++++++------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 5974edf1f8..94325bf26b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1913,7 +1913,6 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac, return true; } -static uint32_t ul_pf_tbs[5][29]; // pre-computed, approximate TBS values for PF coefficient typedef struct UEsched_s { float coef; bool sched_inactive; @@ -2069,8 +2068,8 @@ static int pf_ul(gNB_MAC_INST *nrmac, const int max_mcs_table = (current_BWP->mcs_table == 0 || current_BWP->mcs_table == 2) ? 28 : 27; const int max_mcs = min(bo->max_mcs, max_mcs_table); /* no per-user maximum MCS yet */ int selected_mcs; + int nrOfLayers = get_ul_nrOfLayers(sched_ctrl, current_BWP->dci_format); if (bo->harq_round_max == 1) { - int nrOfLayers = get_ul_nrOfLayers(sched_ctrl, current_BWP->dci_format); selected_mcs = get_mcs_from_SINRx10(current_BWP->mcs_table, sched_ctrl->pusch_snrx10, nrOfLayers); selected_mcs = min(max_mcs, selected_mcs); selected_mcs = max(bo->min_mcs, selected_mcs); @@ -2082,7 +2081,17 @@ static int pf_ul(gNB_MAC_INST *nrmac, /* Create UE_sched for UEs eligibale for new data transmission*/ /* Calculate coefficient*/ - const uint32_t tbs = ul_pf_tbs[current_BWP->mcs_table][selected_mcs]; + const uint8_t Qm = nr_get_Qm_dl(selected_mcs, current_BWP->mcs_table); + const uint16_t R = nr_get_code_rate_dl(selected_mcs, current_BWP->mcs_table); + const uint32_t tbs = nr_compute_tbs(Qm, + R, + 1, /* rbSize */ + 10, /* hypothetical number of slots */ + 0, /* N_PRB_DMRS * N_DMRS_SLOT */ + 0 /* N_PRB_oh, 0 for initialBWP */, + 0 /* tb_scaling */, + nrOfLayers) + >> 3; float coeff_ue = (float) tbs / UE->ul_thr_ue; bool sched_inactive = B == 0 && do_sched; LOG_D(NR_MAC, "[UE %04x][%4d.%2d] b %d, ul_thr_ue %f, tbs %d, coeff_ue %f, sched_inactive %d\n", @@ -2707,32 +2716,6 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp nr_pp_impl_ul nr_init_ulsch_preprocessor(int CC_id) { - /* during initialization: no mutex needed */ - /* in the PF algorithm, we have to use the TBsize to compute the coefficient. - * This would include the number of DMRS symbols, which in turn depends on - * the time domain allocation. In case we are in a mixed slot, we do not want - * to recalculate all these values, and therefore we provide a look-up table - * which should approximately(!) give us the TBsize. In particular, the - * number of symbols, the number of DMRS symbols, and the exact Qm and R, are - * not correct*/ - for (int mcsTableIdx = 0; mcsTableIdx < 5; ++mcsTableIdx) { - for (int mcs = 0; mcs < 29; ++mcs) { - if (mcs > 27 && (mcsTableIdx == 1 || mcsTableIdx == 3 || mcsTableIdx == 4)) - continue; - const uint8_t Qm = nr_get_Qm_ul(mcs, mcsTableIdx); - const uint16_t R = nr_get_code_rate_ul(mcs, mcsTableIdx); - /* note: we do not update R/Qm based on low MCS or pi2BPSK */ - ul_pf_tbs[mcsTableIdx][mcs] = nr_compute_tbs(Qm, - R, - 1, /* rbSize */ - 10, /* hypothetical number of slots */ - 0, /* N_PRB_DMRS * N_DMRS_SLOT */ - 0 /* N_PRB_oh, 0 for initialBWP */, - 0 /* tb_scaling */, - 1 /* nrOfLayers */) - >> 3; - } - } return nr_ulsch_preprocessor; } From e6b307bf6e928f9dbf311b05431c5dede1b7e23d Mon Sep 17 00:00:00 2001 From: Nick Hedberg Date: Wed, 29 Oct 2025 20:19:02 +0000 Subject: [PATCH 145/275] Indent correctly --- radio/USRP/usrp_lib.cpp | 53 +++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/radio/USRP/usrp_lib.cpp b/radio/USRP/usrp_lib.cpp index 79c58b59f4..923e938869 100644 --- a/radio/USRP/usrp_lib.cpp +++ b/radio/USRP/usrp_lib.cpp @@ -488,38 +488,39 @@ static int trx_usrp_write(openair0_device *device, buff_tx[i][j] = simde_mm256_slli_epi16(tmp, 4); } } - } + } - s->tx_md.has_time_spec = true; - s->tx_md.start_of_burst = (s->tx_count==0) ? true : first_packet_state; - s->tx_md.end_of_burst = last_packet_state; - s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate); - s->tx_count++; + s->tx_md.has_time_spec = true; + s->tx_md.start_of_burst = (s->tx_count == 0) ? true : first_packet_state; + s->tx_md.end_of_burst = last_packet_state; + s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate); + s->tx_count++; -VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO,1); - // bit 13 enables gpio - if ((flags_gpio & TX_GPIO_CHANGE) != 0) { - // push GPIO bits - s->usrp->set_command_time(s->tx_md.time_spec); - s->usrp->set_gpio_attr(s->gpio_bank, "OUT", flags_gpio, MAN_MASK); - s->usrp->clear_command_time(); - } -VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO,0); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO, 1); + // bit 13 enables gpio + if ((flags_gpio & TX_GPIO_CHANGE) != 0) { + // push GPIO bits + s->usrp->set_command_time(s->tx_md.time_spec); + s->usrp->set_gpio_attr(s->gpio_bank, "OUT", flags_gpio, MAN_MASK); + s->usrp->clear_command_time(); + } + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO, 0); - if (cc>1) { - std::vector buff_ptrs; + if (cc > 1) { + std::vector buff_ptrs; - for (int i=0; itx_stream->send(buff_ptrs, nsamps, s->tx_md); - } - else { - ret = (int)s->tx_stream->send(&(((int16_t *)buff_tx[0])[0]), nsamps, s->tx_md); - } + ret = (int)s->tx_stream->send(buff_ptrs, nsamps, s->tx_md); + } else { + ret = (int)s->tx_stream->send(&(((int16_t *)buff_tx[0])[0]), nsamps, s->tx_md); + } - if (ret != nsamps) LOG_E(HW,"[xmit] tx samples %d != %d\n",ret,nsamps); - return ret; + if (ret != nsamps) { + LOG_E(HW, "[xmit] tx samples %d != %d\n", ret, nsamps); + } + return ret; } else { pthread_mutex_lock(&write_thread->mutex_write); From 330c4fce5d84d3d7fc6a0fbdc67ed74189537e41 Mon Sep 17 00:00:00 2001 From: Nick Hedberg Date: Wed, 29 Oct 2025 20:19:34 +0000 Subject: [PATCH 146/275] Fix capitalization --- openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c index 23cd60b25f..0939b52c7a 100644 --- a/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c @@ -82,7 +82,7 @@ void print_ue_mac_stats(const module_id_t mod, const int frame_rx, const int slo for (int i = 1; i < nb + 1; i++) cur += snprintf(cur, end - cur, "/%lu", mac->stats.dl.rounds[i]); - cur += snprintf(cur, end - cur, "\n Ul harq: %lu", mac->stats.ul.rounds[0]); + cur += snprintf(cur, end - cur, "\n UL harq: %lu", mac->stats.ul.rounds[0]); for (nb = NR_MAX_HARQ_ROUNDS_FOR_STATS - 1; nb > 1; nb--) if (mac->stats.ul.rounds[nb]) break; From e4812c3d8ecf6adc43124ed81ca5d7d339cfa9e4 Mon Sep 17 00:00:00 2001 From: Nick Hedberg Date: Wed, 15 Oct 2025 10:49:50 +0000 Subject: [PATCH 147/275] Allow for null-string REGISTRY names --- .../4g_l2sim_fdd/docker-compose.yml | 6 ++--- .../docker-compose.yml | 4 ++-- .../docker-compose.yml | 4 ++-- .../docker-compose.yml | 4 ++-- .../docker-compose.yml | 4 ++-- .../4g_rfsimulator_fembms/docker-compose.yml | 4 ++-- .../4g_rfsimulator_mbms/docker-compose.yml | 4 ++-- .../docker-compose.yml | 4 ++-- .../5g_f1_rfsimulator/docker-compose.yaml | 8 +++---- .../5g_fdd_rfsimulator/docker-compose.yaml | 4 ++-- .../5g_rfsimulator/docker-compose.yaml | 22 +++++++++---------- .../5g_rfsimulator_24prb/docker-compose.yaml | 4 ++-- .../5g_rfsimulator_2x2/docker-compose.yaml | 4 ++-- .../5g_rfsimulator_e1/docker-compose.yaml | 20 ++++++++--------- .../docker-compose.yaml | 4 ++-- .../5g_rfsimulator_flexric/docker-compose.yml | 16 +++++++------- .../docker-compose.yaml | 4 ++-- .../docker-compose.yaml | 4 ++-- .../5g_rfsimulator_n2_ho/docker-compose.yaml | 14 ++++++------ .../docker-compose.yaml | 4 ++-- .../docker-compose.yaml | 4 ++-- .../docker-compose.yaml | 4 ++-- .../docker-compose.yaml | 4 ++-- .../docker-compose.yaml | 6 ++--- .../5g_sa_f1_b210_ho/docker-compose.yml | 6 ++--- .../5g_sa_n2_ho_b210/docker-compose.yml | 4 ++-- .../5g_sa_n310_2x2_100MHz/docker-compose.yml | 2 +- .../5g_sa_n310_2x2_60MHz/docker-compose.yml | 2 +- .../5g_sa_n310_4x4_60MHz/docker-compose.yml | 4 ++-- .../5g_sa_n310_gnb/docker-compose.yml | 2 +- .../5g_sa_n310_nrue/docker-compose.yml | 2 +- .../fr1_enb_mono_fdd_tim/docker-compose.yml | 2 +- .../docker-compose.yml | 4 ++-- .../lte_b200_fdd_05Mhz_tm1/docker-compose.yml | 2 +- .../docker-compose.yml | 2 +- .../docker-compose.yml | 2 +- .../lte_b200_fdd_10Mhz_tm1/docker-compose.yml | 2 +- .../docker-compose.yml | 2 +- .../docker-compose.yml | 2 +- .../lte_b200_fdd_20Mhz_tm1/docker-compose.yml | 2 +- .../docker-compose.yml | 4 ++-- .../lte_b200_tdd_05Mhz_tm1/docker-compose.yml | 2 +- .../lte_b200_tdd_05Mhz_tm2/docker-compose.yml | 2 +- .../lte_b200_tdd_10Mhz_tm1/docker-compose.yml | 2 +- .../lte_b200_tdd_20Mhz_tm1/docker-compose.yml | 2 +- .../docker-compose.yml | 2 +- .../lte_n3xx_tdd_2x2_tm1/docker-compose.yml | 2 +- .../lte_n3xx_tdd_2x2_tm2/docker-compose.yml | 2 +- .../nsa_b200_enb/docker-compose.yml | 2 +- .../nsa_b200_gnb/docker-compose.yml | 2 +- .../sa_aw2s_2x2_gnb/docker-compose.yml | 2 +- .../yaml_files/sa_aw2s_gnb/docker-compose.yml | 2 +- .../yaml_files/sa_b200_gnb/docker-compose.yml | 2 +- .../sa_e1_b200_gnb/docker-compose.yml | 6 ++--- .../sa_f1_b200_gnb/docker-compose.yml | 4 ++-- .../docker-compose.yml | 2 +- .../docker-compose.yml | 2 +- .../sa_fhi_7.2_vvdn_gnb/docker-compose.yml | 2 +- .../sa_gnb_aerial/docker-compose.yaml | 2 +- .../sa_gnb_aerial_ul/docker-compose.yaml | 2 +- .../sa_sc_b200_gnb/docker-compose.yml | 2 +- 61 files changed, 126 insertions(+), 126 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 0d810a1d4b..4ae1f78aa6 100644 --- a/ci-scripts/yaml_files/4g_l2sim_fdd/docker-compose.yml +++ b/ci-scripts/yaml_files/4g_l2sim_fdd/docker-compose.yml @@ -178,7 +178,7 @@ services: retries: 5 oai_enb: - image: ${REGISTRY:-oaisoftwarealliance}/${ENB_IMG:-oai-enb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${ENB_IMG:-oai-enb}:${TAG:-develop} privileged: true container_name: l2sim4g-oai-enb network_mode: "host" @@ -210,7 +210,7 @@ services: retries: 5 oai_ue1: - image: ${REGISTRY:-oaisoftwarealliance}/${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} privileged: true container_name: l2sim4g-oai-lte-ue1 network_mode: "host" @@ -229,7 +229,7 @@ services: retries: 5 oai_ue2: - image: ${REGISTRY:-oaisoftwarealliance}/${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} privileged: true container_name: l2sim4g-oai-lte-ue2 network_mode: "host" 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 4b110abf74..5461f37a10 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 @@ -178,7 +178,7 @@ services: retries: 5 oai_enb0: - image: ${REGISTRY:-oaisoftwarealliance}/${ENB_IMG:-oai-enb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${ENB_IMG:-oai-enb}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-enb networks: @@ -197,7 +197,7 @@ services: retries: 5 oai_ue0: - image: ${REGISTRY:-oaisoftwarealliance}/${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-lte-ue0 networks: diff --git a/ci-scripts/yaml_files/4g_rfsimulator_fdd_05MHz_noS1/docker-compose.yml b/ci-scripts/yaml_files/4g_rfsimulator_fdd_05MHz_noS1/docker-compose.yml index 9de0f448c3..ab07314c91 100644 --- a/ci-scripts/yaml_files/4g_rfsimulator_fdd_05MHz_noS1/docker-compose.yml +++ b/ci-scripts/yaml_files/4g_rfsimulator_fdd_05MHz_noS1/docker-compose.yml @@ -1,6 +1,6 @@ services: oai_enb0: - image: ${REGISTRY:-oaisoftwarealliance}/${ENB_IMG:-oai-enb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${ENB_IMG:-oai-enb}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-enb networks: @@ -19,7 +19,7 @@ services: retries: 5 oai_ue0: - image: ${REGISTRY:-oaisoftwarealliance}/${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-lte-ue0 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 1cb0150465..856428f728 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 @@ -178,7 +178,7 @@ services: retries: 5 oai_enb0: - image: ${REGISTRY:-oaisoftwarealliance}/${ENB_IMG:-oai-enb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${ENB_IMG:-oai-enb}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-enb networks: @@ -197,7 +197,7 @@ services: retries: 5 oai_ue0: - image: ${REGISTRY:-oaisoftwarealliance}/${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-lte-ue0 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 a0e43a27d2..3458c4f11b 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 @@ -178,7 +178,7 @@ services: retries: 5 oai_enb0: - image: ${REGISTRY:-oaisoftwarealliance}/${ENB_IMG:-oai-enb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${ENB_IMG:-oai-enb}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-enb networks: @@ -197,7 +197,7 @@ services: retries: 5 oai_ue0: - image: ${REGISTRY:-oaisoftwarealliance}/${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-lte-ue0 networks: diff --git a/ci-scripts/yaml_files/4g_rfsimulator_fembms/docker-compose.yml b/ci-scripts/yaml_files/4g_rfsimulator_fembms/docker-compose.yml index 5c7f267074..cadb22236a 100644 --- a/ci-scripts/yaml_files/4g_rfsimulator_fembms/docker-compose.yml +++ b/ci-scripts/yaml_files/4g_rfsimulator_fembms/docker-compose.yml @@ -1,6 +1,6 @@ services: oai_enb0: - image: ${REGISTRY:-oaisoftwarealliance}/${ENB_IMG:-oai-enb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${ENB_IMG:-oai-enb}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-enb networks: @@ -19,7 +19,7 @@ services: retries: 5 oai_ue0: - image: ${REGISTRY:-oaisoftwarealliance}/${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-lte-ue0 networks: diff --git a/ci-scripts/yaml_files/4g_rfsimulator_mbms/docker-compose.yml b/ci-scripts/yaml_files/4g_rfsimulator_mbms/docker-compose.yml index af913ead24..027644d9a1 100644 --- a/ci-scripts/yaml_files/4g_rfsimulator_mbms/docker-compose.yml +++ b/ci-scripts/yaml_files/4g_rfsimulator_mbms/docker-compose.yml @@ -1,6 +1,6 @@ services: oai_enb0: - image: ${REGISTRY:-oaisoftwarealliance}/${ENB_IMG:-oai-enb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${ENB_IMG:-oai-enb}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-enb networks: @@ -19,7 +19,7 @@ services: retries: 5 oai_ue0: - image: ${REGISTRY:-oaisoftwarealliance}/${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-lte-ue0 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 7ebbed861f..24acd9c676 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 @@ -178,7 +178,7 @@ services: retries: 5 oai_enb0: - image: ${REGISTRY:-oaisoftwarealliance}/${ENB_IMG:-oai-enb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${ENB_IMG:-oai-enb}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-enb networks: @@ -197,7 +197,7 @@ services: retries: 5 oai_ue0: - image: ${REGISTRY:-oaisoftwarealliance}/${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${LTEUE_IMG:-oai-lte-ue}:${TAG:-develop} privileged: true container_name: rfsim4g-oai-lte-ue0 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 73d2505e92..49dcf0e450 100644 --- a/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml @@ -92,7 +92,7 @@ services: retries: 5 oai-cu: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-cu cap_drop: - ALL @@ -121,7 +121,7 @@ services: retries: 5 oai-du-pci0: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-du-pci0 cap_drop: - ALL @@ -152,7 +152,7 @@ services: retries: 5 oai-du-pci1: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-du-pci1 cap_drop: - ALL @@ -187,7 +187,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL 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 9eec82a98b..8bcb771967 100644 --- a/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml @@ -90,7 +90,7 @@ services: start_period: 10s start_interval: 500ms oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-gnb cap_drop: - ALL @@ -113,7 +113,7 @@ services: retries: 5 ipc: host oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL diff --git a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml index ecdf79872b..97dcce4d49 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml @@ -90,7 +90,7 @@ services: timeout: 5s retries: 5 oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-gnb cap_add: - SYS_NICE @@ -114,7 +114,7 @@ services: timeout: 5s retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL @@ -141,7 +141,7 @@ services: timeout: 5s retries: 5 oai-nr-ue2: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue2 cap_drop: - ALL @@ -168,7 +168,7 @@ services: timeout: 5s retries: 5 oai-nr-ue3: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue3 cap_drop: - ALL @@ -195,7 +195,7 @@ services: timeout: 5s retries: 5 oai-nr-ue4: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue4 cap_drop: - ALL @@ -222,7 +222,7 @@ services: timeout: 5s retries: 5 oai-nr-ue5: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue5 cap_drop: - ALL @@ -249,7 +249,7 @@ services: timeout: 5s retries: 5 oai-nr-ue6: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue6 cap_drop: - ALL @@ -276,7 +276,7 @@ services: timeout: 5s retries: 5 oai-nr-ue7: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue7 cap_drop: - ALL @@ -303,7 +303,7 @@ services: timeout: 5s retries: 5 oai-nr-ue8: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue8 cap_drop: - ALL @@ -330,7 +330,7 @@ services: timeout: 5s retries: 5 oai-nr-ue9: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue9 cap_drop: - ALL @@ -357,7 +357,7 @@ services: timeout: 5s retries: 5 oai-nr-ue10: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue10 cap_drop: - ALL 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 9cab11ea9e..b5b9684626 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml @@ -90,7 +90,7 @@ services: timeout: 5s retries: 5 oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-gnb cap_drop: - ALL @@ -112,7 +112,7 @@ services: timeout: 5s retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL 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 778754fd58..f3482280bd 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml @@ -90,7 +90,7 @@ services: timeout: 5s retries: 5 oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-gnb cap_drop: - ALL @@ -115,7 +115,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL 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 9ccce2a8e1..200a1b6e3a 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml @@ -71,7 +71,7 @@ services: interface_name: eth1 oai-cucp: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-cucp cap_drop: - ALL @@ -100,7 +100,7 @@ services: retries: 5 oai-cuup: - image: ${REGISTRY:-oaisoftwarealliance}/${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} container_name: rfsim5g-oai-cuup cap_drop: - ALL @@ -131,7 +131,7 @@ services: retries: 5 oai-cuup2: - image: ${REGISTRY:-oaisoftwarealliance}/${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} container_name: rfsim5g-oai-cuup2 cap_drop: - ALL @@ -165,7 +165,7 @@ services: retries: 5 oai-cuup3: - image: ${REGISTRY:-oaisoftwarealliance}/${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} container_name: rfsim5g-oai-cuup3 cap_drop: - ALL @@ -199,7 +199,7 @@ services: retries: 5 oai-du: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-du cap_drop: - ALL @@ -230,7 +230,7 @@ services: retries: 5 oai-du2: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-du2 cap_drop: - ALL @@ -264,7 +264,7 @@ services: retries: 5 oai-du3: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-du3 cap_drop: - ALL @@ -298,7 +298,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL @@ -328,7 +328,7 @@ services: retries: 5 oai-nr-ue2: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue2 cap_drop: - ALL @@ -358,7 +358,7 @@ services: retries: 5 oai-nr-ue3: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue3 cap_drop: - ALL diff --git a/ci-scripts/yaml_files/5g_rfsimulator_fdd_phytest/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_fdd_phytest/docker-compose.yaml index 1dec021ffc..533c5d576c 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_fdd_phytest/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_fdd_phytest/docker-compose.yaml @@ -1,6 +1,6 @@ services: oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-gnb cap_drop: - ALL @@ -25,7 +25,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL diff --git a/ci-scripts/yaml_files/5g_rfsimulator_flexric/docker-compose.yml b/ci-scripts/yaml_files/5g_rfsimulator_flexric/docker-compose.yml index 6b9771e1c1..8e1688dff3 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_flexric/docker-compose.yml +++ b/ci-scripts/yaml_files/5g_rfsimulator_flexric/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-flexric: - image: ${REGISTRY:-oaisoftwarealliance}/oai-flexric:${FLEXRIC_TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-flexric:${FLEXRIC_TAG:-develop} container_name: oai-flexric networks: public_net: @@ -13,7 +13,7 @@ services: timeout: 5s xapp-rc-moni: - image: ${REGISTRY:-oaisoftwarealliance}/oai-flexric:${FLEXRIC_TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-flexric:${FLEXRIC_TAG:-develop} command: "stdbuf -o0 /usr/local/flexric/xApp/c/monitor/xapp_rc_moni" container_name: xapp-rc-moni depends_on: @@ -30,7 +30,7 @@ services: timeout: 5s xapp-kpm-moni: - image: ${REGISTRY:-oaisoftwarealliance}/oai-flexric:${FLEXRIC_TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-flexric:${FLEXRIC_TAG:-develop} command: "stdbuf -o0 /usr/local/flexric/xApp/c/monitor/xapp_kpm_moni" container_name: xapp-kpm-moni depends_on: @@ -47,7 +47,7 @@ services: timeout: 5s xapp-kpm-rc: - image: ${REGISTRY:-oaisoftwarealliance}/oai-flexric:${FLEXRIC_TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-flexric:${FLEXRIC_TAG:-develop} command: "stdbuf -o0 /usr/local/flexric/xApp/c/kpm_rc/xapp_kpm_rc" container_name: xapp-kpm-rc depends_on: @@ -64,7 +64,7 @@ services: timeout: 5s xapp-gtp-mac-rlc-pdcp-moni: - image: ${REGISTRY:-oaisoftwarealliance}/oai-flexric:${FLEXRIC_TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-flexric:${FLEXRIC_TAG:-develop} command: "stdbuf -o0 /usr/local/flexric/xApp/c/monitor/xapp_gtp_mac_rlc_pdcp_moni" container_name: xapp-gtp-mac-rlc-pdcp-moni depends_on: @@ -81,7 +81,7 @@ services: timeout: 5s oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} privileged: true container_name: rfsim5g-oai-gnb environment: @@ -101,7 +101,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} privileged: true container_name: rfsim5g-oai-nr-ue environment: @@ -120,7 +120,7 @@ services: retries: 5 oai-nr-ue2: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} privileged: true container_name: rfsim5g-oai-nr-ue2 environment: diff --git a/ci-scripts/yaml_files/5g_rfsimulator_fr2_32prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_fr2_32prb/docker-compose.yaml index 13d91d9090..7ae33004f5 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_fr2_32prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_fr2_32prb/docker-compose.yaml @@ -91,7 +91,7 @@ services: retries: 5 oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-gnb cap_drop: - ALL @@ -112,7 +112,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL 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 9d660e0c3b..b44178a3f5 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml @@ -83,7 +83,7 @@ services: timeout: 5s retries: 5 oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-gnb cap_drop: - ALL @@ -103,7 +103,7 @@ services: timeout: 5s retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL 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 f3b75c1471..ad171109c7 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 @@ -84,7 +84,7 @@ services: retries: 5 oai-cucp-0: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-cucp-0 cap_drop: - ALL @@ -111,7 +111,7 @@ services: retries: 5 oai-cuup-0: - image: ${REGISTRY:-oaisoftwarealliance}/${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} container_name: rfsim5g-oai-cuup-0 cap_drop: - ALL @@ -139,7 +139,7 @@ services: oai-du-0: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-du-0 cap_drop: - ALL @@ -168,7 +168,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL @@ -194,7 +194,7 @@ services: retries: 5 oai-cucp-1: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-cucp-1 cap_drop: - ALL @@ -226,7 +226,7 @@ services: retries: 5 oai-cuup-1: - image: ${REGISTRY:-oaisoftwarealliance}/${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} container_name: rfsim5g-oai-cuup-1 cap_drop: - ALL @@ -253,7 +253,7 @@ services: retries: 5 oai-du-1: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-du-1 cap_drop: - ALL 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 c83060b713..731b833f08 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 @@ -90,7 +90,7 @@ services: timeout: 5s retries: 5 oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-gnb cap_drop: - ALL @@ -115,7 +115,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL 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 5469b0b247..4e209e09d1 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 @@ -87,7 +87,7 @@ services: timeout: 5s retries: 5 oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-gnb cap_drop: - ALL @@ -110,7 +110,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL diff --git a/ci-scripts/yaml_files/5g_rfsimulator_sidelink/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_sidelink/docker-compose.yaml index c04e634ca2..5d8de35ac8 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_sidelink/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_sidelink/docker-compose.yaml @@ -1,6 +1,6 @@ services: oai-nr-ue-1: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue-1 cap_drop: - ALL @@ -25,7 +25,7 @@ services: retries: 5 oai-nr-ue-2: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue-2 cap_drop: - ALL diff --git a/ci-scripts/yaml_files/5g_rfsimulator_tdd_dora/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_tdd_dora/docker-compose.yaml index 89f6562697..60484914a6 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_tdd_dora/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_tdd_dora/docker-compose.yaml @@ -1,6 +1,6 @@ services: oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-gnb cap_drop: - ALL @@ -25,7 +25,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL 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 ed1701ef86..722290aa38 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 @@ -91,7 +91,7 @@ services: retries: 5 oai-vnf: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-vnf cap_drop: - ALL @@ -115,7 +115,7 @@ services: retries: 5 oai-pnf: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: rfsim5g-oai-pnf cap_drop: - ALL @@ -139,7 +139,7 @@ services: retries: 5 oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} container_name: rfsim5g-oai-nr-ue cap_drop: - ALL diff --git a/ci-scripts/yaml_files/5g_sa_f1_b210_ho/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_f1_b210_ho/docker-compose.yml index c1c3db1607..937f7d5d65 100644 --- a/ci-scripts/yaml_files/5g_sa_f1_b210_ho/docker-compose.yml +++ b/ci-scripts/yaml_files/5g_sa_f1_b210_ho/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-cu: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: oai-cu cap_drop: - ALL @@ -26,7 +26,7 @@ services: retries: 5 oai-du0: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: oai-du0 cap_drop: - ALL @@ -58,7 +58,7 @@ services: retries: 5 oai-du1: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: oai-du1 cap_drop: - ALL diff --git a/ci-scripts/yaml_files/5g_sa_n2_ho_b210/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_n2_ho_b210/docker-compose.yml index 6704b434db..e02afae7cc 100644 --- a/ci-scripts/yaml_files/5g_sa_n2_ho_b210/docker-compose.yml +++ b/ci-scripts/yaml_files/5g_sa_n2_ho_b210/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-gnb-pci0: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: oai-gnb-pci0 cap_drop: - ALL @@ -28,7 +28,7 @@ services: retries: 5 oai-gnb-pci1: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: oai-gnb-pci1 cap_drop: - ALL diff --git a/ci-scripts/yaml_files/5g_sa_n310_2x2_100MHz/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_n310_2x2_100MHz/docker-compose.yml index 11ecb20f08..879f86ce90 100644 --- a/ci-scripts/yaml_files/5g_sa_n310_2x2_100MHz/docker-compose.yml +++ b/ci-scripts/yaml_files/5g_sa_n310_2x2_100MHz/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} network_mode: "host" container_name: oai-gnb cap_drop: diff --git a/ci-scripts/yaml_files/5g_sa_n310_2x2_60MHz/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_n310_2x2_60MHz/docker-compose.yml index 332fb23503..b5eb5dd441 100644 --- a/ci-scripts/yaml_files/5g_sa_n310_2x2_60MHz/docker-compose.yml +++ b/ci-scripts/yaml_files/5g_sa_n310_2x2_60MHz/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} network_mode: "host" container_name: oai-gnb cap_drop: diff --git a/ci-scripts/yaml_files/5g_sa_n310_4x4_60MHz/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_n310_4x4_60MHz/docker-compose.yml index d3d2ddf582..487672ae1f 100644 --- a/ci-scripts/yaml_files/5g_sa_n310_4x4_60MHz/docker-compose.yml +++ b/ci-scripts/yaml_files/5g_sa_n310_4x4_60MHz/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-vnf: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} network_mode: "host" container_name: oai-vnf cap_drop: @@ -20,7 +20,7 @@ services: timeout: 5s retries: 5 oai-pnf: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} network_mode: "host" container_name: oai-pnf cap_drop: diff --git a/ci-scripts/yaml_files/5g_sa_n310_gnb/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_n310_gnb/docker-compose.yml index 562b72561a..e1cd519d22 100644 --- a/ci-scripts/yaml_files/5g_sa_n310_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/5g_sa_n310_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} network_mode: "host" container_name: oai-gnb cap_drop: diff --git a/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml b/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml index 9fb80ad065..5db92f6ee2 100644 --- a/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml +++ b/ci-scripts/yaml_files/5g_sa_n310_nrue/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-nr-ue: - image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} network_mode: host container_name: oai-nr-ue cap_drop: diff --git a/ci-scripts/yaml_files/fr1_enb_mono_fdd_tim/docker-compose.yml b/ci-scripts/yaml_files/fr1_enb_mono_fdd_tim/docker-compose.yml index 4c40d738a4..7963f823b1 100644 --- a/ci-scripts/yaml_files/fr1_enb_mono_fdd_tim/docker-compose.yml +++ b/ci-scripts/yaml_files/fr1_enb_mono_fdd_tim/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: prod-enb-mono-fdd environment: diff --git a/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_if4.5/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_if4.5/docker-compose.yml index d15a9df172..290edf23b1 100644 --- a/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_if4.5/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_if4.5/docker-compose.yml @@ -1,6 +1,6 @@ services: rru_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-rru-fdd-05Mhz-tm1 network_mode: "host" @@ -17,7 +17,7 @@ services: timeout: 5s retries: 5 rcc_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-rcc-fdd-05Mhz-tm1 network_mode: "host" diff --git a/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_tm1/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_tm1/docker-compose.yml index 2a6e7bb51a..d3f6897534 100644 --- a/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_tm1/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_tm1/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-fdd-05Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_tm1_no_rrc_activity/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_tm1_no_rrc_activity/docker-compose.yml index 7cdc585d34..6bf2f4606f 100644 --- a/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_tm1_no_rrc_activity/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_fdd_05Mhz_tm1_no_rrc_activity/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-fdd-05Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_oai_ue_magma/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_oai_ue_magma/docker-compose.yml index c6d64cc028..a213d1900b 100644 --- a/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_oai_ue_magma/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_oai_ue_magma/docker-compose.yml @@ -1,6 +1,6 @@ services: lte_ue0: - image: ${REGISTRY:-oaisoftwarealliance}/oai-lte-ue:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-lte-ue:${TAG:-develop} privileged: true container_name: lte-b200-ue-fdd-10Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1/docker-compose.yml index 14d1045de8..3cbf927f8e 100644 --- a/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-fdd-10Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1_cdrx/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1_cdrx/docker-compose.yml index d1fcd91850..38c14472b6 100644 --- a/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1_cdrx/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1_cdrx/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-fdd-10Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1_magma/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1_magma/docker-compose.yml index 6b1b583863..c1c9c4b531 100644 --- a/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1_magma/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_fdd_10Mhz_tm1_magma/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-fdd-10Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_b200_fdd_20Mhz_tm1/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_fdd_20Mhz_tm1/docker-compose.yml index a36afb957e..e027c521d7 100644 --- a/ci-scripts/yaml_files/lte_b200_fdd_20Mhz_tm1/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_fdd_20Mhz_tm1/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-fdd-20Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_if4.5/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_if4.5/docker-compose.yml index 0311390ab7..56c730083a 100644 --- a/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_if4.5/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_if4.5/docker-compose.yml @@ -1,6 +1,6 @@ services: rru_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-rru-tdd-05Mhz-tm1 network_mode: "host" @@ -17,7 +17,7 @@ services: timeout: 5s retries: 5 rcc_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-rcc-tdd-05Mhz-tm1 network_mode: "host" diff --git a/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_tm1/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_tm1/docker-compose.yml index 68f48d7087..9ce6bf3609 100644 --- a/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_tm1/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_tm1/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-tdd-05Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_tm2/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_tm2/docker-compose.yml index b11b268129..d462baac14 100644 --- a/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_tm2/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_tdd_05Mhz_tm2/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-tdd-05Mhz-tm2 environment: diff --git a/ci-scripts/yaml_files/lte_b200_tdd_10Mhz_tm1/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_tdd_10Mhz_tm1/docker-compose.yml index 7934784cdc..025c5ad26b 100644 --- a/ci-scripts/yaml_files/lte_b200_tdd_10Mhz_tm1/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_tdd_10Mhz_tm1/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-tdd-10Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_b200_tdd_20Mhz_tm1/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_tdd_20Mhz_tm1/docker-compose.yml index 0bca3bc43a..748525aa82 100644 --- a/ci-scripts/yaml_files/lte_b200_tdd_20Mhz_tm1/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_tdd_20Mhz_tm1/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-tdd-20Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_b200_tdd_20Mhz_tm1_default_scheduler/docker-compose.yml b/ci-scripts/yaml_files/lte_b200_tdd_20Mhz_tm1_default_scheduler/docker-compose.yml index 953cce7ffc..dfdd4ed877 100644 --- a/ci-scripts/yaml_files/lte_b200_tdd_20Mhz_tm1_default_scheduler/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_b200_tdd_20Mhz_tm1_default_scheduler/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: lte-b200-enb-tdd-20Mhz-tm1 environment: diff --git a/ci-scripts/yaml_files/lte_n3xx_tdd_2x2_tm1/docker-compose.yml b/ci-scripts/yaml_files/lte_n3xx_tdd_2x2_tm1/docker-compose.yml index 5ac5b93d14..0aefaf5ef0 100644 --- a/ci-scripts/yaml_files/lte_n3xx_tdd_2x2_tm1/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_n3xx_tdd_2x2_tm1/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-enb: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: oai-enb environment: diff --git a/ci-scripts/yaml_files/lte_n3xx_tdd_2x2_tm2/docker-compose.yml b/ci-scripts/yaml_files/lte_n3xx_tdd_2x2_tm2/docker-compose.yml index 929c9633c7..db6215a148 100644 --- a/ci-scripts/yaml_files/lte_n3xx_tdd_2x2_tm2/docker-compose.yml +++ b/ci-scripts/yaml_files/lte_n3xx_tdd_2x2_tm2/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-enb: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: oai-enb environment: diff --git a/ci-scripts/yaml_files/nsa_b200_enb/docker-compose.yml b/ci-scripts/yaml_files/nsa_b200_enb/docker-compose.yml index 76790c44f9..6dfe229265 100644 --- a/ci-scripts/yaml_files/nsa_b200_enb/docker-compose.yml +++ b/ci-scripts/yaml_files/nsa_b200_enb/docker-compose.yml @@ -1,6 +1,6 @@ services: enb_mono_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/oai-enb:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-enb:${TAG:-develop} privileged: true container_name: nsa-b200-enb ulimits: diff --git a/ci-scripts/yaml_files/nsa_b200_gnb/docker-compose.yml b/ci-scripts/yaml_files/nsa_b200_gnb/docker-compose.yml index bb6576054e..f104735fa0 100644 --- a/ci-scripts/yaml_files/nsa_b200_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/nsa_b200_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: gnb_mono_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: nsa-b200-gnb cap_drop: - ALL diff --git a/ci-scripts/yaml_files/sa_aw2s_2x2_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_aw2s_2x2_gnb/docker-compose.yml index a1baef974a..fadeea96ef 100644 --- a/ci-scripts/yaml_files/sa_aw2s_2x2_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_aw2s_2x2_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/oai-gnb-aw2s:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-gnb-aw2s:${TAG:-develop} network_mode: "host" container_name: oai-gnb-aw2s cap_drop: diff --git a/ci-scripts/yaml_files/sa_aw2s_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_aw2s_gnb/docker-compose.yml index 9530b3cd5c..54bf6cec00 100644 --- a/ci-scripts/yaml_files/sa_aw2s_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_aw2s_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/oai-gnb-aw2s:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-gnb-aw2s:${TAG:-develop} network_mode: "host" container_name: oai-gnb-aw2s cap_drop: diff --git a/ci-scripts/yaml_files/sa_b200_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_b200_gnb/docker-compose.yml index 7fe7db5317..d57ec4b5bd 100644 --- a/ci-scripts/yaml_files/sa_b200_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_b200_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: gnb_mono_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: sa-b200-gnb cap_drop: - ALL diff --git a/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml index c20a43c0e1..bbcd04238c 100644 --- a/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: gnb_cucp: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: sa-cucp-gnb cap_drop: - ALL @@ -25,7 +25,7 @@ services: retries: 5 gnb_cuup: - image: ${REGISTRY:-oaisoftwarealliance}/${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop} container_name: sa-cuup-gnb cap_drop: - ALL @@ -51,7 +51,7 @@ services: retries: 5 gnb_du_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: sa-du-b200-gnb cap_drop: - ALL diff --git a/ci-scripts/yaml_files/sa_f1_b200_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_f1_b200_gnb/docker-compose.yml index 2c1ab75e92..740b0eff1a 100644 --- a/ci-scripts/yaml_files/sa_f1_b200_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_f1_b200_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: gnb_cu: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: sa-cu-gnb cap_drop: - ALL @@ -25,7 +25,7 @@ services: retries: 5 gnb_du_fdd: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: sa-du-b200-gnb cap_drop: - ALL diff --git a/ci-scripts/yaml_files/sa_fhi_7.2_benetel550_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_fhi_7.2_benetel550_gnb/docker-compose.yml index b70229ac94..8c38739994 100644 --- a/ci-scripts/yaml_files/sa_fhi_7.2_benetel550_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_fhi_7.2_benetel550_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/oai-gnb-fhi72:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-gnb-fhi72:${TAG:-develop} cap_add: - SYS_ADMIN - IPC_LOCK diff --git a/ci-scripts/yaml_files/sa_fhi_7.2_metanoia_2x2_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_fhi_7.2_metanoia_2x2_gnb/docker-compose.yml index f08859a516..3e29c33ff9 100644 --- a/ci-scripts/yaml_files/sa_fhi_7.2_metanoia_2x2_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_fhi_7.2_metanoia_2x2_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/oai-gnb-fhi72:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-gnb-fhi72:${TAG:-develop} cap_add: - IPC_LOCK - SYS_NICE diff --git a/ci-scripts/yaml_files/sa_fhi_7.2_vvdn_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_fhi_7.2_vvdn_gnb/docker-compose.yml index ff06893fa0..2e6c044fcb 100644 --- a/ci-scripts/yaml_files/sa_fhi_7.2_vvdn_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_fhi_7.2_vvdn_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: oai-gnb: - image: ${REGISTRY:-oaisoftwarealliance}/oai-gnb-fhi72:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}oai-gnb-fhi72:${TAG:-develop} cap_add: - IPC_LOCK - SYS_NICE diff --git a/ci-scripts/yaml_files/sa_gnb_aerial/docker-compose.yaml b/ci-scripts/yaml_files/sa_gnb_aerial/docker-compose.yaml index c9f3d90131..f04beeb6c2 100644 --- a/ci-scripts/yaml_files/sa_gnb_aerial/docker-compose.yaml +++ b/ci-scripts/yaml_files/sa_gnb_aerial/docker-compose.yaml @@ -34,7 +34,7 @@ services: retries: 5 oai-gnb-aerial: cpuset: "13-14" - image: ${REGISTRY:-oaisoftwarealliance}/oai-gnb-aerial:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb-aerial}:${TAG:-develop} depends_on: nv-cubb: condition: service_healthy diff --git a/ci-scripts/yaml_files/sa_gnb_aerial_ul/docker-compose.yaml b/ci-scripts/yaml_files/sa_gnb_aerial_ul/docker-compose.yaml index 077c58c67c..c0b3e6f81d 100644 --- a/ci-scripts/yaml_files/sa_gnb_aerial_ul/docker-compose.yaml +++ b/ci-scripts/yaml_files/sa_gnb_aerial_ul/docker-compose.yaml @@ -34,7 +34,7 @@ services: retries: 5 oai-gnb-aerial: cpuset: "13-16" - image: ${REGISTRY:-oaisoftwarealliance}/oai-gnb-aerial:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb-aerial}:${TAG:-develop} depends_on: nv-cubb: condition: service_healthy diff --git a/ci-scripts/yaml_files/sa_sc_b200_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_sc_b200_gnb/docker-compose.yml index f0b86f4c33..51571332bd 100644 --- a/ci-scripts/yaml_files/sa_sc_b200_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_sc_b200_gnb/docker-compose.yml @@ -1,6 +1,6 @@ services: gnb_mono_tdd: - image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop} + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} container_name: sa-b200-gnb cap_drop: - ALL From aff3c9872faca1a5671a42200bef228f16335cc9 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Thu, 30 Oct 2025 10:11:12 +0100 Subject: [PATCH 148/275] CI: align REGISTRY env var with updated image path format Following the change to use: image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} the REGISTRY variable must now include the trailing slash. --- ci-scripts/cls_containerize.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index c97bb8d4d1..3cebcb24d7 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -119,7 +119,7 @@ def ExistEnvFilePrint(ssh, wd, prompt='env vars in existing'): def WriteEnvFile(ssh, services, wd, tag, flexric_tag): ret = ssh.run(f'cat {wd}/.env', silent=True, reportNonZero=False) - registry = "oai-ci" # pull_images() gives us this registry path + registry = "oai-ci/" # pull_images() gives us this registry path envs = {"REGISTRY":registry, "TAG": tag, "FLEXRIC_TAG": flexric_tag} if ret.returncode == 0: # it exists, we have to update # transforms env file to dictionary @@ -134,7 +134,8 @@ def WriteEnvFile(ssh, services, wd, tag, flexric_tag): # or -asan images. We need to detect which kind we did pull. fullImageName = GetImageName(ssh, svc, f"{wd}/docker-compose.y*ml") image = fullImageName.split("/")[-1].split(":")[0] - checkimg = f"{registry}/{image}-asan:{tag}" + # registry now includes the trailing slash ("oai-ci/") + checkimg = f"{registry}{image}-asan:{tag}" ret = ssh.run(f'docker image inspect {checkimg}', reportNonZero=False) if ret.returncode == 0: logging.info(f"detected pulled image {checkimg}") From 5690aa0fe3700f5ca1e0a0dae14ccfbb51799da9 Mon Sep 17 00:00:00 2001 From: rakesh mundlamuri Date: Fri, 22 Aug 2025 15:46:42 +0530 Subject: [PATCH 149/275] first_half index correction in PRS channel estimation while copying memory Previously, we were using int16_t for the variable ch_tmp which was later harmonized to use c16_t while leaving <<1. This introduced the bug while accessing the address of the element at first_half of the ch_tmp variable. --- .../nr_dl_channel_estimation.c | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index 6a49a17608..0d9399ce27 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -315,10 +315,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id, //Start pilot c16_t ch = c16MulConjShift(*pil, *rxF, 15); - multadd_real_vector_complex_scalar(fl, - ch, - ch_tmp, - 16); + multadd_real_vector_complex_scalar(fl, ch, ch_tmp, 16); // SNR & RSRP estimation rsrp += squaredMod(*rxF); @@ -331,10 +328,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id, k = (k+prs_cfg->CombSize) % frame_params->ofdm_symbol_size; rxF = &rxdataF[rxAnt][l * frame_params->ofdm_symbol_size + k]; ch = c16MulConjShift(*pil, *rxF, 15); - multadd_real_vector_complex_scalar(fml, - ch, - ch_tmp, - 16); + multadd_real_vector_complex_scalar(fml, ch, ch_tmp, 16); // SNR & RSRP estimation rsrp += squaredMod(*rxF); @@ -352,10 +346,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id, for(int pIdx = 2; pIdx < num_pilots-2; pIdx++) { c16_t ch = c16MulConjShift(*pil, *rxF, 15); - multadd_real_vector_complex_scalar(fmm, - ch, - ch_tmp, - 16); + multadd_real_vector_complex_scalar(fmm, ch, ch_tmp, 16); // SNR & RSRP estimation rsrp += squaredMod(*rxF); @@ -372,10 +363,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id, //End pilot ch = c16MulConjShift(*pil, *rxF, 15); - multadd_real_vector_complex_scalar(fmr, - ch, - ch_tmp, - 16); + multadd_real_vector_complex_scalar(fmr, ch, ch_tmp, 16); // SNR & RSRP estimation rsrp += squaredMod(*rxF); @@ -388,10 +376,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id, k = (k+prs_cfg->CombSize) % frame_params->ofdm_symbol_size; rxF = &rxdataF[rxAnt][l * frame_params->ofdm_symbol_size + k]; ch = c16MulConjShift(*pil, *rxF, 15); - multadd_real_vector_complex_scalar(fr, - ch, - ch_tmp, - 16); + multadd_real_vector_complex_scalar(fr, ch, ch_tmp, 16); // SNR & RSRP estimation rsrp += squaredMod(*rxF); @@ -437,7 +422,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id, if(first_half > 0) memcpy((int16_t *)&chF_interpol[rxAnt][start_offset], &ch_tmp[0], first_half * sizeof(int32_t)); if(second_half > 0) - memcpy((int16_t *)&chF_interpol[rxAnt][0], &ch_tmp[first_half << 1], second_half * sizeof(int32_t)); + memcpy((int16_t *)&chF_interpol[rxAnt][0], &ch_tmp[first_half], second_half * sizeof(int32_t)); // Convert to time domain freq2time(NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size, From 825c6803adac44a3e4d3315f6b90a2df052e5f26 Mon Sep 17 00:00:00 2001 From: rakesh mundlamuri Date: Tue, 26 Aug 2025 12:59:50 +0530 Subject: [PATCH 150/275] harmonize IQ samples in prs channel estimation to use c16_t --- .../nr_dl_channel_estimation.c | 18 +++++++++--------- openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index 0d9399ce27..45060eaacd 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -47,12 +47,12 @@ #define NO_INTERP 1 /* Generic function to find the peak of channel estimation buffer */ -void peak_estimator(int32_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t *peak_val, int32_t mean_val) +void peak_estimator(c16_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t *peak_val, int32_t mean_val) { int32_t max_val = 0, max_idx = 0, abs_val = 0; for(int k = 0; k < buf_len; k++) { - abs_val = squaredMod(((c16_t*)buffer)[k]); + abs_val = squaredMod(buffer[k]); if(abs_val > max_val) { max_val = abs_val; @@ -83,8 +83,8 @@ int nr_prs_channel_estimation(uint8_t gNB_id, prs_config_t *prs_cfg = &ue->prs_vars[gNB_id]->prs_resource[rsc_id].prs_cfg; prs_meas_t **prs_meas = ue->prs_vars[gNB_id]->prs_resource[rsc_id].prs_meas; c16_t ch_tmp_buf[ ue->frame_parms.ofdm_symbol_size] __attribute__((aligned(32))); - int32_t chF_interpol[frame_params->nb_antennas_rx][NR_PRS_IDFT_OVERSAMP_FACTOR*ue->frame_parms.ofdm_symbol_size] __attribute__((aligned(32))); - int32_t chT_interpol[frame_params->nb_antennas_rx][NR_PRS_IDFT_OVERSAMP_FACTOR*ue->frame_parms.ofdm_symbol_size] __attribute__((aligned(32))); + c16_t chF_interpol[frame_params->nb_antennas_rx][NR_PRS_IDFT_OVERSAMP_FACTOR*ue->frame_parms.ofdm_symbol_size] __attribute__((aligned(32))); + c16_t chT_interpol[frame_params->nb_antennas_rx][NR_PRS_IDFT_OVERSAMP_FACTOR*ue->frame_parms.ofdm_symbol_size] __attribute__((aligned(32))); memset(ch_tmp_buf,0,sizeof(ch_tmp_buf)); memset(chF_interpol,0,sizeof(chF_interpol)); memset(chT_interpol,0,sizeof(chF_interpol)); @@ -420,9 +420,9 @@ int nr_prs_channel_estimation(uint8_t gNB_id, // Place PRS channel estimates in FFT shifted format if(first_half > 0) - memcpy((int16_t *)&chF_interpol[rxAnt][start_offset], &ch_tmp[0], first_half * sizeof(int32_t)); + memcpy(&chF_interpol[rxAnt][start_offset], &ch_tmp[0], first_half * sizeof(c16_t)); if(second_half > 0) - memcpy((int16_t *)&chF_interpol[rxAnt][0], &ch_tmp[first_half], second_half * sizeof(int32_t)); + memcpy(&chF_interpol[rxAnt][0], &ch_tmp[first_half], second_half * sizeof(c16_t)); // Convert to time domain freq2time(NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size, @@ -481,7 +481,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id, // T tracer dump T(T_UE_PHY_INPUT_SIGNAL, T_INT(gNB_id), T_INT(proc->frame_rx), T_INT(proc->nr_slot_rx), - T_INT(rxAnt), T_BUFFER(&rxdataF[rxAnt][0], frame_params->samples_per_slot_wCP*sizeof(int32_t))); + T_INT(rxAnt), T_BUFFER(&rxdataF[rxAnt][0], frame_params->samples_per_slot_wCP*sizeof(c16_t))); T(T_UE_PHY_DL_CHANNEL_ESTIMATE_FREQ, T_INT(gNB_id), @@ -489,7 +489,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id, T_INT(proc->frame_rx), T_INT(proc->nr_slot_rx), T_INT(rxAnt), - T_BUFFER(&chF_interpol[rxAnt][0], NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size * sizeof(int32_t))); + T_BUFFER(&chF_interpol[rxAnt][0], NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size * sizeof(c16_t))); T(T_UE_PHY_DL_CHANNEL_ESTIMATE, T_INT(gNB_id), @@ -497,7 +497,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id, T_INT(proc->frame_rx), T_INT(proc->nr_slot_rx), T_INT(rxAnt), - T_BUFFER(&chT_interpol[rxAnt][0], NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size * sizeof(int32_t))); + T_BUFFER(&chT_interpol[rxAnt][0], NR_PRS_IDFT_OVERSAMP_FACTOR * frame_params->ofdm_symbol_size * sizeof(c16_t))); } return(0); diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h index 85b8999118..d0d868197c 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h @@ -39,7 +39,7 @@ int nr_prs_channel_estimation(uint8_t gNB_id, c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]); /* Generic function to find the peak of channel estimation buffer */ -void peak_estimator(int32_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t *peak_val, int32_t mean_val); +void peak_estimator(c16_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t *peak_val, int32_t mean_val); /*! \brief This function performs channel estimation including frequency and temporal interpolation From a72abe3cc8c25763859f28700fa523d0de41f429 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 19 Sep 2025 12:31:59 +0200 Subject: [PATCH 151/275] Refactor dl_toa setting to separate function --- .../NR_UE_ESTIMATION/nr_dl_channel_estimation.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index 45060eaacd..4151752f11 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -71,6 +71,11 @@ void peak_estimator(c16_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t * } } +void set_prs_dl_toa(prs_meas_t *prs_meas, float dl_toa) +{ + prs_meas->dl_toa = dl_toa; +} + int nr_prs_channel_estimation(uint8_t gNB_id, uint8_t rsc_id, uint8_t rep_num, @@ -448,9 +453,9 @@ int nr_prs_channel_estimation(uint8_t gNB_id, prs_meas[rxAnt]->slot = proc->nr_slot_rx; prs_meas[rxAnt]->rxAnt_idx = rxAnt; prs_meas[rxAnt]->dl_aoa = rsc_id; - prs_meas[rxAnt]->dl_toa = prs_toa / (float)NR_PRS_IDFT_OVERSAMP_FACTOR; - if ((frame_params->ofdm_symbol_size - prs_meas[rxAnt]->dl_toa) < frame_params->ofdm_symbol_size / 2) - prs_meas[rxAnt]->dl_toa -= (frame_params->ofdm_symbol_size); + float dl_toa = prs_toa / (float)NR_PRS_IDFT_OVERSAMP_FACTOR; + if ((frame_params->ofdm_symbol_size - dl_toa) < frame_params->ofdm_symbol_size / 2) + dl_toa -= (frame_params->ofdm_symbol_size); LOG_I(PHY, "[gNB %d][rsc %d][Rx %d][sfn %d][slot %d] DL PRS ToA ==> %.1f / %d samples, peak channel power %.1f dBm, SNR %+.1f dB, rsrp %+.1f dBm\n", gNB_id, @@ -458,12 +463,14 @@ int nr_prs_channel_estimation(uint8_t gNB_id, rxAnt, proc->frame_rx, proc->nr_slot_rx, - prs_meas[rxAnt]->dl_toa, + dl_toa, frame_params->ofdm_symbol_size, ch_pwr_dbm, prs_meas[rxAnt]->snr, prs_meas[rxAnt]->rsrp_dBm); + set_prs_dl_toa(prs_meas[rxAnt], dl_toa); + #ifdef DEBUG_PRS_CHEST sprintf(filename, "%s%i%s", "PRSpilot_", rxAnt, ".m"); LOG_M(filename, "prs_loc", mod_prs, num_pilots, 1, 1); From b8e249fc4a61aea48918577eaf21a327b07c9b7a Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Mon, 3 Nov 2025 13:09:24 +0100 Subject: [PATCH 152/275] CI: Add new Jenkinsfiles Created Jenkinsfiles: - Jenkinsfile: for stadard test pipelines - Jenkinsfile-oc: for deployments with OC These 2 Jenkinsfiles covers all current test pipelines. --- ci-scripts/Jenkinsfile | 195 +++++++++++++++++++++++++++++++++++++ ci-scripts/Jenkinsfile-oc | 199 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 394 insertions(+) create mode 100644 ci-scripts/Jenkinsfile create mode 100644 ci-scripts/Jenkinsfile-oc diff --git a/ci-scripts/Jenkinsfile b/ci-scripts/Jenkinsfile new file mode 100644 index 0000000000..329caa5834 --- /dev/null +++ b/ci-scripts/Jenkinsfile @@ -0,0 +1,195 @@ +#!/bin/groovy +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +def pythonExecutor = params.pythonExecutor +def testXMLFile = params.pythonTestXmlFile +def mainPythonAllXmlFiles = "" +def buildStageStatus = true +def JOB_TIMESTAMP + +// Choose test stage name or fallback default +def testStageName = params.pipelineTestStageName != null ? params.pipelineTestStageName : 'Template Test Stage' + +// Name of the resource +def lockResources = [] +if (params.LockResources != null && params.LockResources.trim().length() > 0) + params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} + +// Global Parameters. Normally they should be populated when the master job +// triggers the slave job with parameters +def sourceRepository = params.eNB_Repository ?: env.GIT_URL +def sourceBranch = params.eNB_Branch ?: env.GIT_BRANCH +def targetRepository = params.Target_Repository ?: sourceRepository +def targetBranch = params.eNB_TargetBranch ?: 'develop' +def commitID = params.eNB_CommitID ?: env.GIT_COMMIT +def is_MR = params.eNB_mergeRequest ?: false + +def flexricOption = "" + +//------------------------------------------------------------------------------- +// Pipeline start +pipeline { + agent { + label pythonExecutor + } + options { + timestamps() + ansiColor('xterm') + lock(extra: lockResources) + } + + stages { + stage("Build Init") { + steps { + script { + echo '\u2705 \u001B[94mBuild Init\u001B[0m' + JOB_TIMESTAMP = sh(returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"').trim() + } + // update the build name and description + buildName "${params.eNB_MR}" + buildDescription "Branch : ${sourceBranch}" + } + } + stage ('Verify Parameters') { + steps { + script { + echo '\u2705 \u001B[94mVerify Parameters\u001B[0m' + def allParametersPresent = true + + if (params.LockResources == null) { + echo "no LockResources given" + allParametersPresent = false + } + if (params.eNB_SourceCodePath == null) { + echo "no eNB_SourceCodePath given" + allParametersPresent = false + } + + if (params.Flexric_Tag != null) { + echo "This pipeline is configured to run with a FlexRIC deployment." + echo "Appending FlexRicTag option to the list of options" + flexricOption = "--FlexRicTag=${params.Flexric_Tag}" + echo "Using new Flexric option: ${flexricOption}" + } + + echo "CI executor node : ${pythonExecutor}" + echo "Source Repository : ${sourceRepository}" + echo "Source Branch : ${sourceBranch}" + echo "Target Repository : ${targetRepository}" + echo "Target Branch : ${targetBranch}" + echo "Commit ID : ${commitID}" + + if (allParametersPresent) { + echo '\u2705 \u001B[94m All parameters are present\u001B[0m' + if (is_MR) { + sh "git fetch" + sh "./ci-scripts/doGitLabMerge.sh --src-branch ${sourceBranch} --src-commit ${commitID} --target-branch ${targetBranch} --target-commit latest" + } else { + sh "git fetch" + sh "git checkout -f ${commitID}" + } + } else { + echo "\u274C Some parameters are missing" + sh "./ci-scripts/fail.sh" + } + } + } + } + + stage ("Deploy and Test") { + steps { + script { + dir ('ci-scripts') { + echo "\u2705 \u001B[94m Deploy and Test: ${testStageName}\u001B[0m" + String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") + for (xmlFile in myXmlTestSuite) { + if (fileExists(xmlFile)) { + mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " + echo "Test XML file : ${xmlFile}" + } else { + echo "Test XML file ${xmlFile}: no such file" + } + } + sh """ + python3 main.py --mode=InitiateHtml --ranRepository=${targetRepository} \ + --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ + --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ + ${flexricOption} ${mainPythonAllXmlFiles} + """ + for (xmlFile in myXmlTestSuite) { + if (fileExists(xmlFile)) { + try { + timeout (time: 60, unit: 'MINUTES') { + sh """ + python3 main.py --mode=TesteNB --ranRepository=${targetRepository} \ + --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ + --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ + --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} \ + ${flexricOption} + """ + } + } catch (Exception e) { + currentBuild.result = 'FAILURE' + buildStageStatus = false + } + } + } + sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" + } + } + } + } + stage ("Log Collection") { + steps { + script { + echo '\u2705 \u001B[94mLog Collection\u001B[0m' + dir ('ci-scripts') { + // Zipping all archived log files + sh "mkdir test_logs" + sh "mv *.log* test_logs || true" + // Zip all log files matching cmake_targets/{*.log*,log/*} into test_logs_XXXX.zip + if (fileExists('../cmake_targets/log')) { + sh "mv ../cmake_targets/log/* test_logs || true" + } + sh "zip -r -qq test_logs_${env.BUILD_ID}.zip *test_log*/" + sh "rm -rf *test_log*/" + if (fileExists("test_logs_${env.BUILD_ID}.zip")) { + archiveArtifacts artifacts: "test_logs_${env.BUILD_ID}.zip" + } + if (fileExists("test_results.html")) { + def reportName = "test_results_${env.JOB_NAME}.html" + sh "mv test_results.html ${reportName}" + sh """ + sed -i -e 's#TEMPLATE_BUILD_TIME#${JOB_TIMESTAMP}#' \ + -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' \ + -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' \ + -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' \ + -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' ${reportName} + """ + archiveArtifacts artifacts: "${reportName}" + } + } + } + } + } + } +} diff --git a/ci-scripts/Jenkinsfile-oc b/ci-scripts/Jenkinsfile-oc new file mode 100644 index 0000000000..01a8022c12 --- /dev/null +++ b/ci-scripts/Jenkinsfile-oc @@ -0,0 +1,199 @@ +#!/bin/groovy +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +def pythonExecutor = params.pythonExecutor +def testXMLFile = params.pythonTestXmlFile +def mainPythonAllXmlFiles = "" +def buildStageStatus = true +def JOB_TIMESTAMP + +// Choose test stage name or fallback default +def testStageName = params.pipelineTestStageName != null ? params.pipelineTestStageName : 'Template Test Stage' + +// Name of the resource +def lockResources = [] +if (params.LockResources != null && params.LockResources.trim().length() > 0) + params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} + +// Global Parameters. Normally they should be populated when the master job +// triggers the slave job with parameters + +def sourceRepository = params.eNB_Repository ?: env.GIT_URL +def sourceBranch = params.eNB_Branch ?: env.GIT_BRANCH +def targetRepository = params.Target_Repository ?: sourceRepository +def targetBranch = params.eNB_TargetBranch ?: 'develop' +def commitID = params.eNB_CommitID ?: env.GIT_COMMIT +def is_MR = params.eNB_mergeRequest ?: false + +//------------------------------------------------------------------------------- +// Pipeline start +pipeline { + agent { + label pythonExecutor + } + options { + timestamps() + ansiColor('xterm') + lock(extra: lockResources) + } + + stages { + stage("Build Init") { + steps { + script { + echo '\u2705 \u001B[94mBuild Init\u001B[0m' + JOB_TIMESTAMP = sh(returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"').trim() + } + // update the build name and description + buildName "${params.eNB_MR}" + buildDescription "Branch : ${sourceBranch}" + } + } + stage ('Verify Parameters') { + steps { + script { + echo '\u2705 \u001B[94mVerify Parameters\u001B[0m' + def allParametersPresent = true + + if (params.LockResources == null) { + echo "no LockResources given" + allParametersPresent = false + } + if (params.eNB_SourceCodePath == null) { + echo "no eNB_SourceCodePath given" + allParametersPresent = false + } + if (params.OC_Credentials == null) { + echo "no OC_Credentials given" + allParametersPresent = false + } + if (params.OC_ProjectName == null) { + echo "no OC_ProjectName given" + allParametersPresent = false + } + + echo "CI executor node : ${pythonExecutor}" + echo "Source Repository : ${sourceRepository}" + echo "Source Branch : ${sourceBranch}" + echo "Target Repository : ${targetRepository}" + echo "Target Branch : ${targetBranch}" + echo "Commit ID : ${commitID}" + + if (allParametersPresent) { + echo '\u2705 \u001B[94m All parameters are present\u001B[0m' + if (is_MR) { + sh "git fetch" + sh "./ci-scripts/doGitLabMerge.sh --src-branch ${sourceBranch} --src-commit ${commitID} --target-branch ${targetBranch} --target-commit latest" + } else { + sh "git fetch" + sh "git checkout -f ${commitID}" + } + } else { + echo "\u274C Some parameters are missing" + sh "./ci-scripts/fail.sh" + } + } + } + } + + stage ("Deploy and Test") { + steps { + script { + dir ('ci-scripts') { + echo "\u2705 \u001B[94m Deploy and Test: ${testStageName}\u001B[0m" + String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") + for (xmlFile in myXmlTestSuite) { + if (fileExists(xmlFile)) { + mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " + echo "Test XML file : ${xmlFile}" + } else { + echo "Test XML file ${xmlFile}: no such file" + } + } + withCredentials([ + usernamePassword(credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password') + ]) { + sh """ + python3 main.py --mode=InitiateHtml --ranRepository=${targetRepository} \ + --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ + --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ + ${mainPythonAllXmlFiles} + """ + for (xmlFile in myXmlTestSuite) { + if (fileExists(xmlFile)) { + try { + timeout (time: 60, unit: 'MINUTES') { + sh """ + python3 main.py --mode=TesteNB --ranRepository=${targetRepository} \ + --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ + --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ + --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} \ + --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName} + """ + } + } catch (Exception e) { + currentBuild.result = 'FAILURE' + buildStageStatus = false + } + } + } + sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" + } + } + } + } + } + stage ("Log Collection") { + steps { + script { + echo '\u2705 \u001B[94mLog Collection\u001B[0m' + dir ('ci-scripts') { + // Zipping all archived log files + sh "mkdir test_logs" + sh "mv *.log* test_logs || true" + // Zip all log files matching cmake_targets/{*.log*,log/*} into test_logs_XXXX.zip + if (fileExists('../cmake_targets/log')) { + sh "mv ../cmake_targets/log/* test_logs || true" + } + sh "zip -r -qq test_logs_${env.BUILD_ID}.zip *test_log*/" + sh "rm -rf *test_log*/" + if (fileExists("test_logs_${env.BUILD_ID}.zip")) { + archiveArtifacts artifacts: "test_logs_${env.BUILD_ID}.zip" + } + if (fileExists("test_results.html")) { + def reportName = "test_results_${env.JOB_NAME}.html" + sh "mv test_results.html ${reportName}" + sh """ + sed -i -e 's#TEMPLATE_BUILD_TIME#${JOB_TIMESTAMP}#' \ + -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' \ + -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' \ + -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' \ + -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' ${reportName} + """ + archiveArtifacts artifacts: "${reportName}" + } + } + } + } + } + } +} From a5689f5b8c90a35c13868606b2ba9966bfea5bbd Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 14:18:37 +0100 Subject: [PATCH 153/275] CI: Remove unused code --- ci-scripts/args_parse.py | 6 +----- ci-scripts/main.py | 19 +------------------ 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/ci-scripts/args_parse.py b/ci-scripts/args_parse.py index 4953f049fb..3a3ea52fa0 100644 --- a/ci-scripts/args_parse.py +++ b/ci-scripts/args_parse.py @@ -43,10 +43,6 @@ import constants as CONST def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER): - - py_param_file_present = False - py_params={} - force_local = False while len(argvs) > 1: myArgv = argvs.pop(1) # 0th is this file's name @@ -172,4 +168,4 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER): HELP.GenericHelp(CONST.Version) sys.exit('Invalid Parameter: ' + myArgv) - return py_param_file_present, py_params, mode, force_local + return mode, force_local diff --git a/ci-scripts/main.py b/ci-scripts/main.py index ac4ac2c3be..873976a8db 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -85,14 +85,6 @@ def CheckClassValidity(xml_class_list,action,id): resp=True return resp -#assigning parameters to object instance attributes (even if the attributes do not exist !!) -def AssignParams(params_dict): - - for key,value in params_dict.items(): - setattr(CiTestObj, key, value) - setattr(RAN, key, value) - setattr(HTML, key, value) - def ExecuteActionWithParam(action, ctx): global RAN global HTML @@ -399,16 +391,7 @@ CLUSTER = cls_cluster.Cluster() import args_parse # Force local execution, move all execution targets to localhost force_local = False -py_param_file_present, py_params, mode, force_local = args_parse.ArgsParse(sys.argv,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER) - - - -#----------------------------------------------------------- -# TEMPORARY params management (UNUSED) -#----------------------------------------------------------- -#temporary solution for testing: -if py_param_file_present == True: - AssignParams(py_params) +mode, force_local = args_parse.ArgsParse(sys.argv,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER) #----------------------------------------------------------- # mode amd XML class (action) analysis From 9e0540fbed97466cbf89130411d34c136ca17035 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 14:28:28 +0100 Subject: [PATCH 154/275] Simplify node management in CI Some CI tasks/commands like Iperf(), Ping() get multiple nodes (via XML parameter "nodes") to potentially run UEs on different nodes (hosts) at the same time. However, I argue that this is not good: - we don't actually use this -- where we specify multiple UEs, it's always "localhost localhost..." - it is inconsistent, as we typically have a single "node", not nodes, and there is a possibility to harmonize (see also next commit) - if we needed it, there would be better ways to achieve the same. First, for hardware-based UEs, the ci_infra.yaml can specify different UEs. For simulated UEs, it would be feasible to have multiple XML steps run in parallel, e.g., in the XML tag, which would clarify that multiple UEs on different hosts run in parallel - it reduces code. Hence, make a single for these XML steps. --- ci-scripts/cls_oaicitest.py | 41 +++++++------- ci-scripts/main.py | 56 +++++-------------- ci-scripts/tests/ping-iperf.py | 10 ++-- .../xml_files/container_4g_l2sim_tdd.xml | 10 ++-- .../container_4g_rfsim_fdd_05MHz.xml | 10 ++-- .../container_4g_rfsim_fdd_05MHz_noS1.xml | 8 +-- .../container_4g_rfsim_fdd_10MHz.xml | 10 ++-- .../container_4g_rfsim_fdd_20MHz.xml | 10 ++-- .../xml_files/container_4g_rfsim_fembms.xml | 2 +- .../xml_files/container_4g_rfsim_mbms.xml | 2 +- .../container_4g_rfsim_tdd_05MHz.xml | 8 +-- .../xml_files/container_5g_e1_rfsim.xml | 4 +- .../xml_files/container_5g_f1_rfsim.xml | 10 ++-- .../xml_files/container_5g_fdd_rfsim.xml | 10 ++-- .../xml_files/container_5g_flexric_rfsim.xml | 6 +- ci-scripts/xml_files/container_5g_rfsim.xml | 24 ++++---- .../xml_files/container_5g_rfsim_24prb.xml | 10 ++-- .../xml_files/container_5g_rfsim_2x2.xml | 10 ++-- .../container_5g_rfsim_fdd_phytest.xml | 8 +-- .../container_5g_rfsim_fr2_32prb.xml | 6 +- .../xml_files/container_5g_rfsim_multiue.xml | 20 +++---- .../xml_files/container_5g_rfsim_n2_ho.xml | 10 ++-- .../xml_files/container_5g_rfsim_ntn_geo.xml | 8 +-- .../xml_files/container_5g_rfsim_ntn_leo.xml | 6 +- .../xml_files/container_5g_rfsim_simple.xml | 6 +- .../xml_files/container_5g_rfsim_tdd_dora.xml | 4 +- .../xml_files/container_5g_rfsim_u0_25prb.xml | 14 ++--- 27 files changed, 146 insertions(+), 177 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 144bc24da6..f64972f253 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -304,13 +304,12 @@ class OaiCiTest(): self.finalStatus = False self.air_interface='' self.ue_ids = [] - self.nodes = [] self.svr_node = None self.svr_id = None self.cmd_prefix = '' # prefix before {lte,nr}-uesoftmodem - def InitializeUE(self, HTML): - ues = [cls_module.Module_UE(n.strip()) for n in self.ue_ids] + def InitializeUE(self, node, HTML): + ues = [cls_module.Module_UE(n.strip(), node) for n in self.ue_ids] messages = [] with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: futures = [executor.submit(ue.initialize) for ue in ues] @@ -321,8 +320,8 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue('N/A', 'OK', messages) return True - def AttachUE(self, HTML): - ues = [cls_module.Module_UE(ue_id, server_name) for ue_id, server_name in zip(self.ue_ids, self.nodes)] + def AttachUE(self, node, HTML): + ues = [cls_module.Module_UE(ue_id, node) for ue_id in self.ue_ids] with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: futures = [executor.submit(ue.attach) for ue in ues] attached = [f.result() for f in futures] @@ -337,8 +336,8 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue('N/A', 'KO', ["Could not retrieve UE IP address(es) or MTU(s) wrong!"]) return success - def DetachUE(self, HTML): - ues = [cls_module.Module_UE(ue_id, server_name) for ue_id, server_name in zip(self.ue_ids, self.nodes)] + def DetachUE(self, node, HTML): + ues = [cls_module.Module_UE(ue_id, node) for ue_id in self.ue_ids] with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: futures = [executor.submit(ue.detach) for ue in ues] [f.result() for f in futures] @@ -346,8 +345,8 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue('NA', 'OK', messages) return True - def DataDisableUE(self, HTML): - ues = [cls_module.Module_UE(n.strip()) for n in self.ue_ids] + def DataDisableUE(self, node, HTML): + ues = [cls_module.Module_UE(n.strip(), node) for n in self.ue_ids] with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: futures = [executor.submit(ue.dataDisable) for ue in ues] status = [f.result() for f in futures] @@ -360,8 +359,8 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue('N/A', 'KO', ["Could not disable UE data!"]) return success - def DataEnableUE(self, HTML): - ues = [cls_module.Module_UE(n.strip()) for n in self.ue_ids] + def DataEnableUE(self, node, HTML): + ues = [cls_module.Module_UE(n.strip(), node) for n in self.ue_ids] logging.debug(f'disabling data for UEs {ues}') with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: futures = [executor.submit(ue.dataEnable) for ue in ues] @@ -375,8 +374,8 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue('N/A', 'KO', ["Could not enable UE data!"]) return success - def CheckStatusUE(self,HTML): - ues = [cls_module.Module_UE(n.strip()) for n in self.ue_ids] + def CheckStatusUE(self, node, HTML): + ues = [cls_module.Module_UE(n.strip(), node) for n in self.ue_ids] logging.debug(f'checking status of UEs {ues}') messages = [] with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: @@ -446,10 +445,10 @@ class OaiCiTest(): return (True, message) - def Ping(self, ctx, HTML, infra_file="ci_infra.yaml"): + def Ping(self, ctx, node, HTML, infra_file="ci_infra.yaml"): if self.ue_ids == [] or self.svr_id == None: raise Exception("no module names in self.ue_ids or/and self.svr_id provided") - ues = [cls_module.Module_UE(ue_id, server_name, infra_file) for ue_id, server_name in zip(self.ue_ids, self.nodes)] + ues = [cls_module.Module_UE(ue_id, node, infra_file) for ue_id in self.ue_ids] cn = cls_corenetwork.CoreNetwork(self.svr_id, self.svr_node, filename=infra_file) with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: futures = [executor.submit(self.Ping_common, ctx, cn, ue) for ue in ues] @@ -520,12 +519,12 @@ class OaiCiTest(): return (status, f'{ue_header}\n{msg}') - def Iperf(self, ctx, HTML, infra_file="ci_infra.yaml"): + def Iperf(self, ctx, node, HTML, infra_file="ci_infra.yaml"): logging.debug(f'Iperf: iperf_args "{self.iperf_args}" iperf_packetloss_threshold "{self.iperf_packetloss_threshold}" iperf_bitrate_threshold "{self.iperf_bitrate_threshold}" iperf_profile "{self.iperf_profile}" iperf_options "{self.iperf_options}"') if self.ue_ids == [] or self.svr_id == None: raise Exception("no module names in self.ue_ids or/and self.svr_id provided") - ues = [cls_module.Module_UE(ue_id, server_name, infra_file) for ue_id, server_name in zip(self.ue_ids, self.nodes)] + ues = [cls_module.Module_UE(ue_id, node, infra_file) for ue_id in self.ue_ids] cn = cls_corenetwork.CoreNetwork(self.svr_id, self.svr_node, filename=infra_file) with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: futures = [executor.submit(self.Iperf_Module, ctx, cn, ue, i, len(ues)) for i, ue in enumerate(ues)] @@ -549,10 +548,10 @@ class OaiCiTest(): HTML.CreateHtmlTestRowQueue(self.iperf_args, 'KO', messages) return success - def Iperf2_Unidir(self, ctx, HTML, infra_file="ci_infra.yaml"): + def Iperf2_Unidir(self, ctx, node, HTML, infra_file="ci_infra.yaml"): if self.ue_ids == [] or self.svr_id == None or len(self.ue_ids) != 1: raise Exception("no module names in self.ue_ids or/and self.svr_id provided, multi UE scenario not supported") - ue = cls_module.Module_UE(self.ue_ids[0].strip(),self.nodes[0].strip(), infra_file) + ue = cls_module.Module_UE(self.ue_ids[0].strip(), node, infra_file) cn = cls_corenetwork.CoreNetwork(self.svr_id, self.svr_node, filename=infra_file) ueIP = ue.getIP() if not ueIP: @@ -852,8 +851,8 @@ class OaiCiTest(): global_status = CONST.OAI_UE_PROCESS_COULD_NOT_SYNC return global_status - def TerminateUE(self, ctx, HTML): - ues = [cls_module.Module_UE(n.strip()) for n in self.ue_ids] + def TerminateUE(self, ctx, node, HTML): + ues = [cls_module.Module_UE(n.strip(), node) for n in self.ue_ids] with concurrent.futures.ThreadPoolExecutor(max_workers=64) as executor: futures = [executor.submit(ue.terminate, ctx) for ue in ues] archives = [f.result() for f in futures] diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 873976a8db..6b51c180a3 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -150,31 +150,21 @@ def ExecuteActionWithParam(action, ctx): elif action == 'Initialize_UE' or action == 'Attach_UE' or action == 'Detach_UE' or action == 'Terminate_UE' or action == 'CheckStatusUE' or action == 'DataEnable_UE' or action == 'DataDisable_UE': CiTestObj.ue_ids = test.findtext('id').split(' ') - if force_local: - # Change all execution targets to localhost - CiTestObj.nodes = ['localhost'] * len(CiTestObj.ue_ids) - else: - if test.findtext('nodes'): - CiTestObj.nodes = test.findtext('nodes').split(' ') - if len(CiTestObj.ue_ids) != len(CiTestObj.nodes): - logging.error('Number of Nodes are not equal to the total number of UEs') - sys.exit("Mismatch in number of Nodes and UIs") - else: - CiTestObj.nodes = [None] * len(CiTestObj.ue_ids) + node = test.findtext('node') if not force_local else 'localhost' if action == 'Initialize_UE': - success = CiTestObj.InitializeUE(HTML) + success = CiTestObj.InitializeUE(node, HTML) elif action == 'Attach_UE': - success = CiTestObj.AttachUE(HTML) + success = CiTestObj.AttachUE(node, HTML) elif action == 'Detach_UE': - success = CiTestObj.DetachUE(HTML) + success = CiTestObj.DetachUE(node, HTML) elif action == 'Terminate_UE': - success = CiTestObj.TerminateUE(ctx, HTML) + success = CiTestObj.TerminateUE(ctx, node, HTML) elif action == 'CheckStatusUE': - success = CiTestObj.CheckStatusUE(HTML) + success = CiTestObj.CheckStatusUE(node, HTML) elif action == 'DataEnable_UE': - success = CiTestObj.DataEnableUE(HTML) + success = CiTestObj.DataEnableUE(node, HTML) elif action == 'DataDisable_UE': - success = CiTestObj.DataDisableUE(HTML) + success = CiTestObj.DataDisableUE(node, HTML) elif action == 'Ping': CiTestObj.ping_args = test.findtext('ping_args') @@ -183,35 +173,15 @@ def ExecuteActionWithParam(action, ctx): CiTestObj.svr_id = test.findtext('svr_id') or None if test.findtext('svr_node'): CiTestObj.svr_node = test.findtext('svr_node') if not force_local else 'localhost' - if force_local: - # Change all execution targets to localhost - CiTestObj.nodes = ['localhost'] * len(CiTestObj.ue_ids) - else: - if test.findtext('nodes'): - CiTestObj.nodes = test.findtext('nodes').split(' ') - if len(CiTestObj.ue_ids) != len(CiTestObj.nodes): - logging.error('Number of Nodes are not equal to the total number of UEs') - sys.exit("Mismatch in number of Nodes and UIs") - else: - CiTestObj.nodes = [None] * len(CiTestObj.ue_ids) + node = test.findtext('node') if not force_local else 'localhost' ping_rttavg_threshold = test.findtext('ping_rttavg_threshold') or '' - success = CiTestObj.Ping(ctx, HTML) + success = CiTestObj.Ping(ctx, node, HTML) elif action == 'Iperf' or action == 'Iperf2_Unidir': CiTestObj.iperf_args = test.findtext('iperf_args') CiTestObj.ue_ids = test.findtext('id').split(' ') CiTestObj.svr_id = test.findtext('svr_id') or None - if force_local: - # Change all execution targets to localhost - CiTestObj.nodes = ['localhost'] * len(CiTestObj.ue_ids) - else: - if test.findtext('nodes'): - CiTestObj.nodes = test.findtext('nodes').split(' ') - if len(CiTestObj.ue_ids) != len(CiTestObj.nodes): - logging.error('Number of Nodes are not equal to the total number of UEs') - sys.exit("Mismatch in number of Nodes and UIs") - else: - CiTestObj.nodes = [None] * len(CiTestObj.ue_ids) + node = test.findtext('node') if not force_local else 'localhost' if test.findtext('svr_node'): CiTestObj.svr_node = test.findtext('svr_node') if not force_local else 'localhost' CiTestObj.iperf_packetloss_threshold = test.findtext('iperf_packetloss_threshold') @@ -226,9 +196,9 @@ def ExecuteActionWithParam(action, ctx): logging.error('test-case has wrong option ' + CiTestObj.iperf_options) CiTestObj.iperf_options = 'check' if action == 'Iperf': - success = CiTestObj.Iperf(ctx, HTML) + success = CiTestObj.Iperf(ctx, node, HTML) elif action == 'Iperf2_Unidir': - success = CiTestObj.Iperf2_Unidir(ctx, HTML) + success = CiTestObj.Iperf2_Unidir(ctx, node, HTML) elif action == 'IdleSleep': st = test.findtext('idle_sleep_time_in_sec') or "5" diff --git a/ci-scripts/tests/ping-iperf.py b/ci-scripts/tests/ping-iperf.py index bd30768a9f..3450a2b3e9 100644 --- a/ci-scripts/tests/ping-iperf.py +++ b/ci-scripts/tests/ping-iperf.py @@ -23,7 +23,7 @@ class TestPingIperf(unittest.TestCase): self.html.testCaseId = "000000" self.ci = cls_oaicitest.OaiCiTest() self.ci.ue_ids = ["test"] - self.ci.nodes = ["localhost"] + self.node = "localhost" self.ctx = TestCaseCtx.Default(tempfile.mkdtemp()) def tearDown(self): with cls_cmd.LocalCmd() as c: @@ -35,7 +35,7 @@ class TestPingIperf(unittest.TestCase): self.ci.svr_id = "test" infra_file = "tests/config/infra_ping_iperf.yaml" # TODO Should need nothing but options and UE(s) to use - success = self.ci.Ping(self.ctx, self.html, infra_file=infra_file) + success = self.ci.Ping(self.ctx, self.node, self.html, infra_file=infra_file) self.assertTrue(success) def test_iperf(self): @@ -50,7 +50,7 @@ class TestPingIperf(unittest.TestCase): self.ci.iperf_profile = "balanced" infra_file = "tests/config/infra_ping_iperf.yaml" # TODO Should need nothing but options and UE(s) to use - success = self.ci.Iperf(self.ctx, self.html, infra_file=infra_file) + success = self.ci.Iperf(self.ctx, self.node, self.html, infra_file=infra_file) self.assertTrue(success) def test_iperf2_unidir(self): @@ -62,7 +62,7 @@ class TestPingIperf(unittest.TestCase): self.ci.iperf_profile = "balanced" infra_file = "tests/config/infra_ping_iperf.yaml" # TODO Should need nothing but options and UE(s) to use - success = self.ci.Iperf2_Unidir(self.ctx, self.html, infra_file=infra_file) + success = self.ci.Iperf2_Unidir(self.ctx, self.node, self.html, infra_file=infra_file) self.assertTrue(success) def test_iperf_highrate(self): @@ -77,7 +77,7 @@ class TestPingIperf(unittest.TestCase): self.ci.iperf_profile = "balanced" infra_file = "tests/config/infra_ping_iperf.yaml" # TODO Should need nothing but options and UE(s) to use - success = self.ci.Iperf(self.ctx, self.html, infra_file=infra_file) + success = self.ci.Iperf(self.ctx, self.node, self.html, infra_file=infra_file) self.assertTrue(success) if __name__ == '__main__': diff --git a/ci-scripts/xml_files/container_4g_l2sim_tdd.xml b/ci-scripts/xml_files/container_4g_l2sim_tdd.xml index d9ac14038b..8a010b9308 100644 --- a/ci-scripts/xml_files/container_4g_l2sim_tdd.xml +++ b/ci-scripts/xml_files/container_4g_l2sim_tdd.xml @@ -88,14 +88,14 @@ Attach_UE Attach OAI UE l2sim4g_ue - localhost + localhost
Ping Ping trf-gen from LTE-UE 1 l2sim4g_ue - localhost + localhost l2sim4g_ext_dn localhost -c 20 @@ -106,7 +106,7 @@ Ping Ping LTE-UE 1 from trf-gen l2sim4g_ext_dn - localhost + localhost l2sim4g_ue localhost -c 20 @@ -117,7 +117,7 @@ Iperf Iperf UDP Downlink l2sim4g_ue - localhost + localhost l2sim4g_ext_dn localhost -u -t 30 -b 1M -R @@ -129,7 +129,7 @@ Iperf Iperf UDP Uplink l2sim4g_ue - localhost + localhost l2sim4g_ext_dn localhost -u -t 30 -b 3M diff --git a/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz.xml b/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz.xml index 562856b00c..3606957af6 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz.xml @@ -103,14 +103,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim4g_ue - localhost + localhost Ping Ping Traffic-Gen from LTE-UE rfsim4g_ue - localhost + localhost rfsim4g_ext_dn localhost -c 20 @@ -121,7 +121,7 @@ Ping Ping LTE-UE from Traffic-Gen rfsim4g_ext_dn - localhost + localhost rfsim4g_ue localhost -c 20 @@ -136,7 +136,7 @@ -u -t 30 -b 2M -R 25 80 - localhost + localhost localhost @@ -148,7 +148,7 @@ -u -t 30 -b 1M 25 80 - localhost + localhost localhost
diff --git a/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz_noS1.xml b/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz_noS1.xml index 1773d5d439..1d7194f44f 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz_noS1.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fdd_05MHz_noS1.xml @@ -83,7 +83,7 @@ Ping Ping Traffic-Gen from LTE-UE rfsim4g_ue - localhost + localhost rfsim4g_enb_nos1 localhost -c 20 @@ -94,7 +94,7 @@ Ping Ping LTE-UE from eNB rfsim4g_enb_nos1 - localhost + localhost rfsim4g_ue localhost -c 20 @@ -105,7 +105,7 @@ Iperf Iperf UDP DL rfsim4g_ue - localhost + localhost rfsim4g_enb_nos1 localhost -u -t 30 -b 2M -R @@ -117,7 +117,7 @@ Iperf Iperf UDP UL rfsim4g_ue - localhost + localhost rfsim4g_enb_nos1 localhost -u -t 30 -b 1M diff --git a/ci-scripts/xml_files/container_4g_rfsim_fdd_10MHz.xml b/ci-scripts/xml_files/container_4g_rfsim_fdd_10MHz.xml index 7861d8f332..a5b642ebe0 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fdd_10MHz.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fdd_10MHz.xml @@ -103,14 +103,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim4g_ue - localhost + localhost
Ping Ping Traffic-Gen from LTE-UE rfsim4g_ue - localhost + localhost rfsim4g_ext_dn localhost -c 20 @@ -121,7 +121,7 @@ Ping Ping LTE-UE from Traffic-Gen rfsim4g_ext_dn - localhost + localhost rfsim4g_ue localhost -c 20 @@ -132,7 +132,7 @@ Iperf Iperf UDP DL rfsim4g_ue - localhost + localhost rfsim4g_ext_dn localhost -u -t 30 -b 2M -R @@ -144,7 +144,7 @@ Iperf Iperf UDP UL rfsim4g_ue - localhost + localhost rfsim4g_ext_dn localhost -u -t 30 -b 1M diff --git a/ci-scripts/xml_files/container_4g_rfsim_fdd_20MHz.xml b/ci-scripts/xml_files/container_4g_rfsim_fdd_20MHz.xml index 9199f276e7..592c80de68 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fdd_20MHz.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fdd_20MHz.xml @@ -103,14 +103,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim4g_ue - localhost + localhost Ping Ping Traffic-Gen from LTE-UE rfsim4g_ue - localhost + localhost rfsim4g_ext_dn localhost -c 20 @@ -121,7 +121,7 @@ Ping Ping LTE-UE from Traffic-Gen rfsim4g_ext_dn - localhost + localhost rfsim4g_ue localhost -c 20 @@ -132,7 +132,7 @@ Iperf Iperf UDP DL rfsim4g_ue - localhost + localhost rfsim4g_ext_dn localhost -u -t 30 -b 2M -R @@ -144,7 +144,7 @@ Iperf Iperf UDP UL rfsim4g_ue - localhost + localhost rfsim4g_ext_dn localhost -u -t 30 -b 1M diff --git a/ci-scripts/xml_files/container_4g_rfsim_fembms.xml b/ci-scripts/xml_files/container_4g_rfsim_fembms.xml index 73c01f2b32..21d2acc4eb 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_fembms.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_fembms.xml @@ -80,7 +80,7 @@ Iperf2_Unidir Iperf2 UDP DL rfsim4g_ue_fembms - localhost + localhost rfsim4g_enb_fembms localhost -u -t 30 -b 2M diff --git a/ci-scripts/xml_files/container_4g_rfsim_mbms.xml b/ci-scripts/xml_files/container_4g_rfsim_mbms.xml index ea00caa09e..70d5db33cb 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_mbms.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_mbms.xml @@ -80,7 +80,7 @@ Iperf Iperf UDP DL rfsim4g_ue - localhost + localhost rfsim4g_enb_nos1 localhost -u -t 30 -b 2M -R diff --git a/ci-scripts/xml_files/container_4g_rfsim_tdd_05MHz.xml b/ci-scripts/xml_files/container_4g_rfsim_tdd_05MHz.xml index d3129e0c68..60054cb305 100644 --- a/ci-scripts/xml_files/container_4g_rfsim_tdd_05MHz.xml +++ b/ci-scripts/xml_files/container_4g_rfsim_tdd_05MHz.xml @@ -103,7 +103,7 @@ Ping Ping Traffic-Gen from LTE-UE rfsim4g_ue - localhost + localhost rfsim4g_ext_dn localhost -c 20 @@ -114,7 +114,7 @@ Ping Ping LTE-UE from Traffic-Gen rfsim4g_ext_dn - localhost + localhost rfsim4g_ue localhost -c 20 @@ -125,7 +125,7 @@ Iperf Iperf UDP DL rfsim4g_ue - localhost + localhost rfsim4g_ext_dn localhost -u -t 30 -b 2M -R @@ -137,7 +137,7 @@ Iperf Iperf UDP UL rfsim4g_ue - localhost + localhost rfsim4g_ext_dn localhost -u -t 30 -b 1M diff --git a/ci-scripts/xml_files/container_5g_e1_rfsim.xml b/ci-scripts/xml_files/container_5g_e1_rfsim.xml index 54b34e0cff..5c0fa2b99e 100644 --- a/ci-scripts/xml_files/container_5g_e1_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_e1_rfsim.xml @@ -76,14 +76,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue rfsim5g_ue2 rfsim5g_ue3 - localhost localhost localhost + localhost Ping Ping ext-dn from all NR-UEs rfsim5g_ue rfsim5g_ue2 rfsim5g_ue3 - localhost localhost localhost + localhost rfsim5g_5gc_fixed_ip localhost -c 20 -i0.25 diff --git a/ci-scripts/xml_files/container_5g_f1_rfsim.xml b/ci-scripts/xml_files/container_5g_f1_rfsim.xml index 49a28e4a9f..f17be0215d 100644 --- a/ci-scripts/xml_files/container_5g_f1_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_f1_rfsim.xml @@ -107,7 +107,7 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - localhost + localhost @@ -122,7 +122,7 @@ Ping Ping ext-dn from all NR-UEs rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i0.25 @@ -141,7 +141,7 @@ Iperf (DL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -153,7 +153,7 @@ Iperf (UL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -171,7 +171,7 @@ Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i0.5 -w25 diff --git a/ci-scripts/xml_files/container_5g_fdd_rfsim.xml b/ci-scripts/xml_files/container_5g_fdd_rfsim.xml index 3c4e30c97b..44ccb3a3d2 100644 --- a/ci-scripts/xml_files/container_5g_fdd_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_fdd_rfsim.xml @@ -70,14 +70,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - localhost + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -88,7 +88,7 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_ue localhost -c 20 -i 0.25 @@ -100,7 +100,7 @@ Iperf (DL/3Mbps/UDP)(30 sec) -u -b 3M -t 30 -R rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -112,7 +112,7 @@ Iperf (UL/3Mbps/UDP)(30 sec) -u -b 3M -t 30 rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 diff --git a/ci-scripts/xml_files/container_5g_flexric_rfsim.xml b/ci-scripts/xml_files/container_5g_flexric_rfsim.xml index 8ee9326b5e..536952a2bc 100644 --- a/ci-scripts/xml_files/container_5g_flexric_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_flexric_rfsim.xml @@ -121,7 +121,7 @@ Ping Ping ext-dn from both UEs rfsim5g_ue rfsim5g_ue2 - carabe carabe + carabe oai_ext_dn carabe -c 20 @@ -132,14 +132,14 @@ Attach_UE Attach OAI UE 1 and 2 rfsim5g_ue rfsim5g_ue2 - carabe carabe + carabe Detach_UE Detach OAI UE 1 and 2 rfsim5g_ue rfsim5g_ue2 - carabe carabe + carabe diff --git a/ci-scripts/xml_files/container_5g_rfsim.xml b/ci-scripts/xml_files/container_5g_rfsim.xml index 89cbe35dfa..43d2072040 100644 --- a/ci-scripts/xml_files/container_5g_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_rfsim.xml @@ -125,14 +125,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue rfsim5g_ue2 rfsim5g_ue3 rfsim5g_ue4 rfsim5g_ue5 rfsim5g_ue6 rfsim5g_ue7 rfsim5g_ue8 rfsim5g_ue9 rfsim5g_ue10 - localhost localhost localhost localhost localhost localhost localhost localhost localhost localhost + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -143,7 +143,7 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_ue localhost -c 20 -i 0.25 @@ -154,7 +154,7 @@ Ping Ping ext-dn from Second NR-UE rfsim5g_ue2 - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -165,7 +165,7 @@ Ping Ping Second NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_ue2 localhost -c 20 -i 0.25 @@ -176,7 +176,7 @@ Ping Ping ext-dn from both UEs rfsim5g_ue rfsim5g_ue2 - localhost localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -187,7 +187,7 @@ Ping Ping ext-dn from all UEs rfsim5g_ue rfsim5g_ue2 rfsim5g_ue3 rfsim5g_ue4 rfsim5g_ue5 rfsim5g_ue6 rfsim5g_ue7 rfsim5g_ue8 rfsim5g_ue9 rfsim5g_ue10 - localhost localhost localhost localhost localhost localhost localhost localhost localhost localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 -s 1016 @@ -199,7 +199,7 @@ Iperf (DL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -211,7 +211,7 @@ Iperf (UL/1Mbps/UDP)(20 sec) -u -b 1M -t 20 rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -222,21 +222,21 @@ Attach_UE Attach OAI UE 1 and 2 rfsim5g_ue rfsim5g_ue2 - localhost localhost + localhost Detach_UE Detach OAI UE 1 and 2 rfsim5g_ue rfsim5g_ue2 - localhost localhost + localhost Detach_UE Detach OAI UE 3 to 10 rfsim5g_ue3 rfsim5g_ue4 rfsim5g_ue5 rfsim5g_ue6 rfsim5g_ue7 rfsim5g_ue8 rfsim5g_ue9 rfsim5g_ue10 - localhost localhost localhost localhost localhost localhost localhost localhost + localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_24prb.xml b/ci-scripts/xml_files/container_5g_rfsim_24prb.xml index 470c5e9539..b8e01c7edc 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_24prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_24prb.xml @@ -88,7 +88,7 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - localhost + localhost @@ -101,7 +101,7 @@ Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i0.5 @@ -112,7 +112,7 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_ue localhost -c 20 -i0.2 @@ -124,7 +124,7 @@ Iperf (DL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -136,7 +136,7 @@ Iperf (UL/1Mbps/UDP)(20 sec) -u -b 1M -t 20 rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 diff --git a/ci-scripts/xml_files/container_5g_rfsim_2x2.xml b/ci-scripts/xml_files/container_5g_rfsim_2x2.xml index 636df9e729..236106d966 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_2x2.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_2x2.xml @@ -70,14 +70,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - localhost + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -88,7 +88,7 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_ue localhost -c 20 -i 0.25 @@ -100,7 +100,7 @@ Iperf (DL/10Mbps/UDP)(20 sec) -u -b 10M -t 20 -R rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -112,7 +112,7 @@ Iperf (UL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 diff --git a/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml b/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml index 2a180440d0..4b695063e7 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_fdd_phytest.xml @@ -77,7 +77,7 @@ Ping Ping gNB from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_gnb_nos1 localhost -c20 -i0.2 @@ -88,7 +88,7 @@ Ping Ping NR-UE from gNB rfsim5g_gnb_nos1 - localhost + localhost rfsim5g_ue localhost -c20 -i0.2 @@ -100,7 +100,7 @@ Iperf (DL/30kbps/UDP)(20 sec) -u -b 0.03M -t 20 -R -c 10.0.1.1 rfsim5g_ue - localhost + localhost rfsim5g_gnb_nos1 localhost 5 @@ -112,7 +112,7 @@ Iperf (UL/30kbps/UDP)(20 sec) -u -b 0.03M -t 20 -c 10.0.1.1 rfsim5g_ue - localhost + localhost rfsim5g_gnb_nos1 localhost 5 diff --git a/ci-scripts/xml_files/container_5g_rfsim_fr2_32prb.xml b/ci-scripts/xml_files/container_5g_rfsim_fr2_32prb.xml index 37b90f2385..224a64c757 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_fr2_32prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_fr2_32prb.xml @@ -70,14 +70,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - localhost + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i0.2 @@ -88,7 +88,7 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_ue localhost -c 20 -i0.2 diff --git a/ci-scripts/xml_files/container_5g_rfsim_multiue.xml b/ci-scripts/xml_files/container_5g_rfsim_multiue.xml index 4296fdf970..8022741c6c 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_multiue.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_multiue.xml @@ -84,14 +84,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_multiue1 rfsim5g_multiue2 rfsim5g_multiue3 rfsim5g_multiue4 rfsim5g_multiue5 rfsim5g_multiue6 rfsim5g_multiue7 rfsim5g_multiue8 rfsim5g_multiue9 rfsim5g_multiue10 - localhost localhost localhost localhost localhost localhost localhost localhost localhost localhost + localhost Ping Ping ext-dn from NR-UE rfsim5g_multiue1 - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -102,7 +102,7 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_multiue1 localhost -c 20 -i 0.25 @@ -113,7 +113,7 @@ Ping Ping ext-dn from Second NR-UE rfsim5g_multiue2 - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -124,7 +124,7 @@ Ping Ping Second NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_multiue2 localhost -c 20 -i 0.25 @@ -135,7 +135,7 @@ Ping Ping ext-dn from both UEs rfsim5g_multiue1 rfsim5g_multiue2 - localhost localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -146,7 +146,7 @@ Ping Ping ext-dn from all UEs rfsim5g_multiue1 rfsim5g_multiue2 rfsim5g_multiue3 rfsim5g_multiue4 rfsim5g_multiue5 rfsim5g_multiue6 rfsim5g_multiue7 rfsim5g_multiue8 rfsim5g_multiue9 rfsim5g_multiue10 - localhost localhost localhost localhost localhost localhost localhost localhost localhost localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 -s 1016 @@ -158,7 +158,7 @@ Iperf (DL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_multiue1 - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -170,7 +170,7 @@ Iperf (UL/1Mbps/UDP)(20 sec) -u -b 1M -t 20 rfsim5g_multiue1 - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -181,7 +181,7 @@ Detach_UE Detach OAI UEs rfsim5g_multiue1 - localhost + localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml b/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml index 814f258e0a..37cab0ddb5 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_n2_ho.xml @@ -85,7 +85,7 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - localhost + localhost @@ -100,7 +100,7 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_ue localhost -c 50 -i 0.25 @@ -111,7 +111,7 @@ Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 50 -i 0.25 @@ -123,7 +123,7 @@ Iperf (DL/10Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -135,7 +135,7 @@ Iperf (UL/4Mbps/UDP)(20 sec) -u -b 1M -t 20 rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 diff --git a/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml b/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml index 32bbbc683d..66f725ca53 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml @@ -74,14 +74,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - localhost + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -92,7 +92,7 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_ue localhost -c 20 -i 0.25 @@ -111,7 +111,7 @@ Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i0.5 -w25 diff --git a/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml b/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml index dd9fb56067..cde84b00e7 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml @@ -69,14 +69,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - localhost + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -87,7 +87,7 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_ue localhost -c 20 -i 0.25 diff --git a/ci-scripts/xml_files/container_5g_rfsim_simple.xml b/ci-scripts/xml_files/container_5g_rfsim_simple.xml index 738ce1a077..3dd5a560e0 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_simple.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_simple.xml @@ -63,14 +63,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - localhost + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -81,7 +81,7 @@ Detach_UE Detach OAI UE 1 rfsim5g_ue - localhost + localhost diff --git a/ci-scripts/xml_files/container_5g_rfsim_tdd_dora.xml b/ci-scripts/xml_files/container_5g_rfsim_tdd_dora.xml index a9f7f2c3aa..6a09b8138d 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_tdd_dora.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_tdd_dora.xml @@ -83,7 +83,7 @@ Ping Ping gNB from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_gnb_nos1 localhost -c20 -i0.2 @@ -94,7 +94,7 @@ Ping Ping NR-UE from gNB rfsim5g_gnb_nos1 - localhost + localhost rfsim5g_ue localhost -c20 -i0.2 diff --git a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml index c7668c5022..d563b3aab4 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml @@ -72,14 +72,14 @@ Attach_UE Attach OAI UE (Wait for IP) rfsim5g_ue - localhost + localhost Ping Ping ext-dn from NR-UE rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -90,7 +90,7 @@ Ping Ping NR-UE from ext-dn rfsim5g_ext_dn - localhost + localhost rfsim5g_ue localhost -c 20 -i 0.25 @@ -101,14 +101,14 @@ Attach_UE Verify 2nd PDU session is up rfsim5g_ue_pdu_2 - localhost + localhost Ping Ping ext-dn from NR-UE on PDU session ID 2 rfsim5g_ue_pdu_2 - localhost + localhost rfsim5g_ext_dn localhost -c 20 -i 0.25 @@ -120,7 +120,7 @@ Iperf (DL/3Mbps/UDP)(20 sec) -u -b 3M -t 20 -R rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 @@ -132,7 +132,7 @@ Iperf (UL/1Mbps/UDP)(20 sec) -u -b 1M -t 20 rfsim5g_ue - localhost + localhost rfsim5g_ext_dn localhost 5 From b0106f27b5381e1c01967791740a8d18b0573042 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 27 Oct 2025 18:34:18 +0100 Subject: [PATCH 155/275] Centralize node for all XML steps All XML steps take a single node. We can thus "centralize" reading this parameter in the main loop and pass it to ExecuteActionWithParam(). --- ci-scripts/main.py | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 6b51c180a3..7e96389bf7 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -85,7 +85,7 @@ def CheckClassValidity(xml_class_list,action,id): resp=True return resp -def ExecuteActionWithParam(action, ctx): +def ExecuteActionWithParam(action, ctx, node): global RAN global HTML global CONTAINERS @@ -94,7 +94,6 @@ def ExecuteActionWithParam(action, ctx): 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') CONTAINERS.imageKind=test.findtext('kind') - node = test.findtext('node') proxy_commit = test.findtext('proxy_commit') if proxy_commit is not None: CONTAINERS.proxyCommit = proxy_commit @@ -110,7 +109,6 @@ def ExecuteActionWithParam(action, ctx): success = CONTAINERS.BuildRunTests(ctx, node, HTML) elif action == 'Initialize_eNB': - node = test.findtext('node') datalog_rt_stats_file=test.findtext('rt_stats_cfg') if datalog_rt_stats_file is None: RAN.datalog_rt_stats_file='datalog_rt_stats.default.yaml' @@ -131,7 +129,6 @@ def ExecuteActionWithParam(action, ctx): success = RAN.InitializeeNB(ctx, node, HTML) elif action == 'Terminate_eNB': - node = test.findtext('node') #retx checkers string_field = test.findtext('d_retx_th') if (string_field is not None): @@ -150,7 +147,6 @@ def ExecuteActionWithParam(action, ctx): elif action == 'Initialize_UE' or action == 'Attach_UE' or action == 'Detach_UE' or action == 'Terminate_UE' or action == 'CheckStatusUE' or action == 'DataEnable_UE' or action == 'DataDisable_UE': CiTestObj.ue_ids = test.findtext('id').split(' ') - node = test.findtext('node') if not force_local else 'localhost' if action == 'Initialize_UE': success = CiTestObj.InitializeUE(node, HTML) elif action == 'Attach_UE': @@ -173,7 +169,6 @@ def ExecuteActionWithParam(action, ctx): CiTestObj.svr_id = test.findtext('svr_id') or None if test.findtext('svr_node'): CiTestObj.svr_node = test.findtext('svr_node') if not force_local else 'localhost' - node = test.findtext('node') if not force_local else 'localhost' ping_rttavg_threshold = test.findtext('ping_rttavg_threshold') or '' success = CiTestObj.Ping(ctx, node, HTML) @@ -181,7 +176,6 @@ def ExecuteActionWithParam(action, ctx): CiTestObj.iperf_args = test.findtext('iperf_args') CiTestObj.ue_ids = test.findtext('id').split(' ') CiTestObj.svr_id = test.findtext('svr_id') or None - node = test.findtext('node') if not force_local else 'localhost' if test.findtext('svr_node'): CiTestObj.svr_node = test.findtext('svr_node') if not force_local else 'localhost' CiTestObj.iperf_packetloss_threshold = test.findtext('iperf_packetloss_threshold') @@ -206,7 +200,6 @@ def ExecuteActionWithParam(action, ctx): elif action == 'Deploy_Run_OC_PhySim': oc_release = test.findtext('oc_release') - node = test.findtext('node') or None script = "scripts/oc-deploy-physims.sh" image_tag = cls_containerize.CreateTag(CLUSTER.ranCommitID, CLUSTER.ranBranch, CLUSTER.ranAllowMerge) options = f"oaicicd-core-for-ci-ran {oc_release} {image_tag} {CLUSTER.eNBSourceCodePath}" @@ -214,7 +207,6 @@ def ExecuteActionWithParam(action, ctx): success = cls_oaicitest.Deploy_Physim(ctx, HTML, node, workdir, script, options) elif action == 'Build_Deploy_Docker_PhySim' or action == 'Build_Deploy_Source_PhySim': - node = test.findtext('node') or None ctest_opt = test.findtext('ctest-opt') or '' script = "scripts/docker-build-and-deploy-physims.sh" if action == 'Build_Deploy_Docker_PhySim' else 'scripts/source-deploy-physims.sh' options = f"{CONTAINERS.eNBSourceCodePath} {ctest_opt}" @@ -227,7 +219,6 @@ def ExecuteActionWithParam(action, ctx): success = core_op(cn_id, ctx, HTML) elif action == 'Deploy_Object' or action == 'Undeploy_Object' or action == "Create_Workspace": - node = test.findtext('node') CONTAINERS.yamlPath = test.findtext('yaml_path') string_field=test.findtext('d_retx_th') if (string_field is not None): @@ -249,15 +240,12 @@ def ExecuteActionWithParam(action, ctx): success = CONTAINERS.Create_Workspace(node, HTML) elif action == 'LicenceAndFormattingCheck': - node = test.findtext('node') success = SCA.LicenceAndFormattingCheck(ctx, node, HTML) elif action == 'Cppcheck_Analysis': - node = test.findtext('node') success = SCA.CppCheckAnalysis(ctx, node, HTML) elif action == 'Push_Local_Registry': - node = test.findtext('node') tag_prefix = test.findtext('tag_prefix') or "" success = CONTAINERS.Push_Image_to_Local_Registry(node, HTML, tag_prefix) @@ -265,7 +253,6 @@ def ExecuteActionWithParam(action, ctx): if force_local: # Do not pull or remove images when running locally. User is supposed to handle image creation & cleanup return True - node = test.findtext('node') tag_prefix = test.findtext('tag_prefix') or "" images = test.findtext('images').split() # hack: for FlexRIC, we need to overwrite the tag to use @@ -278,17 +265,12 @@ def ExecuteActionWithParam(action, ctx): success = CONTAINERS.Clean_Test_Server_Images(HTML, node, images, tag=tag) elif action == 'Custom_Command': - node = test.findtext('node') - if force_local: - # Change all execution targets to localhost - node = 'localhost' command = test.findtext('command') # Allow referencing repository workspace path in XML via %%workspace%% command = command.replace("%%workspace%%", CONTAINERS.eNBSourceCodePath) success = cls_oaicitest.Custom_Command(HTML, node, command) elif action == 'Custom_Script': - node = test.findtext('node') script = test.findtext('script') # Allow referencing repository workspace path in XML via %%workspace%% script = script.replace("%%workspace%%", CONTAINERS.eNBSourceCodePath) @@ -297,7 +279,6 @@ def ExecuteActionWithParam(action, ctx): elif action == 'Pull_Cluster_Image': tag_prefix = test.findtext('tag_prefix') or "" images = test.findtext('images').split() - node = test.findtext('node') success = CLUSTER.PullClusterImage(HTML, node, images, tag_prefix=tag_prefix) else: @@ -525,6 +506,7 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re ctx = TestCaseCtx(i, int(id), logPath) HTML.testCase_id=CiTestObj.testCase_id desc = test.findtext('desc') + node = test.findtext('node') if not force_local else 'localhost' always_exec = test.findtext('always_exec') in ['True', 'true', 'Yes', 'yes'] may_fail = test.findtext('may_fail') in ['True', 'true', 'Yes', 'yes'] HTML.desc = desc @@ -539,7 +521,7 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re HTML.CreateHtmlTestRowQueue(msg, "SKIP", []) break try: - test_succeeded = ExecuteActionWithParam(action, ctx) + test_succeeded = ExecuteActionWithParam(action, ctx, node) if not test_succeeded and may_fail: logging.warning(f"test ID {test_case_id} action {action} may or may not fail, proceeding despite error") elif not test_succeeded: From 19c6d20b451ebb78da35de4d219906e9eab6b71b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 28 Oct 2025 09:32:32 +0100 Subject: [PATCH 156/275] Bugfix: "or None" is useless "or None" would assign if the first term is false-y. But not specifying it would already lead to the variable being set to None. --- ci-scripts/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 7e96389bf7..b56d8d7b87 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -166,7 +166,7 @@ def ExecuteActionWithParam(action, ctx, node): CiTestObj.ping_args = test.findtext('ping_args') CiTestObj.ping_packetloss_threshold = test.findtext('ping_packetloss_threshold') CiTestObj.ue_ids = test.findtext('id').split(' ') - CiTestObj.svr_id = test.findtext('svr_id') or None + CiTestObj.svr_id = test.findtext('svr_id') if test.findtext('svr_node'): CiTestObj.svr_node = test.findtext('svr_node') if not force_local else 'localhost' ping_rttavg_threshold = test.findtext('ping_rttavg_threshold') or '' @@ -175,7 +175,7 @@ def ExecuteActionWithParam(action, ctx, node): elif action == 'Iperf' or action == 'Iperf2_Unidir': CiTestObj.iperf_args = test.findtext('iperf_args') CiTestObj.ue_ids = test.findtext('id').split(' ') - CiTestObj.svr_id = test.findtext('svr_id') or None + CiTestObj.svr_id = test.findtext('svr_id') if test.findtext('svr_node'): CiTestObj.svr_node = test.findtext('svr_node') if not force_local else 'localhost' CiTestObj.iperf_packetloss_threshold = test.findtext('iperf_packetloss_threshold') From 032fc5e9c24f97726e078e7a752f0099d3a1a990 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Fri, 31 Oct 2025 12:25:54 +0100 Subject: [PATCH 157/275] fix bugs in scope for pdcch, and optimize cpu cost of pdcch decoding by not processing useless samples in one symbol --- openair1/PHY/NR_REFSIG/nr_dmrs_rx.c | 17 ++------ openair1/PHY/NR_REFSIG/refsig_defs_ue.h | 5 +-- .../nr_dl_channel_estimation.c | 2 +- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 39 +++++++++---------- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 2 +- 5 files changed, 25 insertions(+), 40 deletions(-) diff --git a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c index e25d9700da..62dc85e90c 100644 --- a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c +++ b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c @@ -150,22 +150,11 @@ int nr_pdsch_dmrs_rx(const PHY_VARS_NR_UE *ue, return(0); } -int nr_pdcch_dmrs_rx(const unsigned int *nr_gold_pdcch, - c16_t *output, - unsigned short p, - unsigned short nb_rb_coreset) +void nr_pdcch_dmrs_rx(const unsigned int *nr_gold_pdcch, c16_t *output, unsigned short nb_rb_coreset) { - if (p==2000) { - for (int i=0; i<((nb_rb_coreset*6)>>1); i++) { - output[i] = get_modulated(nr_gold_pdcch, i, true); -#ifdef DEBUG_PDCCH - if (i<8) - printf("i %d idx %d pdcch mod_dmrs %d %d\n", i, idx, output[i].r, output[i].i); -#endif - } + for (int i = 0; i < ((nb_rb_coreset * 6) >> 1); i++) { + output[i] = get_modulated(nr_gold_pdcch, i, true); } - - return(0); } void nr_pbch_dmrs_rx(int symbol, const unsigned int *nr_gold_pbch, c16_t *output, bool sidelink) diff --git a/openair1/PHY/NR_REFSIG/refsig_defs_ue.h b/openair1/PHY/NR_REFSIG/refsig_defs_ue.h index 414dae9976..d9d1ef4534 100644 --- a/openair1/PHY/NR_REFSIG/refsig_defs_ue.h +++ b/openair1/PHY/NR_REFSIG/refsig_defs_ue.h @@ -35,10 +35,7 @@ void nr_pbch_dmrs_rx(int dmrss, const unsigned int *nr_gold_pbch, c16_t *output, /*!\brief This function generates the NR Gold sequence (38-211, Sec 5.2.1) for the PDCCH DMRS. @param PHY_VARS_NR_UE* ue structure provides configuration, frame parameters and the pointers to the 32 bits sequence storage tables */ -int nr_pdcch_dmrs_rx(const unsigned int *nr_gold_pdcch, - c16_t *output, - unsigned short p, - unsigned short nb_rb_corset); +void nr_pdcch_dmrs_rx(const unsigned int *nr_gold_pdcch, c16_t *output, unsigned short nb_rb_corset); int nr_pdsch_dmrs_rx(const PHY_VARS_NR_UE *ue, unsigned int Ns, diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index 6a49a17608..f310c0236b 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -846,7 +846,7 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, c16_t pilot[(nb_rb_coreset + dmrs_ref) * 3] __attribute__((aligned(16))); // Note: pilot returned by the following function is already the complex conjugate of the transmitted DMRS const uint32_t *gold = nr_gold_pdcch(ue->frame_parms.N_RB_DL, ue->frame_parms.symbols_per_slot, scrambling_id, slot, symbol); - nr_pdcch_dmrs_rx(gold, pilot, 2000, (nb_rb_coreset + dmrs_ref)); + nr_pdcch_dmrs_rx(gold, pilot, nb_rb_coreset + dmrs_ref); for (aarx=0; aarxframe_parms.nb_antennas_rx; aarx++) { diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 9e48f52a03..d956ca7cc3 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -294,8 +294,9 @@ static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, } static void nr_pdcch_channel_compensation(int arraySz, - c16_t rxdataF_ext[][arraySz], - c16_t dl_ch_estimates_ext[][arraySz], + int sz2, + c16_t rxdataF_ext[][sz2], + c16_t dl_ch_estimates_ext[][sz2], c16_t rxdataF_comp[][arraySz], int antRx, uint8_t output_shift) @@ -306,17 +307,17 @@ static void nr_pdcch_channel_compensation(int arraySz, } } -static void nr_pdcch_detection_mrc(int sz, c16_t rxdataF_comp[][sz]) +static void nr_pdcch_detection_mrc(int nb_ant, int sz, c16_t rxdataF_comp[][sz]) { - LOG_D(NR_PHY_DCI, "we enter nr_pdcch_detection_mrc (hard coded 2 antennas)\n"); c16_t *rx0 = rxdataF_comp[0]; - c16_t *rx1 = rxdataF_comp[1]; - // MRC on each re of rb // input always aligned and accepting tail padding to process all actual samples - for (int i = 0; i < sz; i += 4) { - *(simde__m128i *)(rx0 + i) = - simde_mm_adds_epi16(simde_mm_srai_epi16(*(simde__m128i *)(rx0 + i), 1), simde_mm_srai_epi16(*(simde__m128i *)(rx1 + i), 1)); + for (int a = 1; a < nb_ant; a++) { + c16_t *rx = rxdataF_comp[a]; + for (int i = 0; i < sz; i += 4) { + *(simde__m128i *)(rx0 + i) = simde_mm_adds_epi16(simde_mm_srai_epi16(*(simde__m128i *)(rx0 + i), 1), + simde_mm_srai_epi16(*(simde__m128i *)(rx + i), 1)); + } } } @@ -334,7 +335,7 @@ static void nr_rx_pdcch_symbol(PHY_VARS_NR_UE *ue, NR_DL_FRAME_PARMS *fp = &ue->frame_parms; NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; fapi_nr_coreset_t *coreset = &phy_pdcch_config->pdcch_config[ss_idx].coreset; - int32_t pdcch_est_size = ((((fp->ofdm_symbol_size + LTE_CE_FILTER_LENGTH) + 15) / 16) * 16); + int32_t pdcch_est_size = ceil_mod(fp->ofdm_symbol_size + LTE_CE_FILTER_LENGTH, 16); __attribute__((aligned(16))) c16_t pdcch_dl_ch_estimates[fp->nb_antennas_rx][pdcch_est_size]; nr_pdcch_channel_estimation(ue, @@ -347,12 +348,9 @@ static void nr_rx_pdcch_symbol(PHY_VARS_NR_UE *ue, pdcch_dl_ch_estimates, rxdataF); - const int32_t rx_size = ((4 * fp->N_RB_DL * 12 + 31) >> 5) << 5; + const int32_t rx_size = ceil_mod(fp->N_RB_DL * 12, 32); __attribute__((aligned(32))) c16_t rxdataF_ext[fp->nb_antennas_rx][rx_size]; - __attribute__((aligned(32))) c16_t rxdataF_comp[fp->nb_antennas_rx][rx_size]; __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[fp->nb_antennas_rx][rx_size]; - memset(rxdataF_comp, 0, sizeof(rxdataF_comp)); - int n_rb; int rb_offset; get_coreset_rballoc(coreset->frequency_domain_resource, &n_rb, &rb_offset); @@ -376,20 +374,21 @@ static void nr_rx_pdcch_symbol(PHY_VARS_NR_UE *ue, for (int i = 1; i < fp->nb_antennas_rx; i++) avgs = cmax(avgs, avg[i]); const int log2_maxh = (log2_approx(avgs) / 2) + 5; //+frame_parms->nb_antennas_rx; - - nr_pdcch_channel_compensation(rx_size, + int rx_comp_sz = ceil_mod(llr_size_symbol, 4); + __attribute__((aligned(32))) c16_t rxdataF_comp[fp->nb_antennas_rx][rx_comp_sz]; + memset(rxdataF_comp, 0, sizeof(rxdataF_comp)); + nr_pdcch_channel_compensation(rx_comp_sz, + rx_size, rxdataF_ext, pdcch_dl_ch_estimates_ext, rxdataF_comp, fp->nb_antennas_rx, log2_maxh); // log2_maxh+I0_shift - UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp, sizeof(struct complex16), fp->nb_antennas_rx, rx_size, 0); - if (fp->nb_antennas_rx > 1) { - nr_pdcch_detection_mrc(rx_size, rxdataF_comp); + nr_pdcch_detection_mrc(fp->nb_antennas_rx, rx_comp_sz, rxdataF_comp); } - + UEscopeCopy(ue, pdcchRxdataF_comp, rxdataF_comp[0], sizeof(c16_t), 1, llr_size_symbol, 0); nr_pdcch_llr(llr_size_symbol, rxdataF_comp[0], llr); } diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index cbef1dc62a..51c7bf2c55 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -1041,7 +1041,7 @@ void pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_ nr_pdcch_generate_llr(ue, proc, symbol, phy_data, llr_size_symbol, num_monitoring_occ, max_nb_symb_pdcch, rxdataF_symb, pdcch_llr); if (symbol == last_symb_pdcch) { nr_pdcch_dci_indication(proc, llr_size_symbol * max_nb_symb_pdcch, num_monitoring_occ, ue, phy_data, pdcch_llr); - UEscopeCopy(ue, pdcchLlr, pdcch_llr, sizeof(c16_t), 1, sizeof(pdcch_llr), 0); + UEscopeCopy(ue, pdcchLlr, pdcch_llr, sizeof(c16_t), 1, sizeof(pdcch_llr) / sizeof(c16_t), 0); } } stop_meas_nr_ue_phy(ue, DLSCH_RX_PDCCH_STATS); From f982d9b5df9f713699ba6eedd52dac714503f6c0 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Mon, 3 Nov 2025 20:24:37 +0100 Subject: [PATCH 158/275] rename a function to what it does (it generate a reference signal) and minor readability changes --- openair1/PHY/NR_REFSIG/nr_dmrs_rx.c | 2 +- openair1/PHY/NR_REFSIG/refsig_defs_ue.h | 2 +- .../nr_dl_channel_estimation.c | 58 ++++++++----------- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c index 62dc85e90c..3624d754e9 100644 --- a/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c +++ b/openair1/PHY/NR_REFSIG/nr_dmrs_rx.c @@ -150,7 +150,7 @@ int nr_pdsch_dmrs_rx(const PHY_VARS_NR_UE *ue, return(0); } -void nr_pdcch_dmrs_rx(const unsigned int *nr_gold_pdcch, c16_t *output, unsigned short nb_rb_coreset) +void nr_pdcch_dmrs_ref(const unsigned int *nr_gold_pdcch, c16_t *output, unsigned short nb_rb_coreset) { for (int i = 0; i < ((nb_rb_coreset * 6) >> 1); i++) { output[i] = get_modulated(nr_gold_pdcch, i, true); diff --git a/openair1/PHY/NR_REFSIG/refsig_defs_ue.h b/openair1/PHY/NR_REFSIG/refsig_defs_ue.h index d9d1ef4534..fb7dce4489 100644 --- a/openair1/PHY/NR_REFSIG/refsig_defs_ue.h +++ b/openair1/PHY/NR_REFSIG/refsig_defs_ue.h @@ -35,7 +35,7 @@ void nr_pbch_dmrs_rx(int dmrss, const unsigned int *nr_gold_pbch, c16_t *output, /*!\brief This function generates the NR Gold sequence (38-211, Sec 5.2.1) for the PDCCH DMRS. @param PHY_VARS_NR_UE* ue structure provides configuration, frame parameters and the pointers to the 32 bits sequence storage tables */ -void nr_pdcch_dmrs_rx(const unsigned int *nr_gold_pdcch, c16_t *output, unsigned short nb_rb_corset); +void nr_pdcch_dmrs_ref(const unsigned int *nr_gold_pdcch, c16_t *output, unsigned short nb_rb_corset); int nr_pdsch_dmrs_rx(const PHY_VARS_NR_UE *ue, unsigned int Ns, diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index f310c0236b..823c38c88d 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -807,10 +807,8 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, c16_t pdcch_dl_ch_estimates[][pdcch_est_size], c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size]) { + const int symb_sz = ue->frame_parms.ofdm_symbol_size; int slot = proc->nr_slot_rx; - unsigned char aarx; - unsigned short k; - unsigned int pilot_cnt; int nb_rb_coreset=0; int coreset_start_rb=0; @@ -824,14 +822,6 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, unsigned short coreset_start_subcarrier = first_carrier_offset+(BWPStart + coreset_start_rb)*12; -#ifdef DEBUG_PDCCH - printf("PDCCH Channel Estimation : OFDM size %d, Ncp=%d, slot=%d, symbol %d\n", - ue->frame_parms.ofdm_symbol_size, - ue->frame_parms.Ncp, - slot, - symbol); -#endif - #if CH_INTERP int16_t *fl = filt16a_l1; int16_t *fm = filt16a_m1; @@ -846,14 +836,13 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, c16_t pilot[(nb_rb_coreset + dmrs_ref) * 3] __attribute__((aligned(16))); // Note: pilot returned by the following function is already the complex conjugate of the transmitted DMRS const uint32_t *gold = nr_gold_pdcch(ue->frame_parms.N_RB_DL, ue->frame_parms.symbols_per_slot, scrambling_id, slot, symbol); - nr_pdcch_dmrs_rx(gold, pilot, nb_rb_coreset + dmrs_ref); + nr_pdcch_dmrs_ref(gold, pilot, nb_rb_coreset + dmrs_ref); - for (aarx=0; aarxframe_parms.nb_antennas_rx; aarx++) { - - k = coreset_start_subcarrier; + for (int aarx = 0; aarx < ue->frame_parms.nb_antennas_rx; aarx++) { + int k = coreset_start_subcarrier; c16_t *pil = &pilot[dmrs_ref * 3]; c16_t *rxF = &rxdataF[aarx][k + 1]; - c16_t *dl_ch = &pdcch_dl_ch_estimates[aarx][0]; + c16_t *dl_ch = pdcch_dl_ch_estimates[aarx]; memset(dl_ch, 0, sizeof(c16_t) * ue->frame_parms.ofdm_symbol_size); @@ -871,36 +860,36 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, rxF += 4; k += 2; - if (k >= ue->frame_parms.ofdm_symbol_size) { - k -= ue->frame_parms.ofdm_symbol_size; + if (k >= symb_sz) { + k -= symb_sz; rxF = &rxdataF[aarx][k + 1]; } multadd_real_vector_complex_scalar(fm, c16mulShift(*pil++, *rxF, 15), dl_ch, 16); - k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][k + 1)]; + k = (k + 4) % symb_sz; + rxF = &rxdataF[aarx][k + 1)]; multadd_real_vector_complex_scalar(fr, c16mulShift(*pil++, *rxF, 15), dl_ch, 16); dl_ch += 12; - k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][k + 1]; + k = (k + 4) % symb_sz; + rxF = &rxdataF[aarx][k + 1]; - for (pilot_cnt=3; pilot_cnt<(3*nb_rb_coreset); pilot_cnt += 3) { + for (int pilot_cnt = 3; pilot_cnt < (3 * nb_rb_coreset); pilot_cnt += 3) { multadd_real_vector_complex_scalar(fl, c16mulShift(*pil++, *rxF, 15), dl_ch, 16); - k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][k + 1]; + k = (k + 4) % symb_sz; + rxF = &rxdataF[aarx][k + 1]; multadd_real_vector_complex_scalar(fm, c16mulShift(*pil++, *rxF, 15), dl_ch, 16); - k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][k + 1]; + k = (k + 4) % symb_sz; + rxF = &rxdataF[aarx][k + 1]; multadd_real_vector_complex_scalar(fr, c16mulShift(*pil++, *rxF, 15), dl_ch, 16); dl_ch += 12; - k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][k + 1]; + k = (k + 4) % symb_sz; + rxF = &rxdataF[aarx][k + 1]; } - #else //ELSE CH_INTERP +#else //ELSE CH_INTERP c32_t ch_sum = {0, 0}; - for (pilot_cnt = 0; pilot_cnt < 3*nb_rb_coreset; pilot_cnt++) { + for (int pilot_cnt = 0; pilot_cnt < 3 * nb_rb_coreset; pilot_cnt++) { #ifdef DEBUG_PDCCH printf("pilot[%u] = (%d, %d)\trxF[%d] = (%d, %d)\n", pilot_cnt, pil[0], pil[1], k+1, rxF[0], rxF[1]); @@ -908,8 +897,8 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, c16_t ch = c16mulShift(*pil++, *rxF, 15); ch_sum.r += ch.r; ch_sum.i += ch.i; - k = (k + 4) % ue->frame_parms.ofdm_symbol_size; - rxF = (c16_t *)&rxdataF[aarx][k + 1]; + k = (k + 4) % symb_sz; + rxF = &rxdataF[aarx][k + 1]; if (pilot_cnt % 3 == 2) { ch.r = ch_sum.r / 3; @@ -923,11 +912,10 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, ch_sum = (c32_t){0}; } } - #endif //END CH_INTERP +#endif //END CH_INTERP //} - } } From ab14e3ad229f1042030c506c3343136a138699ac Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Mon, 3 Nov 2025 20:33:20 +0100 Subject: [PATCH 159/275] move all antennas comon code of begining of nr_pdcch_channel_estimation to prepare the future move of the antenna loop above the serial call of functions to decode pdcch --- .../nr_dl_channel_estimation.c | 29 +++++-------------- openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h | 11 +++---- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 27 ++++++++++++----- 3 files changed, 32 insertions(+), 35 deletions(-) diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index 823c38c88d..a95ef9f6f8 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -797,23 +797,18 @@ int nr_pbch_channel_estimation(const NR_DL_FRAME_PARMS *fp, return(0); } -void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, - const UE_nr_rxtx_proc_t *proc, - unsigned char symbol, - fapi_nr_coreset_t *coreset, +void nr_pdcch_channel_estimation(const PHY_VARS_NR_UE *ue, + int nb_rb_coreset, + int coreset_start_rb, + int dmrs_ref, uint16_t first_carrier_offset, uint16_t BWPStart, int32_t pdcch_est_size, c16_t pdcch_dl_ch_estimates[][pdcch_est_size], - c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size]) + c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size], + c16_t *pilot) { const int symb_sz = ue->frame_parms.ofdm_symbol_size; - int slot = proc->nr_slot_rx; - - int nb_rb_coreset=0; - int coreset_start_rb=0; - get_coreset_rballoc(coreset->frequency_domain_resource,&nb_rb_coreset,&coreset_start_rb); - if(nb_rb_coreset==0) return; #ifdef DEBUG_PDCCH printf("pdcch_channel_estimation: first_carrier_offset %d, BWPStart %d, coreset_start_rb %d, coreset_nb_rb %d\n", @@ -828,23 +823,13 @@ void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, int16_t *fr = filt16a_r1; #endif - unsigned short scrambling_id = coreset->pdcch_dmrs_scrambling_id; - int dmrs_ref = 0; - if (coreset->CoreSetType == NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG) - dmrs_ref = BWPStart; - // generate pilot - c16_t pilot[(nb_rb_coreset + dmrs_ref) * 3] __attribute__((aligned(16))); - // Note: pilot returned by the following function is already the complex conjugate of the transmitted DMRS - const uint32_t *gold = nr_gold_pdcch(ue->frame_parms.N_RB_DL, ue->frame_parms.symbols_per_slot, scrambling_id, slot, symbol); - nr_pdcch_dmrs_ref(gold, pilot, nb_rb_coreset + dmrs_ref); - for (int aarx = 0; aarx < ue->frame_parms.nb_antennas_rx; aarx++) { int k = coreset_start_subcarrier; c16_t *pil = &pilot[dmrs_ref * 3]; c16_t *rxF = &rxdataF[aarx][k + 1]; c16_t *dl_ch = pdcch_dl_ch_estimates[aarx]; - memset(dl_ch, 0, sizeof(c16_t) * ue->frame_parms.ofdm_symbol_size); + memset(dl_ch, 0, sizeof(c16_t) * symb_sz); #ifdef DEBUG_PDCCH printf("pdcch ch est pilot addr %p RB_DL %d\n",&pilot[dmrs_ref*3], ue->frame_parms.N_RB_DL); diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h index 85b8999118..4ed05ce9cf 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h @@ -44,15 +44,16 @@ void peak_estimator(int32_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t /*! \brief This function performs channel estimation including frequency and temporal interpolation */ -void nr_pdcch_channel_estimation(PHY_VARS_NR_UE *ue, - const UE_nr_rxtx_proc_t *proc, - unsigned char symbol, - fapi_nr_coreset_t *coreset, +void nr_pdcch_channel_estimation(const PHY_VARS_NR_UE *ue, + int nb_rb, + int rb_offset, + int dmrs_ref, uint16_t first_carrier_offset, uint16_t BWPStart, int32_t pdcch_est_size, c16_t pdcch_dl_ch_estimates[][pdcch_est_size], - c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size]); + c16_t rxdataF[ue->frame_parms.nb_antennas_rx][ue->frame_parms.ofdm_symbol_size], + c16_t *pilot); c32_t nr_pbch_dmrs_correlation(const NR_DL_FRAME_PARMS *fp, const UE_nr_rxtx_proc_t *proc, diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index d956ca7cc3..4c5c6e6f39 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -40,7 +40,7 @@ #include "PHY/sse_intrin.h" #include "common/utils/nr/nr_common.h" #include -#include "openair1/PHY/NR_REFSIG/nr_refsig_common.h" +#include "openair1/PHY/NR_REFSIG/refsig_defs_ue.h" #include "PHY/NR_UE_ESTIMATION/nr_estimation.h" #include "assertions.h" @@ -337,23 +337,34 @@ static void nr_rx_pdcch_symbol(PHY_VARS_NR_UE *ue, fapi_nr_coreset_t *coreset = &phy_pdcch_config->pdcch_config[ss_idx].coreset; int32_t pdcch_est_size = ceil_mod(fp->ofdm_symbol_size + LTE_CE_FILTER_LENGTH, 16); __attribute__((aligned(16))) c16_t pdcch_dl_ch_estimates[fp->nb_antennas_rx][pdcch_est_size]; + int n_rb; + int rb_offset; + get_coreset_rballoc(coreset->frequency_domain_resource, &n_rb, &rb_offset); + unsigned short scrambling_id = coreset->pdcch_dmrs_scrambling_id; + int dmrs_ref = 0; + if (coreset->CoreSetType == NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG) + dmrs_ref = phy_pdcch_config->pdcch_config[ss_idx].BWPStart; + // generate pilot + c16_t pilot[(n_rb + dmrs_ref) * 3] __attribute__((aligned(16))); + // Note: pilot returned by the following function is already the complex conjugate of the transmitted DMRS + const uint32_t *gold = + nr_gold_pdcch(ue->frame_parms.N_RB_DL, ue->frame_parms.symbols_per_slot, scrambling_id, proc->nr_slot_rx, symbol); + nr_pdcch_dmrs_ref(gold, pilot, n_rb + dmrs_ref); nr_pdcch_channel_estimation(ue, - proc, - symbol, - coreset, + n_rb, + rb_offset, + dmrs_ref, fp->first_carrier_offset, phy_pdcch_config->pdcch_config[ss_idx].BWPStart, pdcch_est_size, pdcch_dl_ch_estimates, - rxdataF); + rxdataF, + pilot); const int32_t rx_size = ceil_mod(fp->N_RB_DL * 12, 32); __attribute__((aligned(32))) c16_t rxdataF_ext[fp->nb_antennas_rx][rx_size]; __attribute__((aligned(32))) c16_t pdcch_dl_ch_estimates_ext[fp->nb_antennas_rx][rx_size]; - int n_rb; - int rb_offset; - get_coreset_rballoc(coreset->frequency_domain_resource, &n_rb, &rb_offset); nr_pdcch_extract_rbs_single(ue->frame_parms.ofdm_symbol_size, rxdataF, From f7eaafa8358b3825a0b8953047bd630696b20e2b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 3 Nov 2025 19:03:03 +0100 Subject: [PATCH 160/275] CI test runner: handle SIGINT Handle SIGINT and mark any subsequent steps as failed. Receiving SIGINT a second time will abort the script immediately (as is the case before this commit). Note that Python seems to relay the signal to the "controlled" process (e.g., Ping), which would return immediately with an error. So some steps like Ping will abort immediately, others (e.g., IdleSleep) will finish their step, and then the present logic will mark subsequent steps as failed. --- ci-scripts/main.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ci-scripts/main.py b/ci-scripts/main.py index b56d8d7b87..99500fff12 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -296,8 +296,16 @@ def test_in_list(test, list): return True return False +test_runner_abort = False def receive_signal(signum, frame): - sys.exit(1) + global test_runner_abort + if not test_runner_abort: + logging.warning("received signal, canceling steps") + logging.info("send signal again to exit immediately") + test_runner_abort = True + else: + logging.warning("received signal again, exiting") + sys.exit(1) def ShowTestID(ctx, desc): logging.info(f'\u001B[1m----------------------------------------\u001B[0m') @@ -488,7 +496,7 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re #logging.info('test will be run: ' + test) todo_tests.append(test) - signal.signal(signal.SIGUSR1, receive_signal) + signal.signal(signal.SIGINT, receive_signal) HTML.CreateHtmlTabHeader() @@ -498,6 +506,8 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re i = 0 for test_case_id in todo_tests: for test in all_tests: + if test_runner_abort: + task_set_succeeded = False id = test.get('id') if test_case_id != id: continue From 579d263a9c594c2cfb5dfdf0243d53f12c43958d Mon Sep 17 00:00:00 2001 From: Chieh-Chun Chen Date: Tue, 4 Nov 2025 09:48:07 +0100 Subject: [PATCH 161/275] Bugfix: save old tunnel info to remain the user plane connection while rolling back to source DU due to F1 handover failure --- openair2/RRC/NR/rrc_gNB.c | 15 +++++++++++++++ openair2/RRC/NR/rrc_gNB_mobility.c | 6 ++++++ openair2/RRC/NR/rrc_gNB_mobility.h | 2 ++ 3 files changed, 23 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 97d0feda67..dcc17ec2c0 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1283,6 +1283,20 @@ static rrc_gNB_ue_context_t *rrc_gNB_get_ue_context_source_cell(gNB_RRC_INST *rr return NULL; } +/** + * @brief Rollback F1-U DL TL and TEID in RRC + */ +static void f1u_dl_gtp_rollback(gNB_RRC_UE_t *UE) +{ + DevAssert(UE != NULL); + DevAssert(UE->ho_context->source != NULL); + + FOR_EACH_SEQ_ARR(drb_t *, drb, &UE->drbs) { + drb->du_tunnel_config = UE->ho_context->source->old_du_tunnel_config; + LOG_W(NR_RRC, "DRB id %d rollback to tunnel TEID %x\n", drb->drb_id, drb->du_tunnel_config.teid); + } +} + static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id, const NR_RRCReestablishmentRequest_IEs_t *req, @@ -1361,6 +1375,7 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, } source_ctx->ho_cancel(rrc, UE); + f1u_dl_gtp_rollback(UE); /* we need the original CellGroupConfig */ ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 552cc881d6..bfe5dece8c 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -160,6 +160,12 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, ho_ctx->source->du_ue_id = ue_data.secondary_ue; ho_ctx->source->old_rnti = ue->rnti; + // Save the GTP-U tunnel info for source DU before process UE context setup request/response + // since tunnel info will be updated by calling store_du_f1u_tunnel() in rrc_CU_process_ue_context_setup_response() + FOR_EACH_SEQ_ARR(drb_t *, drb, &ue->drbs) { + ho_ctx->source->old_du_tunnel_config = drb->du_tunnel_config; + } + int result = asn_copy(&asn_DEF_NR_CellGroupConfig, (void **)&ho_ctx->source->old_cellGroupConfig, ue->masterCellGroup); AssertFatal(result == 0, "error during asn_copy() of CellGroupConfig\n"); } diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index 7010cc83ba..bd0fd8e22c 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -56,6 +56,8 @@ typedef struct nr_ho_source_cu { ho_cancel_t ho_cancel; /// status transfer ho_status_transfer_t ho_status_transfer; + /// old (source) downlink tunnel + gtpu_tunnel_t old_du_tunnel_config; } nr_ho_source_cu_t; /* acknowledgement of handover request. buf+len is the RRC Reconfiguration */ From 7655604d3c785f5a6df6728bc9caec36b8a7a931 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Wed, 5 Nov 2025 00:08:55 +0800 Subject: [PATCH 162/275] NR UE: fix PDCCH LLR indexing for different search space RB sizes When multiple search spaces have different CORESET configurations (different number of RBs), the LLR buffer stride must be calculated based on the maximum RB size across all search spaces, not the current search space's RB size. The LLR buffer is allocated with size based on get_pdcch_max_rbs(), but the demapping function was using coreset_nbr_rb (current search space) for symbol stride calculation, causing incorrect LLR extraction when search spaces have different RB configurations. --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 9e48f52a03..00821b03cc 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -81,7 +81,8 @@ static void nr_pdcch_demapping_deinterleaving(uint32_t coreset_nbr_rb, uint8_t n_shift, uint8_t number_of_candidates, uint16_t *CCE, - uint8_t *L) + uint8_t *L, + int llr_stride_per_symbol) { /* * This function will do demapping and deinterleaving from llr containing demodulated symbols @@ -168,7 +169,7 @@ static void nr_pdcch_demapping_deinterleaving(uint32_t coreset_nbr_rb, for (int cce_count = 0; cce_count < L[c_id]; cce_count++) { for (int k = 0; k < NR_NB_REG_PER_CCE / reg_bundle_size_L; k++) { // loop over REG bundles int f = f_bundle_j_list_ord[c_id][k + NR_NB_REG_PER_CCE * cce_count / reg_bundle_size_L]; - c16_t *in = llr + (f * B_rb + symbol_idx * coreset_nbr_rb) * RE_PER_RB_OUT_DMRS; + c16_t *in = llr + f * B_rb * RE_PER_RB_OUT_DMRS + symbol_idx * llr_stride_per_symbol; // loop over the RBs of the bundle memcpy(e_rx + RE_PER_RB_OUT_DMRS * rb_count, in, B_rb * RE_PER_RB_OUT_DMRS * sizeof(*e_rx)); rb_count += B_rb; @@ -497,6 +498,7 @@ void nr_pdcch_dci_indication(const UE_nr_rxtx_proc_t *proc, fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &phy_pdcch_config->pdcch_config[ss_idx]; uint8_t unused_start_symb[NR_SYMBOLS_PER_SLOT] = {0}; const int num_monitoring_occ = get_pdcch_mon_occasions_slot(rel15, unused_start_symb); + const int llr_stride = llr_size / rel15->coreset.duration; int n_rb; int rb_offset; @@ -515,7 +517,8 @@ void nr_pdcch_dci_indication(const UE_nr_rxtx_proc_t *proc, rel15->coreset.ShiftIndex, rel15->number_of_candidates, rel15->CCE, - rel15->L); + rel15->L, + llr_stride); nr_dci_decoding_procedure(ue, proc, pdcch_e_rx, rel15, &dci_ind); } From b95b4c09d662f8bef48d67097e47a86380cf9f67 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 19 Sep 2025 12:34:26 +0200 Subject: [PATCH 163/275] Write DL ToA into circular buffer In a future commit, we will look up the maximum ToA over the last transmissions. Introduce a buffer in which we write the last 16 measurements. --- openair1/PHY/INIT/nr_init_ue.c | 2 ++ openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c | 5 ++++- openair1/PHY/defs_nr_common.h | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index e617b08172..2dce10d9dc 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -157,6 +157,8 @@ void init_nr_prs_ue_vars(PHY_VARS_NR_UE *ue) for (int j=0; jnb_antennas_rx; j++) { prs_vars[idx]->prs_resource[k].prs_meas[j] = malloc16_clear(sizeof(prs_meas_t)); AssertFatal((prs_vars[idx]->prs_resource[k].prs_meas[j]!=NULL), "%s: PRS measurements malloc failed for gNB_id %d, rx_ant %d\n", __FUNCTION__, idx, j); + prs_meas_t *m = prs_vars[idx]->prs_resource[k].prs_meas[j]; + m->next_dl_toa = m->dl_toa; } } } diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index 4151752f11..3af0937c48 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -73,7 +73,10 @@ void peak_estimator(c16_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t * void set_prs_dl_toa(prs_meas_t *prs_meas, float dl_toa) { - prs_meas->dl_toa = dl_toa; + *prs_meas->next_dl_toa++ = dl_toa; + if (prs_meas->next_dl_toa >= prs_meas->dl_toa + sizeofArray(prs_meas->dl_toa)) + prs_meas->next_dl_toa = prs_meas->dl_toa; + LOG_D(NR_PHY, "next_dl_toa %p\n", prs_meas->next_dl_toa); } int nr_prs_channel_estimation(uint8_t gNB_id, diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h index f4681020eb..b4d61d32f9 100644 --- a/openair1/PHY/defs_nr_common.h +++ b/openair1/PHY/defs_nr_common.h @@ -312,7 +312,9 @@ typedef struct { int32_t sfn; int8_t slot; int8_t rxAnt_idx; - float dl_toa; + // circular buffer to be able to read maximum of last estimations + float dl_toa[128]; // set through set_prs_dl_toa() + float *next_dl_toa; int32_t dl_aoa; float snr; float rsrp; From d682b6c5123012c3a356e71ba802141c28e49cd9 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 19 Sep 2025 12:44:37 +0200 Subject: [PATCH 164/275] Add function to read max DL ToA Protect with mutex to allow reading from another thread. --- openair1/PHY/INIT/nr_init_ue.c | 1 + .../nr_dl_channel_estimation.c | 18 ++++++++++++++++++ openair1/PHY/defs_nr_common.h | 3 +++ 3 files changed, 22 insertions(+) diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index 2dce10d9dc..7ab8a8863d 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -159,6 +159,7 @@ void init_nr_prs_ue_vars(PHY_VARS_NR_UE *ue) AssertFatal((prs_vars[idx]->prs_resource[k].prs_meas[j]!=NULL), "%s: PRS measurements malloc failed for gNB_id %d, rx_ant %d\n", __FUNCTION__, idx, j); prs_meas_t *m = prs_vars[idx]->prs_resource[k].prs_meas[j]; m->next_dl_toa = m->dl_toa; + pthread_mutex_init(&m->dl_toa_mtx, NULL); } } } diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index 3af0937c48..de0b6d2b29 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -73,10 +73,28 @@ void peak_estimator(c16_t *buffer, int32_t buf_len, int32_t *peak_idx, int32_t * void set_prs_dl_toa(prs_meas_t *prs_meas, float dl_toa) { + int rc = pthread_mutex_lock(&prs_meas->dl_toa_mtx); + AssertFatal(rc == 0, "pthread_mutex_lock() failed: errno %d, %s\n", errno, strerror(errno)); *prs_meas->next_dl_toa++ = dl_toa; if (prs_meas->next_dl_toa >= prs_meas->dl_toa + sizeofArray(prs_meas->dl_toa)) prs_meas->next_dl_toa = prs_meas->dl_toa; LOG_D(NR_PHY, "next_dl_toa %p\n", prs_meas->next_dl_toa); + rc = pthread_mutex_unlock(&prs_meas->dl_toa_mtx); + AssertFatal(rc == 0, "pthread_mutex_unlock() failed: errno %d, %s\n", errno, strerror(errno)); +} + +float get_prs_max_dl_toa(prs_meas_t *prs_meas) +{ + float max = 0.0f; + int rc = pthread_mutex_lock(&prs_meas->dl_toa_mtx); + AssertFatal(rc == 0, "pthread_mutex_lock() failed: errno %d, %s\n", errno, strerror(errno)); + for (int i = 0; i < sizeofArray(prs_meas->dl_toa); ++i) { + max = cmax(max, prs_meas->dl_toa[i]); + LOG_I(NR_PHY, "i %d dl_toa %.3f max %.3f\n", i, prs_meas->dl_toa[i], max); + } + rc = pthread_mutex_unlock(&prs_meas->dl_toa_mtx); + AssertFatal(rc == 0, "pthread_mutex_unlock() failed: errno %d, %s\n", errno, strerror(errno)); + return max; } int nr_prs_channel_estimation(uint8_t gNB_id, diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h index b4d61d32f9..23972fc9f2 100644 --- a/openair1/PHY/defs_nr_common.h +++ b/openair1/PHY/defs_nr_common.h @@ -38,6 +38,8 @@ #include "impl_defs_nr.h" #include "PHY/CODING/nrPolar_tools/nr_polar_defs.h" +#include + #define MAX_NUM_SUBCARRIER_SPACING 5 #define NR_MAX_OFDM_SYMBOL_SIZE 8192 @@ -312,6 +314,7 @@ typedef struct { int32_t sfn; int8_t slot; int8_t rxAnt_idx; + pthread_mutex_t dl_toa_mtx; // protect reading of max from write // circular buffer to be able to read maximum of last estimations float dl_toa[128]; // set through set_prs_dl_toa() float *next_dl_toa; From 34e002a80063bbb05633971957290a3231f57da9 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 19 Sep 2025 12:59:01 +0200 Subject: [PATCH 165/275] Add get_max_dl_toa() for ciUE telnet module --- common/utils/telnetsrv/telnetsrv_ciUE.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/common/utils/telnetsrv/telnetsrv_ciUE.c b/common/utils/telnetsrv/telnetsrv_ciUE.c index 90e044ed86..88320a6ce0 100644 --- a/common/utils/telnetsrv/telnetsrv_ciUE.c +++ b/common/utils/telnetsrv/telnetsrv_ciUE.c @@ -37,6 +37,8 @@ #include "openair2/LAYER2/NR_MAC_UE/mac_defs.h" #include "openair2/LAYER2/NR_MAC_UE/mac_proto.h" #include "openair2/RRC/NR_UE/rrc_proto.h" +#include "openair1/PHY/phy_extern_nr_ue.h" +#include "openair1/PHY/defs_nr_common.h" #define TELNETSERVERCODE #include "telnetsrv.h" @@ -110,6 +112,26 @@ static int force_deregistration(char *buf, int debug, telnet_printfunc_t prnt) return 0; } +extern float get_prs_max_dl_toa(prs_meas_t *prs_meas); +static int get_dl_toa(char *buf, int debug, telnet_printfunc_t prnt) +{ + // TODO multiple antennas, resources, gNBs? + int gNB_id = 0; + int rsc_id = 0; + int ant = 0; + + PHY_VARS_NR_UE *UE = PHY_vars_UE_g[0][0]; + if (!UE || !UE->prs_vars[gNB_id]) + ERROR_MSG_RET("no UE/prs_vars found!\n"); + NR_PRS_RESOURCE_t *prs_res = &UE->prs_vars[gNB_id]->prs_resource[rsc_id]; + if (!prs_res->prs_meas || !prs_res->prs_meas[ant]) + ERROR_MSG_RET("prs_meas not initialized!\n"); + + float max = get_prs_max_dl_toa(prs_res->prs_meas[ant]); + prnt("UE max PRS DL ToA %.3f\n", max); + return 0; +} + /* Telnet shell command definitions */ static telnetshell_cmddef_t cicmds[] = { {"sync_state", "[UE_ID(int,opt)]", get_sync_state}, @@ -117,6 +139,7 @@ static telnetshell_cmddef_t cicmds[] = { {"force_RRC_IDLE", "", force_RRC_IDLE}, {"force_crnti_ra", "", force_crnti_ra}, {"deregistration", "", force_deregistration}, + {"get_max_dl_toa", "[ant]", get_dl_toa}, {"", "", NULL}, }; From 1dca20ba85cac8228bf44a34c492271685419a9c Mon Sep 17 00:00:00 2001 From: rakesh mundlamuri Date: Tue, 23 Sep 2025 14:34:09 +0530 Subject: [PATCH 166/275] Introduce a bash script to test PRS using estimated ToA Set RFsim (DL) distance and use PRS to estimate the new distance. This is verified by comparing the distance set in RFsim with the distance reported through ToA estimation at the UE. Note that since RFsim telnet output is asynchronous (it uses a queue internally), we use grep --max-count 1 to wait for the matching line, together with ncat --idle 0.3 to keep ncat open for some time. The --max-count option will terminate grep after the first occurrence. For this to work reliably, we need to use grep <(echo | ncat) as opposod to simply echo | ncat | grep, as the latter does not seem to reliably make grep exit after the occurrence. Co-authored-by: Robert Schmidt --- .../scripts/set-and-verify-distance-prs.sh | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 ci-scripts/scripts/set-and-verify-distance-prs.sh diff --git a/ci-scripts/scripts/set-and-verify-distance-prs.sh b/ci-scripts/scripts/set-and-verify-distance-prs.sh new file mode 100755 index 0000000000..e54c8897a2 --- /dev/null +++ b/ci-scripts/scripts/set-and-verify-distance-prs.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +die() { echo -e "$@"; exit 1; } + +[[ -z "$1" ]] && die "Usage: $0 " + +IP=192.168.71.150 +PORT=8091 +distance=$1 + +set_and_verify_distance() { + local distance=$1 + echo "Testing PRS ToA estimation for distance: $distance m" + + # it sems that grep returns immediately with this syntax, but not echo | ncat | grep + # so prefer this to receive new distance immediately. We use --idle to keep + # ncat open for some additional time + local setdist_resp="$(grep --max-count 1 new_offset <(echo rfsimu setdistance rfsimu_channel_enB0 $distance | ncat --idle 1 ${IP} ${PORT}))" + echo "> response: ${setdist_resp}" + local gettoa_resp="$(echo ciUE get_max_dl_toa | ncat ${IP} ${PORT} | grep "UE max PRS DL ToA")" + echo "> response: ${gettoa_resp}" + + [[ "$setdist_resp" =~ new_offset\ ([0-9]+) ]] || die "Set ToA extraction failed for distance: $distance m\nLOG: $setdist_resp" + local set_toa="${BASH_REMATCH[1]}" + + [[ "$gettoa_resp" =~ UE\ max\ PRS\ DL\ ToA\ ([0-9]+) ]] || die "Estimated ToA extraction failed for distance: $distance m\nLOG: $gettoa_resp" + local est_toa="${BASH_REMATCH[1]}" + + [[ $set_toa == $est_toa ]] || die "PRS FAILURE for distance: $distance m\nActual ToA = $set_toa, Estimated ToA = $est_toa" + + echo "PRS SUCCESS for distance: $distance m" +} + +# Set distance to 0 initially and then set the actual distance +sleep 4 +set_and_verify_distance 0 + +sleep 4 +set_and_verify_distance $distance From 0d2212ca38e6da4dd4873de98d6a0f09bd278114 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Wed, 24 Sep 2025 08:01:35 +0000 Subject: [PATCH 167/275] CI: Extend Custom_Script to accept arguments --- ci-scripts/cls_oaicitest.py | 4 ++-- ci-scripts/main.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 144bc24da6..485653851c 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -233,10 +233,10 @@ def Custom_Command(HTML, node, command): HTML.CreateHtmlTestRowQueue(command, status, message) return status == 'OK' or status == 'Warning' -def Custom_Script(HTML, node, script): +def Custom_Script(HTML, node, script, args): logging.info(f"Executing custom script on {node}") with cls_cmd.getConnection(node) as c: - ret = c.exec_script(script, 90) + ret = c.exec_script(script, 90, args) logging.debug(f"Custom_Script: {script} on node: {node} - return code {ret.returncode}, output:\n{ret.stdout}") status = 'OK' message = [ret.stdout] diff --git a/ci-scripts/main.py b/ci-scripts/main.py index ac4ac2c3be..ff0b9ddb18 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -328,9 +328,10 @@ def ExecuteActionWithParam(action, ctx): elif action == 'Custom_Script': node = test.findtext('node') script = test.findtext('script') + args = test.findtext('args') # Allow referencing repository workspace path in XML via %%workspace%% script = script.replace("%%workspace%%", CONTAINERS.eNBSourceCodePath) - success = cls_oaicitest.Custom_Script(HTML, node, script) + success = cls_oaicitest.Custom_Script(HTML, node, script, args) elif action == 'Pull_Cluster_Image': tag_prefix = test.findtext('tag_prefix') or "" From 871b813c12c89b8b1e54f22432bb211f43517939 Mon Sep 17 00:00:00 2001 From: Rakesh Mundlamuri Date: Mon, 3 Nov 2025 11:45:49 +0530 Subject: [PATCH 168/275] Modify the script with a retry logic for a failed test and print summary This commit inputs several distances at once and obtain the summary of successful and failed tests. We look for the number of successful tests that are geater than 0. We do that here since we sometimes dont receive a response for a telnet command from the rfsim although the command is applied. This is related to the queing telnet commands in the rfsim. The script for the distances 50, 100 and 150 are called as follows, ./set-and-verify-distance-prs.sh 50 100 150 The summary can be viewed as follows, ==================== SUMMARY ==================== Total tests run : 3 Successful tests: 3 Failed tests : 0 ================================================= --- .../scripts/set-and-verify-distance-prs.sh | 92 ++++++++++++++----- 1 file changed, 71 insertions(+), 21 deletions(-) diff --git a/ci-scripts/scripts/set-and-verify-distance-prs.sh b/ci-scripts/scripts/set-and-verify-distance-prs.sh index e54c8897a2..73012a4484 100755 --- a/ci-scripts/scripts/set-and-verify-distance-prs.sh +++ b/ci-scripts/scripts/set-and-verify-distance-prs.sh @@ -1,39 +1,89 @@ #!/bin/bash -die() { echo -e "$@"; exit 1; } - -[[ -z "$1" ]] && die "Usage: $0 " +[[ $# -lt 1 ]] && { echo "Usage: $0 "; exit 1; } +DISTANCES=("$@") IP=192.168.71.150 PORT=8091 -distance=$1 +NCAT_TIMEOUT=1 #s +SLEEP_WAIT=4 #s +MAX_RETRIES=3 set_and_verify_distance() { local distance=$1 echo "Testing PRS ToA estimation for distance: $distance m" - # it sems that grep returns immediately with this syntax, but not echo | ncat | grep + # it seems that grep returns immediately with this syntax, but not echo | ncat | grep # so prefer this to receive new distance immediately. We use --idle to keep # ncat open for some additional time - local setdist_resp="$(grep --max-count 1 new_offset <(echo rfsimu setdistance rfsimu_channel_enB0 $distance | ncat --idle 1 ${IP} ${PORT}))" + local setdist_resp="$(grep --max-count 1 new_offset <(echo rfsimu setdistance rfsimu_channel_enB0 $distance | ncat --idle ${NCAT_TIMEOUT} ${IP} ${PORT}))" echo "> response: ${setdist_resp}" - local gettoa_resp="$(echo ciUE get_max_dl_toa | ncat ${IP} ${PORT} | grep "UE max PRS DL ToA")" + + local gettoa_resp="$(echo "ciUE get_max_dl_toa" | ncat ${IP} ${PORT} | grep "UE max PRS DL ToA")" echo "> response: ${gettoa_resp}" - [[ "$setdist_resp" =~ new_offset\ ([0-9]+) ]] || die "Set ToA extraction failed for distance: $distance m\nLOG: $setdist_resp" - local set_toa="${BASH_REMATCH[1]}" - - [[ "$gettoa_resp" =~ UE\ max\ PRS\ DL\ ToA\ ([0-9]+) ]] || die "Estimated ToA extraction failed for distance: $distance m\nLOG: $gettoa_resp" - local est_toa="${BASH_REMATCH[1]}" - - [[ $set_toa == $est_toa ]] || die "PRS FAILURE for distance: $distance m\nActual ToA = $set_toa, Estimated ToA = $est_toa" - - echo "PRS SUCCESS for distance: $distance m" + [[ -z "$setdist_resp" || -z "$gettoa_resp" ]] && return 1 + + # Extract ToA values + [[ "$setdist_resp" =~ new_offset\ ([0-9]+) ]] && local set_toa="${BASH_REMATCH[1]}" || { echo "> Set ToA extraction failed for distance: $distance m"; return 1; } + + [[ "$gettoa_resp" =~ UE\ max\ PRS\ DL\ ToA\ ([0-9]+) ]] && local est_toa="${BASH_REMATCH[1]}" || { echo "> Estimated ToA extraction failed for distance: $distance m"; return 1; } + + # Compare extracted ToA values + [[ $set_toa == $est_toa ]] && echo "PRS SUCCESS for distance: $distance m" || { echo "PRS FAILURE for distance: $distance m (Actual ToA=$set_toa, Estimated ToA=$est_toa)" ; return 1; } + } -# Set distance to 0 initially and then set the actual distance -sleep 4 -set_and_verify_distance 0 +test_distance() { + local distance=$1 retries=0 -sleep 4 -set_and_verify_distance $distance + # retry loop incase we didn't receive the response + while (( retries < MAX_RETRIES )); do + echo " Attempt $((retries + 1))/$MAX_RETRIES" + + # Always reset to 0 m before testing target distance + if ! set_and_verify_distance 0; then + echo " Set distance 0 m failed during attempt $((retries + 1))" + else + sleep "$SLEEP_WAIT" + # Now test the actual target distance + if set_and_verify_distance "$distance"; then + return 0 + fi + fi + + ((retries++)) + if (( retries < MAX_RETRIES )); then + sleep "$SLEEP_WAIT" + else + echo " ERROR: No valid response after $MAX_RETRIES retries for distance: $distance m" + return 1 + fi + done +} + +num_success=0 +num_fail=0 + +for d in "${DISTANCES[@]}"; do + if test_distance "$d"; then + ((num_success++)) + else + ((num_fail++)) + fi + sleep "$SLEEP_WAIT" +done + +# ---- Summary ---- +echo +echo "==================== SUMMARY ====================" +echo "Total tests run : ${#DISTANCES[@]}" +echo "Successful tests: ${num_success}" +echo "Failed tests : ${num_fail}" +echo "=================================================" + +if [ $num_success -gt 0 ]; then + exit 0 +else + exit 1 +fi From c625a95042e110f9628f840f2a19eaca49ed33b3 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Fri, 12 Sep 2025 12:55:22 +0200 Subject: [PATCH 169/275] CI: add rfsim test with PRS Use the bash script from a parent commit to verify PRS functionality. --- .../gnb.sa.band78.106prb.rfsim.prs.conf | 256 ++++++++++++++++++ .../conf_files/nrue.band78.106prb.prs.conf | 89 ++++++ .../xml_files/container_5g_rfsim_prs.xml | 100 +++++++ .../5g_rfsimulator_prs/docker-compose.yaml | 71 +++++ 4 files changed, 516 insertions(+) create mode 100644 ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.prs.conf create mode 100644 ci-scripts/conf_files/nrue.band78.106prb.prs.conf create mode 100644 ci-scripts/xml_files/container_5g_rfsim_prs.xml create mode 100644 ci-scripts/yaml_files/5g_rfsimulator_prs/docker-compose.yaml diff --git a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.prs.conf b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.prs.conf new file mode 100644 index 0000000000..3b6b591d85 --- /dev/null +++ b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.prs.conf @@ -0,0 +1,256 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + + plmn_list = ({mcc = 208; mnc = 97; mnc_length = 2;}); + + ////////// Physical parameters: + + min_rxtxtime = 6; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3300.60 MHz + 53*12*30e-3 MHz = 3319.68 + absoluteFrequencySSB = 621312; + # this is 3300.60 MHz + dl_absoluteFrequencyPointA = 620040; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=0,L=106 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 11; + initialDLBWPsearchSpaceZero = 0; + +#uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 12; + preambleReceivedTargetPower = -104; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#one (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; + + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spar e1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + +#tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + ////////// AMF parameters: + amf_ip_address = ({ ipv4 = "CI_MME_IP_ADDR"; }); + + NETWORK_INTERFACES : + { + + GNB_IPV4_ADDRESS_FOR_NG_AMF = "CI_GNB_IP_ADDR"; + GNB_IPV4_ADDRESS_FOR_NGU = "CI_GNB_IP_ADDR"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 200; + pucch_TargetSNRx10 = 200; +} +); + +prs_config = ( +{ + NumPRSResources = 1; + PRSResourceSetPeriod = [20, 2]; + SymbolStart = [7]; + NumPRSSymbols = [6]; + NumRB = 106; + RBOffset = 0; + CombSize = 4; + REOffset = [0]; + PRSResourceOffset = [0]; + PRSResourceRepetition = 1; + PRSResourceTimeGap = 1; + NPRS_ID = [0]; + MutingPattern1 = []; + MutingPattern2 = []; + MutingBitRepetition = 1; +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; +} +); + +RUs = ( +{ + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + sf_extension = 0; + eNB_instances = [0]; +} +); + + +rfsimulator : +{ + serveraddr = "server"; + serverport = 4043; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs"; +}; + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : +{ + global_log_level = "info"; + hw_log_level = "info"; + phy_log_level = "info"; + mac_log_level = "info"; + rlc_log_level = "info"; + pdcp_log_level = "info"; + rrc_log_level = "info"; + f1ap_log_level = "debug"; +}; diff --git a/ci-scripts/conf_files/nrue.band78.106prb.prs.conf b/ci-scripts/conf_files/nrue.band78.106prb.prs.conf new file mode 100644 index 0000000000..1bf92fd0d6 --- /dev/null +++ b/ci-scripts/conf_files/nrue.band78.106prb.prs.conf @@ -0,0 +1,89 @@ +PRSs = +( + { + Active_gNBs = 1; + prs_config0 = ( + { + gNB_id = 0; + NumPRSResources = 1; + PRSResourceSetPeriod = [20, 2]; + SymbolStart = [7]; + NumPRSSymbols = [6]; + NumRB = 106; + RBOffset = 0; + CombSize = 4; + REOffset = [0]; + PRSResourceOffset = [0]; + PRSResourceRepetition = 1; + PRSResourceTimeGap = 1; + NPRS_ID = [0]; + MutingPattern1 = []; + MutingPattern2 = []; + MutingBitRepetition = 1; + } + ); + + prs_config1 = ( + { + gNB_id = 1; + NumPRSResources = 1; + PRSResourceSetPeriod = [20, 2]; + SymbolStart = [7]; + NumPRSSymbols = [6]; + NumRB = 106; + RBOffset = 0; + CombSize = 4; + REOffset = [0]; + PRSResourceOffset = [1]; + PRSResourceRepetition = 1; + PRSResourceTimeGap = 1; + NPRS_ID = [1]; + MutingPattern1 = []; + MutingPattern2 = []; + MutingBitRepetition = 1; + } + ); + + prs_config2 = ( + { + gNB_id = 2; + NumPRSResources = 1; + PRSResourceSetPeriod = [20, 2]; + SymbolStart = [7]; + NumPRSSymbols = [6]; + NumRB = 106; + RBOffset = 0; + CombSize = 4; + REOffset = [0]; + PRSResourceOffset = [2]; + PRSResourceRepetition = 1; + PRSResourceTimeGap = 1; + NPRS_ID = [2]; + MutingPattern1 = []; + MutingPattern2 = []; + MutingBitRepetition = 1; + } + ); + + prs_config3 = ( + { + gNB_id = 3; + NumPRSResources = 1; + PRSResourceSetPeriod = [20, 2]; + SymbolStart = [7]; + NumPRSSymbols = [6]; + NumRB = 106; + RBOffset = 0; + CombSize = 4; + REOffset = [0]; + PRSResourceOffset = [3]; + PRSResourceRepetition = 1; + PRSResourceTimeGap = 1; + NPRS_ID = [3]; + MutingPattern1 = []; + MutingPattern2 = []; + MutingBitRepetition = 1; + } + ); + } +); diff --git a/ci-scripts/xml_files/container_5g_rfsim_prs.xml b/ci-scripts/xml_files/container_5g_rfsim_prs.xml new file mode 100644 index 0000000000..456ae62c1a --- /dev/null +++ b/ci-scripts/xml_files/container_5g_rfsim_prs.xml @@ -0,0 +1,100 @@ + + + rfsim-5gnr-prs + Monolithic gNB with PRS + wrench + + 111111 + 800813 + 000012 + 000013 + 040001 + 040023 + 100011 + 222222 + + + + + Pull_Local_Registry + Pull Images from Local Registry + localhost + oai-gnb-asan oai-nr-ue-asan + + + + Create_Workspace + Create new Workspace + localhost + + + + Deploy_Object + Deploy OAI 5G gNB+UE RF sim SA + localhost + ci-scripts/yaml_files/5g_rfsimulator_prs + oai-gnb oai-nr-ue + + + + Attach_UE + Attach OAI UE (Wait for IP) + rfsim5g_ue + localhost + + + + IdleSleep + Sleep + 2 + + + + Custom_Script + true + Set and verify distance at 50, 100, 150 m + localhost + + 50 100 150 + + + + Undeploy_Object + true + Undeploy all OAI 5G stack + localhost + ci-scripts/yaml_files/5g_rfsimulator_prs + 30,100,100,100 + 30,100,100,100 + + + + Clean_Test_Server_Images + true + Clean Test Images on Test Server + localhost + oai-gnb-asan oai-nr-ue-asan + + + diff --git a/ci-scripts/yaml_files/5g_rfsimulator_prs/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_prs/docker-compose.yaml new file mode 100644 index 0000000000..c6b6c338de --- /dev/null +++ b/ci-scripts/yaml_files/5g_rfsimulator_prs/docker-compose.yaml @@ -0,0 +1,71 @@ +services: + oai-gnb: + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} + container_name: rfsim5g-oai-gnb + cap_drop: + - ALL + cap_add: + - NET_ADMIN # for interface bringup + - NET_RAW # for ping + environment: + USE_ADDITIONAL_OPTIONS: --phy-test --rfsim -E + --log_config.global_log_options level,nocolor,time + ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 + networks: + public_net: + ipv4_address: 192.168.71.140 + devices: + - /dev/net/tun:/dev/net/tun + volumes: + - ../../conf_files/gnb.sa.band78.106prb.rfsim.prs.conf:/opt/oai-gnb/etc/gnb.conf + - rrc.config:/opt/oai-gnb/ + healthcheck: + test: /bin/bash -c "pgrep nr-softmodem" + interval: 10s + timeout: 5s + retries: 5 + + oai-nr-ue: + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + container_name: rfsim5g-oai-nr-ue + cap_drop: + - ALL + cap_add: + - NET_ADMIN # for interface bringup + - NET_RAW # for ping + environment: + USE_ADDITIONAL_OPTIONS: --phy-test --rfsim -E + --reconfig-file etc/rrc/reconfig.raw --rbconfig-file etc/rrc/rbconfig.raw + --rfsimulator.serveraddr 192.168.71.140 + --log_config.global_log_options level,nocolor,time + --telnetsrv --telnetsrv.listenaddr 192.168.71.150 --telnetsrv.listenport 8091 + --telnetsrv.shrmod ciUE + ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 + devices: + - /dev/net/tun:/dev/net/tun + volumes: + - ../../conf_files/nrue.band78.106prb.prs.conf:/opt/oai-nr-ue/etc/nr-ue.conf + - rrc.config:/opt/oai-nr-ue/etc/rrc/ + depends_on: + - oai-gnb + networks: + public_net: + ipv4_address: 192.168.71.150 + healthcheck: + test: /bin/bash -c "pgrep nr-uesoftmodem" + interval: 10s + timeout: 5s + retries: 5 + +volumes: + rrc.config: + +networks: + public_net: + driver: bridge + name: rfsim5g-oai-public-net + ipam: + config: + - subnet: 192.168.71.128/26 + driver_opts: + com.docker.network.bridge.name: "rfsim5g-public" From d4aac07ae9d8f84beb0e42d9e6ea25d1cec58f29 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Nov 2025 11:13:31 +0100 Subject: [PATCH 170/275] Remove write-only code --- ci-scripts/cls_oaicitest.py | 3 +-- ci-scripts/main.py | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index f64972f253..cd5f8ecdf7 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -299,7 +299,6 @@ class OaiCiTest(): self.iperf_packetloss_threshold = '' self.iperf_bitrate_threshold = '' self.iperf_profile = '' - self.iperf_options = '' self.iperf_tcp_rate_target = '' self.finalStatus = False self.air_interface='' @@ -520,7 +519,7 @@ class OaiCiTest(): return (status, f'{ue_header}\n{msg}') def Iperf(self, ctx, node, HTML, infra_file="ci_infra.yaml"): - logging.debug(f'Iperf: iperf_args "{self.iperf_args}" iperf_packetloss_threshold "{self.iperf_packetloss_threshold}" iperf_bitrate_threshold "{self.iperf_bitrate_threshold}" iperf_profile "{self.iperf_profile}" iperf_options "{self.iperf_options}"') + logging.debug(f'Iperf: iperf_args "{self.iperf_args}" iperf_packetloss_threshold "{self.iperf_packetloss_threshold}" iperf_bitrate_threshold "{self.iperf_bitrate_threshold}" iperf_profile "{self.iperf_profile}"') if self.ue_ids == [] or self.svr_id == None: raise Exception("no module names in self.ue_ids or/and self.svr_id provided") diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 99500fff12..f427d1c023 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -185,10 +185,6 @@ def ExecuteActionWithParam(action, ctx, node): if CiTestObj.iperf_profile != 'balanced' and CiTestObj.iperf_profile != 'unbalanced' and CiTestObj.iperf_profile != 'single-ue': logging.error(f'test-case has wrong profile {CiTestObj.iperf_profile}, forcing balanced') CiTestObj.iperf_profile = 'balanced' - CiTestObj.iperf_options = test.findtext('iperf_options') or 'check' - if CiTestObj.iperf_options != 'check' and CiTestObj.iperf_options != 'sink': - logging.error('test-case has wrong option ' + CiTestObj.iperf_options) - CiTestObj.iperf_options = 'check' if action == 'Iperf': success = CiTestObj.Iperf(ctx, node, HTML) elif action == 'Iperf2_Unidir': From db09f50ae455302605f4166416abdb6df7983957 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Wed, 15 Oct 2025 16:50:49 +0200 Subject: [PATCH 171/275] phy_procedures_gNB_uespec_RX is too long function with too long lines of code, this commit only shorten lines and improve a bit the readability --- .../PHY/nr_phy_common/inc/nr_phy_common.h | 2 +- .../PHY/nr_phy_common/src/nr_phy_common_srs.c | 2 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 883 +++++++++--------- 3 files changed, 449 insertions(+), 438 deletions(-) diff --git a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h index 8303c510c7..a2011e1fa6 100644 --- a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h +++ b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h @@ -365,7 +365,7 @@ void nr_channel_level(const int symbol, int32_t avg[nb_rx * Nl], const uint32_t len); void nr_scale_channel(int size, int ch_estimates_ext[][size], int symb, uint32_t len, int nrOfLayers, int nb_rx, int shift_ch_ext); -bool generate_srs_nr(NR_DL_FRAME_PARMS *frame_parms, +bool generate_srs_nr(const NR_DL_FRAME_PARMS *frame_parms, c16_t **txdataF, uint16_t symbol_offset, int bwp_start, 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 ba5d792428..ad21978e06 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 @@ -139,7 +139,7 @@ static int compute_n_b(frame_t frame_number, * See TS 38.211 - Section 6.4.1.4 Sounding reference signal * ***************************************************************************/ -bool generate_srs_nr(NR_DL_FRAME_PARMS *frame_parms, +bool generate_srs_nr(const NR_DL_FRAME_PARMS *frame_parms, c16_t **txdataF, uint16_t symbol_offset, int bwp_start, diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 51e8c771e5..44d0a24d1c 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -627,18 +627,18 @@ 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, uint32_t rb_mask_ul[14][9]) +static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, + int frame_rx, + int slot_rx, + uint32_t rb_mask_ul[14][9], + nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf) { - int rb = 0; - int rb2 = 0; - int prbpos = 0; - 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++) { - prbpos = (m * 32) + i; - if (prbpos>gNB->frame_parms.N_RB_UL) + 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; } @@ -646,23 +646,27 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, uint32 } for (int i = 0; i < gNB->max_nb_pucch; i++){ - NR_gNB_PUCCH_t *pucch = &gNB->pucch[i]; - if (pucch) { - if ((pucch->active == 1) && - (pucch->frame == frame_rx) && - (pucch->slot == slot_rx) ) { - nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; - LOG_D(PHY,"%d.%d pucch %d : start_symbol %d, nb_symbols %d, prb_size %d\n",frame_rx,slot_rx,i,pucch_pdu->start_symbol_index,pucch_pdu->nr_of_symbols,pucch_pdu->prb_size); - for (int symbol=pucch_pdu->start_symbol_index ; symbol<(pucch_pdu->start_symbol_index+pucch_pdu->nr_of_symbols);symbol++) { - if(gNB->frame_parms.frame_type == FDD || - (gNB->frame_parms.frame_type == TDD && gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list[symbol].slot_config.value==1)) { - for (rb=0; rbprb_size; rb++) { - rb2 = rb + pucch_pdu->bwp_start + - ((symbol < pucch_pdu->start_symbol_index+(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] |= (((uint32_t)1)<<(rb2&31)); - } - } + 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; + 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++) { + 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); } } } @@ -672,21 +676,16 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, uint32 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 == true) && (ulsch->frame == frame_rx) && (ulsch->slot == slot_rx) && (ulsch->handled == 0)) { - 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 symbol = symbol_start; symbol < symbol_end; symbol++) { - if (gNB->frame_parms.frame_type == FDD - || (gNB->frame_parms.frame_type == TDD - && gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx] - .max_num_of_symbol_per_slot_list[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 (rb = 0; rb < ulsch_harq->ulsch_pdu.rb_size; rb++) { - rb2 = rb + ulsch_harq->ulsch_pdu.rb_start + ulsch_harq->ulsch_pdu.bwp_start; - rb_mask_ul[symbol][rb2 >> 5] |= 1U << (rb2 & 31); - } + if (!(ulsch->active && ulsch->frame == frame_rx && ulsch->slot == slot_rx && !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 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; + rb_mask_ul[symbol][rb2 >> 5] |= 1U << (rb2 & 31); } } } @@ -694,14 +693,12 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, uint32 for (int i = 0; i < gNB->max_nb_srs; i++) { NR_gNB_SRS_t *srs = &gNB->srs[i]; - if (srs) { - if ((srs->active == 1) && (srs->frame == frame_rx) && (srs->slot == slot_rx)) { - nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; - for(int symbol = 0; symbol < (1 << srs_pdu->num_symbols); symbol++) { - for(rb = srs_pdu->bwp_start; rb < (srs_pdu->bwp_start+srs_pdu->bwp_size); rb++) { - rb_mask_ul[srs_pdu->time_start_position + symbol][rb >> 5] |= 1U << (rb & 31); - } - } + if (!(srs && srs->active && srs->frame == frame_rx && srs->slot == slot_rx)) + continue; + nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + 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[srs_pdu->time_start_position + symbol][rb >> 5] |= 1U << (rb & 31); } } } @@ -728,18 +725,25 @@ int fill_srs_reported_symbol(nfapi_nr_srs_reported_symbol_t *reported_symbol, return 0; } -int fill_srs_channel_matrix(uint8_t *channel_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 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)]) { + 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; @@ -837,75 +841,73 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N int pusch_decode_done = 0; int pusch_DTX = 0; + const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; + const int symb_sz = frame_parms->ofdm_symbol_size; + const int nb_symb = frame_parms->symbols_per_slot; + const int ant = gNB->frame_parms.nb_antennas_rx; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_UESPEC_RX,1); 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]; - fill_ul_rb_mask(gNB, frame_rx, slot_rx, rb_mask_ul); + { + // 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, frame_rx, slot_rx, rb_mask_ul, slot_conf); - int first_symb=0,num_symb=0; - if (gNB->frame_parms.frame_type == TDD) - for(int symbol_count=0; symbol_countgNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list[symbol_count].slot_config.value==1) { - if (num_symb==0) first_symb=symbol_count; - num_symb++; + int first_symb = 0, num_symb = 0; + if (frame_parms->frame_type == TDD) + for (int symbol_count = 0; symbol_count < NR_NUMBER_OF_SYMBOLS_PER_SLOT; symbol_count++) { + if (slot_conf[symbol_count].slot_config.value == 1) { + if (num_symb == 0) + first_symb = symbol_count; + num_symb++; + } } - } - else - num_symb = NR_NUMBER_OF_SYMBOLS_PER_SLOT; - gNB_I0_measurements(gNB, slot_rx, first_symb, num_symb, rb_mask_ul); + else + num_symb = NR_NUMBER_OF_SYMBOLS_PER_SLOT; + gNB_I0_measurements(gNB, slot_rx, first_symb, num_symb, rb_mask_ul); + } - const int soffset = (slot_rx & 3) * gNB->frame_parms.symbols_per_slot * gNB->frame_parms.ofdm_symbol_size; + const int soffset = (slot_rx & 3) * nb_symb * symb_sz; 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) { - if (pucch->active && (pucch->frame == frame_rx) && (pucch->slot == slot_rx)) { - 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; - switch (pucch_pdu->format_type) { - case 0: - UL_INFO->uci_ind.sfn = frame_rx; - UL_INFO->uci_ind.slot = slot_rx; - 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 * gNB->frame_parms.ofdm_symbol_size - + (gNB->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); - - UL_INFO->uci_ind.num_ucis += 1; - pucch->active = false; - break; - case 2: - UL_INFO->uci_ind.sfn = frame_rx; - UL_INFO->uci_ind.slot = slot_rx; - 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); - - UL_INFO->uci_ind.num_ucis += 1; - pucch->active = false; - break; - default: - AssertFatal(1 == 0, "Only PUCCH formats 0 and 2 are currently supported\n"); - } - } + 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 * symb_sz + (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"); } + UL_INFO->uci_ind.num_ucis += 1; + pucch->active = false; } UL_INFO->crc_ind.sfn = frame_rx; @@ -915,104 +917,103 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N 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((void *)ulsch_to_decode, sizeof(ulsch_to_decode)); + bzero(ulsch_to_decode, sizeof(ulsch_to_decode)); 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 == true) && (ulsch->frame == frame_rx) && (ulsch->slot == slot_rx) && (ulsch->handled == 0)) { - 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); - nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu; - int num_dmrs = count_bits64_with_mask(pdu->ul_dmrs_symb_pos, 0, NR_NUMBER_OF_SYMBOLS_PER_SLOT); + 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); + nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu; + int num_dmrs = count_bits64_with_mask(pdu->ul_dmrs_symb_pos, 0, NR_NUMBER_OF_SYMBOLS_PER_SLOT); #ifdef DEBUG_RXDATA - NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - 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], - frame_parms->get_samples_per_slot(slot_rx, frame_parms) * sizeof(int32_t)); - gNB->common_vars.debugBuff_sample_offset += (frame_parms->get_samples_per_slot(slot_rx, frame_parms) + 1000 + 4); - if (gNB->common_vars.debugBuff_sample_offset > ((frame_parms->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, - (frame_parms->get_samples_per_slot(slot_rx, frame_parms) + 1000 + 4) * 20 * 2, - f); - fclose(f); - exit(-1); - } + 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], + frame_parms->get_samples_per_slot(slot_rx, frame_parms) * sizeof(int32_t)); + gNB->common_vars.debugBuff_sample_offset += (frame_parms->get_samples_per_slot(slot_rx, frame_parms) + 1000 + 4); + if (gNB->common_vars.debugBuff_sample_offset > ((frame_parms->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, + (frame_parms->get_samples_per_slot(slot_rx, frame_parms) + 1000 + 4) * 20 * 2, + f); + fclose(f); + exit(-1); + } #endif - pusch_decode_done = 1; + pusch_decode_done = 1; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RX_PUSCH, 1); - start_meas(&gNB->rx_pusch_stats); - nr_rx_pusch_tp(gNB, ULSCH_id, frame_rx, slot_rx, ulsch->harq_pid, ulsch->beam_nb); - NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id]; - 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[aarx] /= num_dmrs; - pusch_vars->ulsch_power_tot += pusch_vars->ulsch_power[aarx]; - pusch_vars->ulsch_noise_power[aarx] /= num_dmrs; - 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 *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); - 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_to_decode[ULSCH_id] = true; - stop_meas(&gNB->rx_pusch_stats); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RX_PUSCH, 0); - // 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); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RX_PUSCH, 1); + start_meas(&gNB->rx_pusch_stats); + nr_rx_pusch_tp(gNB, ULSCH_id, frame_rx, slot_rx, ulsch->harq_pid, ulsch->beam_nb); + NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id]; + pusch_vars->ulsch_power_tot = 0; + pusch_vars->ulsch_noise_power_tot = 0; + for (int aarx = 0; aarx < ant; aarx++) { + pusch_vars->ulsch_power[aarx] /= num_dmrs; + pusch_vars->ulsch_power_tot += pusch_vars->ulsch_power[aarx]; + pusch_vars->ulsch_noise_power[aarx] /= num_dmrs; + 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 *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); + 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_to_decode[ULSCH_id] = true; + stop_meas(&gNB->rx_pusch_stats); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RX_PUSCH, 0); + // 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); } /* Do ULSCH decoding time measurement only when number of PUSCH is limited to 1 @@ -1033,249 +1034,259 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N * measurement per processed TB.*/ 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) { - if ((srs->active == true) && (srs->frame == frame_rx) && (srs->slot == slot_rx)) { - LOG_D(NR_PHY, "(%d.%d) gNB is waiting for SRS, id = %i\n", frame_rx, slot_rx, 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); - start_meas(&gNB->rx_srs_stats); + start_meas(&gNB->rx_srs_stats); - NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; - uint8_t N_symb_SRS = 1 << srs_pdu->num_symbols; - c16_t srs_received_signal[frame_parms->nb_antennas_rx][frame_parms->ofdm_symbol_size * N_symb_SRS]; - c16_t srs_received_noise[frame_parms->nb_antennas_rx][frame_parms->ofdm_symbol_size * N_symb_SRS]; - c16_t srs_estimated_channel_freq[frame_parms->nb_antennas_rx][1 << srs_pdu->num_ant_ports] - [frame_parms->ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); - c16_t srs_estimated_channel_time[frame_parms->nb_antennas_rx][1 << srs_pdu->num_ant_ports][frame_parms->ofdm_symbol_size] - __attribute__((aligned(32))); - c16_t srs_estimated_channel_time_shifted[frame_parms->nb_antennas_rx][1 << srs_pdu->num_ant_ports] - [frame_parms->ofdm_symbol_size]; - int16_t snr_per_rb[srs_pdu->bwp_size]; + nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + uint8_t N_symb_SRS = 1 << srs_pdu->num_symbols; + c16_t srs_received_signal[ant][symb_sz * N_symb_SRS]; + c16_t srs_received_noise[ant][symb_sz * N_symb_SRS]; + c16_t srs_estimated_channel_freq[ant][1 << srs_pdu->num_ant_ports][symb_sz * N_symb_SRS] __attribute__((aligned(32))); + c16_t srs_estimated_channel_time[ant][1 << srs_pdu->num_ant_ports][symb_sz] __attribute__((aligned(32))); + c16_t srs_estimated_channel_time_shifted[ant][1 << srs_pdu->num_ant_ports][symb_sz]; + int16_t snr_per_rb[srs_pdu->bwp_size]; - start_meas(&gNB->generate_srs_stats); + start_meas(&gNB->generate_srs_stats); - if (check_and_configure_srs_info(srs_pdu, gNB->nr_srs_info[i])) { - generate_srs_nr(frame_parms, - gNB->nr_srs_info[i]->srs_generated_signal, - 0, - srs_pdu->bwp_start, - gNB->nr_srs_info[i], - AMP, - frame_rx, - slot_rx); - } - stop_meas(&gNB->generate_srs_stats); - c16_t **rxdataF = gNB->common_vars.rxdataF[srs->beam_nb]; - start_meas(&gNB->get_srs_signal_stats); - int srs_est = nr_get_srs_signal(gNB, rxdataF, frame_rx, slot_rx, srs_pdu, gNB->nr_srs_info[i], srs_received_signal, srs_received_noise); - stop_meas(&gNB->get_srs_signal_stats); - - if (srs_est >= 0) { - start_meas(&gNB->srs_channel_estimation_stats); - nr_srs_channel_estimation(gNB, - frame_rx, - slot_rx, - srs_pdu, - gNB->nr_srs_info[i], - (const c16_t**)gNB->nr_srs_info[i]->srs_generated_signal, - srs_received_signal, - srs_received_noise, - srs_estimated_channel_freq, - srs_estimated_channel_time, - srs_estimated_channel_time_shifted, - snr_per_rb, - &gNB->srs->snr); - stop_meas(&gNB->srs_channel_estimation_stats); - } - - if ((gNB->srs->snr * 10) < gNB->srs_thres) { - srs_est = -1; - } - - T(T_GNB_PHY_UL_FREQ_CHANNEL_ESTIMATE, - T_INT(0), - T_INT(srs_pdu->rnti), - T_INT(frame_rx), - T_INT(0), - T_INT(0), - T_BUFFER(srs_estimated_channel_freq[0][0], frame_parms->ofdm_symbol_size * sizeof(int32_t))); - - T(T_GNB_PHY_UL_TIME_CHANNEL_ESTIMATE, - T_INT(0), - T_INT(srs_pdu->rnti), - T_INT(frame_rx), - T_INT(0), - T_INT(0), - T_BUFFER(srs_estimated_channel_time_shifted[0][0], frame_parms->ofdm_symbol_size * sizeof(int32_t))); - - T(T_GNB_PHY_UL_SNR_ESTIMATE, - T_INT(0), - T_INT(srs_pdu->rnti), - T_INT(frame_rx), - T_INT(0), - T_INT(0), - T_BUFFER(snr_per_rb, srs_pdu->bwp_size * sizeof(int16_t))); - - 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; - start_meas(&gNB->srs_timing_advance_stats); - srs_indication->timing_advance_offset = srs_est >= 0 ? nr_est_timing_advance_srs(frame_parms, srs_estimated_channel_time[0]) : 0xFFFF; - stop_meas(&gNB->srs_timing_advance_stats); - srs_indication->timing_advance_offset_nsec = srs_est >= 0 ? (int16_t)((((int32_t)srs_indication->timing_advance_offset - 31) * ((int32_t)TC_NSEC_x32768)) >> 15) : 0xFFFF; - 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]; - -#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; - - 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 = 1 << srs_pdu->num_symbols; - 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 - nr_srs_bf_report.num_reported_symbols = 1 << srs_pdu->num_symbols; - 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, frame_parms->N_RB_UL, 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; - } - - 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; - nr_srs_channel_iq_matrix.normalized_iq_representation = srs_pdu->srs_parameters_v4.iq_representation; - nr_srs_channel_iq_matrix.num_gnb_antenna_elements = gNB->frame_parms.nb_antennas_rx; - nr_srs_channel_iq_matrix.num_ue_srs_ports = srs_pdu->srs_parameters_v4.num_total_ue_antennas; - nr_srs_channel_iq_matrix.prg_size = srs_pdu->srs_parameters_v4.prg_size; - nr_srs_channel_iq_matrix.num_prgs = srs_pdu->srs_parameters_v4.srs_bandwidth_size / srs_pdu->srs_parameters_v4.prg_size; - fill_srs_channel_matrix(nr_srs_channel_iq_matrix.channel_matrix, - srs_pdu, - gNB->nr_srs_info[i], - nr_srs_channel_iq_matrix.normalized_iq_representation, - nr_srs_channel_iq_matrix.num_gnb_antenna_elements, - nr_srs_channel_iq_matrix.num_ue_srs_ports, - nr_srs_channel_iq_matrix.prg_size, - nr_srs_channel_iq_matrix.num_prgs, - &gNB->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); - 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); - -#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); - } + if (check_and_configure_srs_info(srs_pdu, gNB->nr_srs_info[i])) { + generate_srs_nr(frame_parms, + gNB->nr_srs_info[i]->srs_generated_signal, + 0, + srs_pdu->bwp_start, + gNB->nr_srs_info[i], + AMP, + frame_rx, + slot_rx); } + stop_meas(&gNB->generate_srs_stats); + c16_t **rxdataF = gNB->common_vars.rxdataF[srs->beam_nb]; + start_meas(&gNB->get_srs_signal_stats); + int srs_est = + nr_get_srs_signal(gNB, rxdataF, frame_rx, slot_rx, srs_pdu, gNB->nr_srs_info[i], srs_received_signal, srs_received_noise); + stop_meas(&gNB->get_srs_signal_stats); + + if (srs_est >= 0) { + start_meas(&gNB->srs_channel_estimation_stats); + nr_srs_channel_estimation(gNB, + frame_rx, + slot_rx, + srs_pdu, + gNB->nr_srs_info[i], + (const c16_t **)gNB->nr_srs_info[i]->srs_generated_signal, + srs_received_signal, + srs_received_noise, + srs_estimated_channel_freq, + srs_estimated_channel_time, + srs_estimated_channel_time_shifted, + snr_per_rb, + &gNB->srs->snr); + stop_meas(&gNB->srs_channel_estimation_stats); + } + + if ((gNB->srs->snr * 10) < gNB->srs_thres) { + srs_est = -1; + } + + T(T_GNB_PHY_UL_FREQ_CHANNEL_ESTIMATE, + T_INT(0), + T_INT(srs_pdu->rnti), + T_INT(frame_rx), + T_INT(0), + T_INT(0), + T_BUFFER(srs_estimated_channel_freq[0][0], symb_sz * sizeof(int32_t))); + + T(T_GNB_PHY_UL_TIME_CHANNEL_ESTIMATE, + T_INT(0), + T_INT(srs_pdu->rnti), + T_INT(frame_rx), + T_INT(0), + T_INT(0), + T_BUFFER(srs_estimated_channel_time_shifted[0][0], symb_sz * sizeof(int32_t))); + + T(T_GNB_PHY_UL_SNR_ESTIMATE, + T_INT(0), + T_INT(srs_pdu->rnti), + T_INT(frame_rx), + T_INT(0), + T_INT(0), + T_BUFFER(snr_per_rb, srs_pdu->bwp_size * sizeof(int16_t))); + + 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; + start_meas(&gNB->srs_timing_advance_stats); + srs_indication->timing_advance_offset = + srs_est >= 0 ? nr_est_timing_advance_srs(frame_parms, srs_estimated_channel_time[0]) : 0xFFFF; + stop_meas(&gNB->srs_timing_advance_stats); + srs_indication->timing_advance_offset_nsec = + srs_est >= 0 ? (int16_t)((((int32_t)srs_indication->timing_advance_offset - 31) * ((int32_t)TC_NSEC_x32768)) >> 15) + : 0xFFFF; + 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]; + +#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; + + 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 = 1 << srs_pdu->num_symbols; + 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 + nr_srs_bf_report.num_reported_symbols = 1 << srs_pdu->num_symbols; + 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, frame_parms->N_RB_UL, 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; + } + + 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, + gNB->nr_srs_info[i], + srs_pdu->srs_parameters_v4.iq_representation, + ant, + 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); + +#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); + 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); + +#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); } stop_meas(&gNB->phy_proc_rx); if (pucch_decode_done || pusch_decode_done) { - T(T_GNB_PHY_PUCCH_PUSCH_IQ, T_INT(frame_rx), T_INT(slot_rx), T_BUFFER(&gNB->common_vars.rxdataF[0][0][0], gNB->frame_parms.symbols_per_slot * gNB->frame_parms.ofdm_symbol_size * 4)); + T(T_GNB_PHY_PUCCH_PUSCH_IQ, + T_INT(frame_rx), + T_INT(slot_rx), + T_BUFFER(&gNB->common_vars.rxdataF[0][0][0], nb_symb * symb_sz * 4)); } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_UESPEC_RX,0); From d2c191b0eaaf7c906f5297dedc47314aaf81e4c3 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Sun, 27 Jul 2025 11:16:22 +0200 Subject: [PATCH 172/275] Add two LOG_Ds to debug PUSCH allocations --- openair1/PHY/NR_TRANSPORT/nr_ulsch.c | 80 +++++++++++++++++++++++++++ openair1/SCHED_NR_UE/fapi_nr_ue_l1.c | 83 ++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c index 4621e184a6..06aef61935 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c @@ -59,8 +59,88 @@ static NR_gNB_ULSCH_t *find_nr_ulsch(PHY_VARS_gNB *gNB, uint16_t rnti, int pid) return ulsch; } +static void dump_pusch_pdu(int instance, int frame, int slot, nfapi_nr_pusch_pdu_t *pusch_pdu) +{ + LOG_D(PHY, + "[gNB %d] %d.%d ULSCH PDU pdu_bit_map %u " + "rnti %u " + "handle %u " + "bwp_size %u " + "bwp_start %u " + "subcarrier_spacing %u " + "cyclic_prefix %u " + "target_code_rate %u " + "qam_mod_order %u " + "mcs_index %u " + "mcs_table %u " + "transform_precoding %u " + "data_scrambling_id %u " + "nrOfLayers %u " + "ul_dmrs_symb_pos %u " + "dmrs_config_type %u " + "ul_dmrs_scrambling_id %u " + "scid %u " + "num_dmrs_cdm_grps_no_data %u " + "dmrs_ports %u " + "resource_alloc %u " + "rb_start %u " + "rb_size %u " + "vrb_to_prb_mapping %u " + "frequency_hopping %u " + "tx_direct_current_location %u " + "uplink_frequency_shift_7p5khz %u " + "start_symbol_index %u " + "nr_of_symbols %u " + "tbslbrm %u " + "ldpcBaseGraph %u " + "pusch_data->rv_index %u " + "pusch_data->harq_process_id %u " + "pusch_data->new_data_indicator %u " + "pusch_data->num_cb %u\n", + instance, + frame, + slot, + pusch_pdu->pdu_bit_map, + pusch_pdu->rnti, + pusch_pdu->handle, + pusch_pdu->bwp_size, + pusch_pdu->bwp_start, + pusch_pdu->subcarrier_spacing, + pusch_pdu->cyclic_prefix, + pusch_pdu->target_code_rate, + pusch_pdu->qam_mod_order, + pusch_pdu->mcs_index, + pusch_pdu->mcs_table, + pusch_pdu->transform_precoding, + pusch_pdu->data_scrambling_id, + pusch_pdu->nrOfLayers, + pusch_pdu->ul_dmrs_symb_pos, + pusch_pdu->dmrs_config_type, + pusch_pdu->ul_dmrs_scrambling_id, + pusch_pdu->scid, + pusch_pdu->num_dmrs_cdm_grps_no_data, + pusch_pdu->dmrs_ports, + pusch_pdu->resource_alloc, + pusch_pdu->rb_start, + pusch_pdu->rb_size, + pusch_pdu->vrb_to_prb_mapping, + pusch_pdu->frequency_hopping, + pusch_pdu->tx_direct_current_location, + pusch_pdu->uplink_frequency_shift_7p5khz, + pusch_pdu->start_symbol_index, + pusch_pdu->nr_of_symbols, + pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes, + pusch_pdu->maintenance_parms_v3.ldpcBaseGraph, + pusch_pdu->pusch_data.rv_index, + pusch_pdu->pusch_data.harq_process_id, + pusch_pdu->pusch_data.new_data_indicator, + pusch_pdu->pusch_data.num_cb); +} + + 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) { diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c index cbdd91c949..a3e86ce433 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -230,6 +230,88 @@ static void nr_ue_scheduled_response_dl(NR_UE_MAC_INST_t *mac, dl_config->number_pdus = 0; } +static void dump_pusch_pdu(int instance, int frame, int slot, nfapi_nr_ue_pusch_pdu_t *pusch_pdu) +{ + LOG_D(PHY, + "[UE %d] %d.%d ULSCH PDU pdu_bit_map %u " + "rnti %u " + "handle %u " + "bwp_size %u " + "bwp_start %u " + "subcarrier_spacing %u " + "cyclic_prefix %u " + "target_code_rate %u " + "qam_mod_order %u " + "mcs_index %u " + "mcs_table %u " + "transform_precoding %u " + "data_scrambling_id %u " + "nrOfLayers %u " + "Tpmi %u " + "ul_dmrs_symb_pos %u " + "dmrs_config_type %u " + "ul_dmrs_scrambling_id %u " + "scid %u " + "num_dmrs_cdm_grps_no_data %u " + "dmrs_ports %u " + "resource_alloc %u " + "rb_start %u " + "rb_size %u " + "vrb_to_prb_mapping %u " + "frequency_hopping %u " + "tx_direct_current_location %u " + "uplink_frequency_shift_7p5khz %u " + "start_symbol_index %u " + "nr_of_symbols %u " + "tbslbrm %u " + "ldpcBaseGraph %u " + "ulsch_indicator %u " + "pusch_data->rv_index %u " + "pusch_data->harq_process_id %u " + "pusch_data->new_data_indicator %u " + "pusch_data->num_cb %u\n", + instance, + frame, + slot, + pusch_pdu->pdu_bit_map, + pusch_pdu->rnti, + pusch_pdu->handle, + pusch_pdu->bwp_size, + pusch_pdu->bwp_start, + pusch_pdu->subcarrier_spacing, + pusch_pdu->cyclic_prefix, + pusch_pdu->target_code_rate, + pusch_pdu->qam_mod_order, + pusch_pdu->mcs_index, + pusch_pdu->mcs_table, + pusch_pdu->transform_precoding, + pusch_pdu->data_scrambling_id, + pusch_pdu->nrOfLayers, + pusch_pdu->Tpmi, + pusch_pdu->ul_dmrs_symb_pos, + pusch_pdu->dmrs_config_type, + pusch_pdu->ul_dmrs_scrambling_id, + pusch_pdu->scid, + pusch_pdu->num_dmrs_cdm_grps_no_data, + pusch_pdu->dmrs_ports, + pusch_pdu->resource_alloc, + pusch_pdu->rb_start, + pusch_pdu->rb_size, + pusch_pdu->vrb_to_prb_mapping, + pusch_pdu->frequency_hopping, + pusch_pdu->tx_direct_current_location, + pusch_pdu->uplink_frequency_shift_7p5khz, + pusch_pdu->start_symbol_index, + pusch_pdu->nr_of_symbols, + pusch_pdu->tbslbrm, + pusch_pdu->ldpcBaseGraph, + pusch_pdu->ulsch_indicator, + pusch_pdu->pusch_data.rv_index, + pusch_pdu->pusch_data.harq_process_id, + pusch_pdu->pusch_data.new_data_indicator, + pusch_pdu->pusch_data.num_cb); +} + static void nr_ue_scheduled_response_ul(PHY_VARS_NR_UE *phy, fapi_nr_ul_config_request_t *ul_config, nr_phy_data_tx_t *phy_data) { fapi_nr_ul_config_request_pdu_t *pdu = fapiLockIterator(ul_config, ul_config->frame, ul_config->slot); @@ -251,6 +333,7 @@ static void nr_ue_scheduled_response_ul(PHY_VARS_NR_UE *phy, fapi_nr_ul_config_r pdu->pusch_config_pdu.num_dmrs_cdm_grps_no_data); memcpy(pusch_pdu, &pdu->pusch_config_pdu, sizeof(*pusch_pdu)); + dump_pusch_pdu(phy->Mod_id, ul_config->frame, ul_config->slot, pusch_pdu); if (pdu->pusch_config_pdu.tx_request_body.fapiTxPdu) { LOG_D(PHY, "%d.%d Copying %d bytes to harq_process_ul_ue->a (harq_pid %d)\n", From cf507115e4eb379c0b36c1666e57c9e8d14540a4 Mon Sep 17 00:00:00 2001 From: luis_pereira87 Date: Wed, 5 Nov 2025 20:51:02 +0000 Subject: [PATCH 173/275] Fix unsigned to signed decoding parameters to allow for 'no affinity'/'no pin' threads to CPUs By default, 'L1_rx_thread_core', 'L1_tx_thread_core', 'ru_thread_core' are set to '-1' but the values were decoded from config file as unsigned instead of signed types, thus, it was not allowing to set them with '-1' from config file --- openair2/ENB_APP/enb_paramdef.h | 2 +- openair2/GNB_APP/L1_nr_paramdef.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openair2/ENB_APP/enb_paramdef.h b/openair2/ENB_APP/enb_paramdef.h index 7bc8f677d8..7be234262f 100644 --- a/openair2/ENB_APP/enb_paramdef.h +++ b/openair2/ENB_APP/enb_paramdef.h @@ -217,7 +217,7 @@ typedef enum { {CONFIG_STRING_RU_NUM_TP_CORES, HLP_RU_NUM_TP_CORES, 0, .uptr=NULL, .defintval=2, TYPE_UINT, 0}, \ {CONFIG_STRING_RU_NUM_INTERFACES, HLP_RU_NUM_INTERFACES, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \ {CONFIG_STRING_RU_HALF_SLOT_PARALLELIZATION, HLP_RU_HALF_SLOT_PARALLELIZATION, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \ - {CONFIG_STRING_RU_RU_THREAD_CORE, HLP_RU_RU_THREAD_CORE, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ + {CONFIG_STRING_RU_RU_THREAD_CORE, HLP_RU_RU_THREAD_CORE, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \ {CONFIG_STRING_RU_GPIO_CONTROL, HLP_RU_GPIO_CONTROL, 0, .strptr=NULL, .defstrval="generic", TYPE_STRING, 0}, \ } // clang-format on diff --git a/openair2/GNB_APP/L1_nr_paramdef.h b/openair2/GNB_APP/L1_nr_paramdef.h index b2b4ab93c1..d140275fa5 100644 --- a/openair2/GNB_APP/L1_nr_paramdef.h +++ b/openair2/GNB_APP/L1_nr_paramdef.h @@ -84,8 +84,8 @@ {CONFIG_STRING_L1_PUSCH_DTX_THRESHOLD, NULL, 0, .uptr=NULL, .defintval=50, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_SRS_DTX_THRESHOLD, NULL, 0, .uptr=NULL, .defintval=30, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_MAX_LDPC_ITERATIONS, NULL, 0, .uptr=NULL, .defintval=8, TYPE_UINT, 0}, \ - {CONFIG_STRING_L1_RX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ - {CONFIG_STRING_L1_TX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ + {CONFIG_STRING_L1_RX_THREAD_CORE, NULL, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \ + {CONFIG_STRING_L1_TX_THREAD_CORE, NULL, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \ {CONFIG_STRING_L1_TX_AMP_BACKOFF_dB, HLP_L1TX_BO,0, .uptr=NULL, .defintval=36, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_PHASE_COMP, HLP_L1_PHASE_COMP,PARAMFLAG_BOOL, .uptr=NULL,.defintval=1, TYPE_UINT, 0}, \ {CONFIG_STRING_NUM_ANTENNAS_PER_THREAD, HLP_NUM_ARX,0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \ From ac93fe590cecc6d8d3159db67ebd1bc0cd99d70a Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Sun, 27 Jul 2025 13:04:34 +0200 Subject: [PATCH 174/275] Fix NR UE MAC not setting subcarrier_spacing field in PUSCH PDU Set the subcarrier_spacing field in nfapi_nr_ue_pusch_pdu_t from MAC to PHY. This has no consequence for the NR UE Softmodem operation as this field is never read, but prevents a potential future bug in case L1 was modified to read it. --- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 1 + 1 file changed, 1 insertion(+) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 714d2eacfd..bd9ea09041 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -555,6 +555,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->tbslbrm = 0; } else if (dci) { + pusch_config_pdu->subcarrier_spacing = scs; pusch_config_pdu->ulsch_indicator = dci->ulsch_indicator; if (dci->csi_request.nbits > 0 && dci->csi_request.val > 0) { AssertFatal(csi_report, "CSI report needs to be present in case of CSI request\n"); From 3f27ae26ae9cd88c8e9c59dbf8dab7317cad1b89 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Thu, 23 Oct 2025 18:20:33 +0200 Subject: [PATCH 175/275] C code cleaning, no change --- openair1/PHY/NR_TRANSPORT/nr_prs.c | 2 +- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 2 +- openair1/SCHED_NR/fapi_nr_l1.h | 9 -- openair1/SCHED_NR/phy_frame_config_nr.c | 2 +- openair1/SCHED_NR/phy_frame_config_nr.h | 2 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 83 ++++++++++--------- 6 files changed, 49 insertions(+), 51 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_prs.c b/openair1/PHY/NR_TRANSPORT/nr_prs.c index 6f26b9a07f..799912713d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prs.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prs.c @@ -13,7 +13,7 @@ int nr_generate_prs(int slot, int16_t amp, prs_config_t *prs_cfg, nfapi_nr_config_request_scf_t *config, - NR_DL_FRAME_PARMS *frame_parms) + const NR_DL_FRAME_PARMS *frame_parms) { int k_prime = 0, k = 0, idx; diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 0714f3dde3..29fca5975e 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -45,7 +45,7 @@ int nr_generate_prs(int slot, int16_t amp, prs_config_t *prs_cfg, nfapi_nr_config_request_scf_t *config, - NR_DL_FRAME_PARMS *frame_parms); + const NR_DL_FRAME_PARMS *frame_parms); /*! \fn int nr_generate_pss diff --git a/openair1/SCHED_NR/fapi_nr_l1.h b/openair1/SCHED_NR/fapi_nr_l1.h index 59df4fb30d..4d426d92ce 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.h +++ b/openair1/SCHED_NR/fapi_nr_l1.h @@ -53,15 +53,6 @@ void handle_nr_nfapi_pdsch_pdu(processingData_L1tx_t *msgTx, nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu, uint8_t *sdu); -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); //added void handle_nfapi_nr_ul_dci_pdu(PHY_VARS_gNB *gNB, diff --git a/openair1/SCHED_NR/phy_frame_config_nr.c b/openair1/SCHED_NR/phy_frame_config_nr.c index 085ce8e087..9479e960a8 100644 --- a/openair1/SCHED_NR/phy_frame_config_nr.c +++ b/openair1/SCHED_NR/phy_frame_config_nr.c @@ -152,7 +152,7 @@ void set_tdd_config_nr(nfapi_nr_config_request_scf_t *cfg, frame_structure_t *fs } } -int nr_slot_select(nfapi_nr_config_request_scf_t *cfg, int nr_frame, int nr_slot) +int nr_slot_select(const nfapi_nr_config_request_scf_t *cfg, int nr_frame, int nr_slot) { (void) nr_frame; diff --git a/openair1/SCHED_NR/phy_frame_config_nr.h b/openair1/SCHED_NR/phy_frame_config_nr.h index 11ae4bf5c6..687fd2bc51 100644 --- a/openair1/SCHED_NR/phy_frame_config_nr.h +++ b/openair1/SCHED_NR/phy_frame_config_nr.h @@ -53,7 +53,7 @@ void set_tdd_config_nr(nfapi_nr_config_request_scf_t *cfg, frame_structure_t *fs * @param nr_slot : slot number @returns int : downlink, uplink or mixed slot type*/ -int nr_slot_select(nfapi_nr_config_request_scf_t *cfg, int nr_frame, int nr_slot); +int nr_slot_select(const nfapi_nr_config_request_scf_t *cfg, int nr_frame, int nr_slot); void do_tdd_config_sim(PHY_VARS_gNB *gNB, int mu); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 44d0a24d1c..e28d22ede9 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -45,6 +45,15 @@ //#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); int beam_index_allocation(bool das, int fapi_beam_index, @@ -186,10 +195,10 @@ void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_n void clear_slot_beamid(PHY_VARS_gNB *gNB, int slot) { LOG_D(PHY, "Clearing beam_id structure for slot %d\n", slot); - NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; + int slot_sz = gNB->frame_parms.symbols_per_slot; for (int i = 0; i < gNB->common_vars.num_beams_period; i++) { if (gNB->common_vars.beam_id) - memset(&gNB->common_vars.beam_id[i][slot * fp->symbols_per_slot], -1, fp->symbols_per_slot * sizeof(int)); + memset(&gNB->common_vars.beam_id[i][slot * slot_sz], -1, slot_sz * sizeof(**gNB->common_vars.beam_id)); } } @@ -199,11 +208,9 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, int do_meas) { PHY_VARS_gNB *gNB = msgTx->gNB; - NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; + const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; - int slot_prs = 0; - int txdataF_offset = slot * fp->samples_per_slot_wCP; - prs_config_t *prs_config = NULL; + const int txdataF_offset = slot * fp->samples_per_slot_wCP; if ((cfg->cell_config.frame_duplex_type.value == TDD) && (nr_slot_select(cfg,frame,slot) == NR_UPLINK_SLOT)) return; @@ -213,19 +220,19 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, // clear the transmit data array and beam index for the current slot for (int i = 0; i < gNB->common_vars.num_beams_period; i++) { for (int aa = 0; aa < cfg->carrier_config.num_tx_ant.value; aa++) { - memset(&gNB->common_vars.txdataF[i][aa][txdataF_offset], 0, fp->samples_per_slot_wCP * sizeof(int32_t)); + memset(&gNB->common_vars.txdataF[i][aa][txdataF_offset], 0, fp->samples_per_slot_wCP * sizeof(***gNB->common_vars.txdataF)); } } // Check for PRS slot - section 7.4.1.7.4 in 3GPP rel16 38.211 for(int rsc_id = 0; rsc_id < gNB->prs_vars.NumPRSResources; rsc_id++) { - prs_config = &gNB->prs_vars.prs_cfg[rsc_id]; + prs_config_t *prs_config = &gNB->prs_vars.prs_cfg[rsc_id]; for (int i = 0; i < prs_config->PRSResourceRepetition; i++) { if( (((frame*fp->slots_per_frame + slot) - (prs_config->PRSResourceSetPeriod[1] + prs_config->PRSResourceOffset)+prs_config->PRSResourceSetPeriod[0])%prs_config->PRSResourceSetPeriod[0]) == i*prs_config->PRSResourceTimeGap ) { - slot_prs = (slot - i*prs_config->PRSResourceTimeGap + fp->slots_per_frame)%fp->slots_per_frame; + int slot_prs = (slot - i * prs_config->PRSResourceTimeGap + fp->slots_per_frame) % fp->slots_per_frame; LOG_D(PHY,"gNB_TX: frame %d, slot %d, slot_prs %d, PRS Resource ID %d\n",frame, slot, slot_prs, rsc_id); nr_generate_prs(slot_prs, &gNB->common_vars.txdataF[0][0][txdataF_offset], AMP, prs_config, cfg, fp); } @@ -537,24 +544,24 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, boo return ret_nr_ulsch_decoding; } -void nr_fill_indication(PHY_VARS_gNB *gNB, - int frame, - int slot_rx, - int ULSCH_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, + int frame, + int slot_rx, + int ULSCH_id, + uint8_t harq_pid, + uint8_t crc_flag, + int dtx_flag, + nfapi_nr_crc_t *crc, + nfapi_nr_rx_data_pdu_t *pdu) { - NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; - NR_UL_gNB_HARQ_t *harq_process = ulsch->harq_process; - NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, ulsch->rnti); + 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 - int sync_pos = ulsch->delay.est_delay; + const int sync_pos = gNB->ulsch[ULSCH_id].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; @@ -584,16 +591,15 @@ void nr_fill_indication(PHY_VARS_gNB *gNB, timing_advance_update); // estimate UL_CQI for MAC - int SNRtimes10 = - dB_fixed_x10(gNB->pusch_vars[ULSCH_id].ulsch_power_tot) - dB_fixed_x10(gNB->pusch_vars[ULSCH_id].ulsch_noise_power_tot); + int SNRtimes10 = dB_fixed_x10(pusch->ulsch_power_tot) - dB_fixed_x10(pusch->ulsch_noise_power_tot); LOG_D(PHY, "%d.%d: Estimated SNR for PUSCH is = %f dB (ulsch_power %f, noise %f) delay %d\n", frame, slot_rx, SNRtimes10 / 10.0, - dB_fixed_x10(gNB->pusch_vars[ULSCH_id].ulsch_power_tot) / 10.0, - dB_fixed_x10(gNB->pusch_vars[ULSCH_id].ulsch_noise_power_tot) / 10.0, + dB_fixed_x10(pusch->ulsch_power_tot) / 10.0, + dB_fixed_x10(pusch->ulsch_noise_power_tot) / 10.0, sync_pos); int cqi; @@ -609,8 +615,7 @@ void nr_fill_indication(PHY_VARS_gNB *gNB, crc->ul_cqi = cqi; crc->timing_advance = timing_advance_update; // in terms of dBFS range -128 to 0 with 0.1 step - crc->rssi = - (dtx_flag == 0) ? 1280 - (10 * dB_fixed(32767 * 32767) - dB_fixed_times10(gNB->pusch_vars[ULSCH_id].ulsch_power[0])) : 0; + crc->rssi = (dtx_flag == 0) ? 1280 - (10 * dB_fixed(32767 * 32767) - dB_fixed_times10(pusch->ulsch_power[0])) : 0; pdu->handle = pusch_pdu->handle; pdu->rnti = pusch_pdu->rnti; @@ -704,21 +709,23 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, } } -int fill_srs_reported_symbol(nfapi_nr_srs_reported_symbol_t *reported_symbol, - const nfapi_nr_srs_pdu_t *srs_pdu, - const int N_RB_UL, - const int16_t *snr_per_rb, - const int srs_est) { +static int fill_srs_reported_symbol(nfapi_nr_srs_reported_symbol_t *reported_symbol, + const nfapi_nr_srs_pdu_t *srs_pdu, + const int N_RB_UL, + const int16_t *snr_per_rb, + const int srs_est) +{ reported_symbol->num_prgs = srs_pdu->beamforming.num_prgs; for (int prg_idx = 0; prg_idx < reported_symbol->num_prgs; prg_idx++) { - if (srs_est<0) { - reported_symbol->prg_list[prg_idx].rb_snr = 0xFF; + uint8_t *snr = &reported_symbol->prg_list[prg_idx].rb_snr; + if (srs_est < 0) { + *snr = 0xFF; } else if (snr_per_rb[prg_idx] < -64) { - reported_symbol->prg_list[prg_idx].rb_snr = 0; + *snr = 0; } else if (snr_per_rb[prg_idx] > 63) { - reported_symbol->prg_list[prg_idx].rb_snr = 0xFE; + *snr = 0xFE; } else { - reported_symbol->prg_list[prg_idx].rb_snr = (snr_per_rb[prg_idx] + 64) << 1; + *snr = (snr_per_rb[prg_idx] + 64) << 1; } } From 95e9f4a47cef2b25a6db71ab772f81598390909f Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Wed, 5 Nov 2025 12:22:38 +0100 Subject: [PATCH 176/275] Use a single Jenkinsfile Combine Jenkinsfile and Jenkinsfile-oc from parrent commit into a single file. --- ci-scripts/Jenkinsfile | 82 +++++++++++----- ci-scripts/Jenkinsfile-oc | 199 -------------------------------------- 2 files changed, 60 insertions(+), 221 deletions(-) delete mode 100644 ci-scripts/Jenkinsfile-oc diff --git a/ci-scripts/Jenkinsfile b/ci-scripts/Jenkinsfile index 329caa5834..abb2e3942c 100644 --- a/ci-scripts/Jenkinsfile +++ b/ci-scripts/Jenkinsfile @@ -36,6 +36,7 @@ if (params.LockResources != null && params.LockResources.trim().length() > 0) // Global Parameters. Normally they should be populated when the master job // triggers the slave job with parameters + def sourceRepository = params.eNB_Repository ?: env.GIT_URL def sourceBranch = params.eNB_Branch ?: env.GIT_BRANCH def targetRepository = params.Target_Repository ?: sourceRepository @@ -44,6 +45,7 @@ def commitID = params.eNB_CommitID ?: env.GIT_COMMIT def is_MR = params.eNB_mergeRequest ?: false def flexricOption = "" +def runWithOC = false //------------------------------------------------------------------------------- // Pipeline start @@ -83,7 +85,14 @@ pipeline { echo "no eNB_SourceCodePath given" allParametersPresent = false } - + if (params.OC_Credentials != null) { + echo "This pipeline is configured to run with Opensift Cluster." + runWithOC = true + if (params.OC_ProjectName == null) { + echo "no OC_ProjectName given" + allParametersPresent = false + } + } if (params.Flexric_Tag != null) { echo "This pipeline is configured to run with a FlexRIC deployment." echo "Appending FlexRicTag option to the list of options" @@ -114,7 +123,6 @@ pipeline { } } } - stage ("Deploy and Test") { steps { script { @@ -130,30 +138,60 @@ pipeline { } } sh """ - python3 main.py --mode=InitiateHtml --ranRepository=${targetRepository} \ - --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ - --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ - ${flexricOption} ${mainPythonAllXmlFiles} + python3 main.py --mode=InitiateHtml --ranRepository=${targetRepository} \ + --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ + --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ + ${flexricOption} ${mainPythonAllXmlFiles} """ - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - try { - timeout (time: 60, unit: 'MINUTES') { - sh """ - python3 main.py --mode=TesteNB --ranRepository=${targetRepository} \ - --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ - --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ - --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} \ - ${flexricOption} - """ + if (runWithOC) { + withCredentials([usernamePassword(credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password')]) { + sh """ + python3 main.py --mode=InitiateHtml --ranRepository=${targetRepository} \ + --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ + --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ + ${flexricOption} ${mainPythonAllXmlFiles} + """ + for (xmlFile in myXmlTestSuite) { + if (fileExists(xmlFile)) { + try { + timeout (time: 60, unit: 'MINUTES') { + sh """ + python3 main.py --mode=TesteNB --ranRepository=${targetRepository} \ + --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ + --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ + --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} \ + --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName} \ + ${flexricOption} + """ + } + } catch (Exception e) { + currentBuild.result = 'FAILURE' + buildStageStatus = false + } + } + } + } + } else { + for (xmlFile in myXmlTestSuite) { + if (fileExists(xmlFile)) { + try { + timeout (time: 60, unit: 'MINUTES') { + sh """ + python3 main.py --mode=TesteNB --ranRepository=${targetRepository} \ + --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ + --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ + --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} \ + ${flexricOption} + """ + } + } catch (Exception e) { + currentBuild.result = 'FAILURE' + buildStageStatus = false } - } catch (Exception e) { - currentBuild.result = 'FAILURE' - buildStageStatus = false } } } - sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" + sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" } } } @@ -176,7 +214,7 @@ pipeline { archiveArtifacts artifacts: "test_logs_${env.BUILD_ID}.zip" } if (fileExists("test_results.html")) { - def reportName = "test_results_${env.JOB_NAME}.html" + def reportName = "test_results-${env.JOB_NAME}.html" sh "mv test_results.html ${reportName}" sh """ sed -i -e 's#TEMPLATE_BUILD_TIME#${JOB_TIMESTAMP}#' \ diff --git a/ci-scripts/Jenkinsfile-oc b/ci-scripts/Jenkinsfile-oc deleted file mode 100644 index 01a8022c12..0000000000 --- a/ci-scripts/Jenkinsfile-oc +++ /dev/null @@ -1,199 +0,0 @@ -#!/bin/groovy -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -def pythonExecutor = params.pythonExecutor -def testXMLFile = params.pythonTestXmlFile -def mainPythonAllXmlFiles = "" -def buildStageStatus = true -def JOB_TIMESTAMP - -// Choose test stage name or fallback default -def testStageName = params.pipelineTestStageName != null ? params.pipelineTestStageName : 'Template Test Stage' - -// Name of the resource -def lockResources = [] -if (params.LockResources != null && params.LockResources.trim().length() > 0) - params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} - -// Global Parameters. Normally they should be populated when the master job -// triggers the slave job with parameters - -def sourceRepository = params.eNB_Repository ?: env.GIT_URL -def sourceBranch = params.eNB_Branch ?: env.GIT_BRANCH -def targetRepository = params.Target_Repository ?: sourceRepository -def targetBranch = params.eNB_TargetBranch ?: 'develop' -def commitID = params.eNB_CommitID ?: env.GIT_COMMIT -def is_MR = params.eNB_mergeRequest ?: false - -//------------------------------------------------------------------------------- -// Pipeline start -pipeline { - agent { - label pythonExecutor - } - options { - timestamps() - ansiColor('xterm') - lock(extra: lockResources) - } - - stages { - stage("Build Init") { - steps { - script { - echo '\u2705 \u001B[94mBuild Init\u001B[0m' - JOB_TIMESTAMP = sh(returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"').trim() - } - // update the build name and description - buildName "${params.eNB_MR}" - buildDescription "Branch : ${sourceBranch}" - } - } - stage ('Verify Parameters') { - steps { - script { - echo '\u2705 \u001B[94mVerify Parameters\u001B[0m' - def allParametersPresent = true - - if (params.LockResources == null) { - echo "no LockResources given" - allParametersPresent = false - } - if (params.eNB_SourceCodePath == null) { - echo "no eNB_SourceCodePath given" - allParametersPresent = false - } - if (params.OC_Credentials == null) { - echo "no OC_Credentials given" - allParametersPresent = false - } - if (params.OC_ProjectName == null) { - echo "no OC_ProjectName given" - allParametersPresent = false - } - - echo "CI executor node : ${pythonExecutor}" - echo "Source Repository : ${sourceRepository}" - echo "Source Branch : ${sourceBranch}" - echo "Target Repository : ${targetRepository}" - echo "Target Branch : ${targetBranch}" - echo "Commit ID : ${commitID}" - - if (allParametersPresent) { - echo '\u2705 \u001B[94m All parameters are present\u001B[0m' - if (is_MR) { - sh "git fetch" - sh "./ci-scripts/doGitLabMerge.sh --src-branch ${sourceBranch} --src-commit ${commitID} --target-branch ${targetBranch} --target-commit latest" - } else { - sh "git fetch" - sh "git checkout -f ${commitID}" - } - } else { - echo "\u274C Some parameters are missing" - sh "./ci-scripts/fail.sh" - } - } - } - } - - stage ("Deploy and Test") { - steps { - script { - dir ('ci-scripts') { - echo "\u2705 \u001B[94m Deploy and Test: ${testStageName}\u001B[0m" - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " - echo "Test XML file : ${xmlFile}" - } else { - echo "Test XML file ${xmlFile}: no such file" - } - } - withCredentials([ - usernamePassword(credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password') - ]) { - sh """ - python3 main.py --mode=InitiateHtml --ranRepository=${targetRepository} \ - --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ - --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ - ${mainPythonAllXmlFiles} - """ - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - try { - timeout (time: 60, unit: 'MINUTES') { - sh """ - python3 main.py --mode=TesteNB --ranRepository=${targetRepository} \ - --ranBranch=${sourceBranch} --ranCommitID=${commitID} \ - --ranAllowMerge=${is_MR} --ranTargetBranch=${targetBranch} \ - --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} \ - --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName} - """ - } - } catch (Exception e) { - currentBuild.result = 'FAILURE' - buildStageStatus = false - } - } - } - sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" - } - } - } - } - } - stage ("Log Collection") { - steps { - script { - echo '\u2705 \u001B[94mLog Collection\u001B[0m' - dir ('ci-scripts') { - // Zipping all archived log files - sh "mkdir test_logs" - sh "mv *.log* test_logs || true" - // Zip all log files matching cmake_targets/{*.log*,log/*} into test_logs_XXXX.zip - if (fileExists('../cmake_targets/log')) { - sh "mv ../cmake_targets/log/* test_logs || true" - } - sh "zip -r -qq test_logs_${env.BUILD_ID}.zip *test_log*/" - sh "rm -rf *test_log*/" - if (fileExists("test_logs_${env.BUILD_ID}.zip")) { - archiveArtifacts artifacts: "test_logs_${env.BUILD_ID}.zip" - } - if (fileExists("test_results.html")) { - def reportName = "test_results_${env.JOB_NAME}.html" - sh "mv test_results.html ${reportName}" - sh """ - sed -i -e 's#TEMPLATE_BUILD_TIME#${JOB_TIMESTAMP}#' \ - -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' \ - -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' \ - -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' \ - -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' ${reportName} - """ - archiveArtifacts artifacts: "${reportName}" - } - } - } - } - } - } -} From 7274e7c4347ab969f5a2c36ff0b41680444acc81 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 6 Nov 2025 11:08:38 +0100 Subject: [PATCH 177/275] SDAP: guard RX header parsing with enable_sdap and init per-entity !3519 refactored the SDAP to use the QFI from the header rather than pass it to the rx function and got rid of the initialization of QFI to -1. However, entity roles are stored into the drb2qfi table, and the indexing implies that the QFI is known. While in TX it is not an issue since packets are forwarded based on the QFI, in RX the QFI can only be parsed from the heaedr, therefore if headers are disabled (--enable_sdap) the QFI is not there and it is wrong to access the header (`buf[0]`) and looking for it. This commit adds `enable_sdap` to `nr_sdap_entity_t` and initialize it in `nr_sdap_add_entity()` from `sdap->role` (disabled when `NO_SDAP_HEADER`). In `nr_sdap_rx_entity()`, only parse `buf[0]` for QFI and derive `sdap_ul_rx`/`sdap_dl_rx` when `enable_sdap` is true; otherwise, keep `offset=0` and forward payload unchanged. --- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 20 +++++++++++++------- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 389213986e..9056c6e733 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -210,15 +210,20 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, { /* The offset of the SDAP header, it might be 0 if has_sdap_rx is not true in the pdcp entity. */ int offset=0; - uint8_t qfi = buf[0] & 0x3F; // QFI is always the first 6 bits in the first octet - if (qfi >= SDAP_MAX_QFI) { - LOG_E(SDAP, "Invalid QFI %d received in SDAP header\n", qfi); - return; + bool sdap_ul_rx = false; + bool sdap_dl_rx = false; + /* If SDAP header is disabled for this entity, bypass header parsing */ + if (entity->enable_sdap) { + uint8_t qfi = buf[0] & 0x3F; // QFI is always the first 6 bits in the first octet + if (qfi >= SDAP_MAX_QFI) { + LOG_E(SDAP, "Invalid QFI %d received in SDAP header\n", qfi); + return; + } + // Fetch entity role from the qfi2drb_table + sdap_ul_rx = entity->qfi2drb_table[qfi].entity_role & SDAP_UL_RX; // gNB RX entity + sdap_dl_rx = entity->qfi2drb_table[qfi].entity_role & SDAP_DL_RX; // UE RX entity } - // Fetch entity role from the qfi2drb_table - bool sdap_ul_rx = entity->qfi2drb_table[qfi].entity_role & SDAP_UL_RX; // gNB RX entity - bool sdap_dl_rx = entity->qfi2drb_table[qfi].entity_role & SDAP_DL_RX; // UE RX entity if (is_gnb) { // gNB if (sdap_ul_rx) { // UL Data/Control PDU with SDAP header @@ -495,6 +500,7 @@ static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap sdap_entity->ue_id = ue_id; sdap_entity->pdusession_id = sdap->pdusession_id; sdap_entity->is_gnb = is_gnb; + sdap_entity->enable_sdap = (sdap->role != NO_SDAP_HEADER); // rx/tx entities sdap_entity->tx_entity = nr_sdap_tx_entity; diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index e96836ac79..9f85ac21cf 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -99,6 +99,7 @@ typedef struct nr_sdap_entity_s { /// sdap_tun_read_thread needs to know if we are gNB/UE, so for noS1 mode, /// store which one we are bool is_gnb; + bool enable_sdap; int pdusession_id; int pdusession_sock; pthread_t pdusession_thread; From c47ddc570f9ebeb06df91d7314475c28e888f774 Mon Sep 17 00:00:00 2001 From: francescomani Date: Sat, 8 Nov 2025 14:34:41 +0100 Subject: [PATCH 178/275] add check for CellGroup decoding at RRC UE (issue #1024) --- openair2/RRC/NR_UE/rrc_UE.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 15c713cb26..9086ad25eb 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -747,12 +747,15 @@ static void nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc, { AssertFatal(!fullConfig, "fullConfig not supported yet\n"); NR_CellGroupConfig_t *cellGroupConfig = NULL; - uper_decode(NULL, - &asn_DEF_NR_CellGroupConfig, //might be added prefix later - (void **)&cellGroupConfig, - (uint8_t *)masterCellGroup->buf, - masterCellGroup->size, 0, 0); - + asn_dec_rval_t dec_rval = uper_decode(NULL, + &asn_DEF_NR_CellGroupConfig, //might be added prefix later + (void **)&cellGroupConfig, + (uint8_t *)masterCellGroup->buf, + masterCellGroup->size, 0, 0); + if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { + LOG_E(NR_RRC, "CellGroupConfig decode error\n"); + return; + } if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig); } From f9ca8431c0b1715972953e05575119dba4ac1a21 Mon Sep 17 00:00:00 2001 From: Nada Bouknana Date: Tue, 11 Nov 2025 13:58:43 +0100 Subject: [PATCH 179/275] add PDCCH IQ and LLR plots to the UE scope --- openair1/PHY/TOOLS/imscope/imscope.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/openair1/PHY/TOOLS/imscope/imscope.cpp b/openair1/PHY/TOOLS/imscope/imscope.cpp index cce7a595f7..b70587614d 100644 --- a/openair1/PHY/TOOLS/imscope/imscope.cpp +++ b/openair1/PHY/TOOLS/imscope/imscope.cpp @@ -444,6 +444,23 @@ void ShowUeScope(void *data_void_ptr, float t) } ImGui::End(); + if (ImGui::Begin("UE PDCCH IQ")) { + static auto iq_data = new IQData(); + static auto pdcch_iq_hist = new IQHist("PDCCH IQ"); + bool new_data = false; + if (pdcch_iq_hist->ShouldReadData()) { + new_data = iq_data->TryCollect(&scope_array[pdcchRxdataF_comp], t, pdcch_iq_hist->GetEpsilon(), iq_procedure_timer); + } + pdcch_iq_hist->Draw(iq_data, t, new_data); + } + ImGui::End(); + + if (ImGui::Begin("UE PDCCH LLR")) { + static auto llr_plot = new LLRPlot(); + llr_plot->Draw(t, pdcchLlr, "PDCCH LLR"); + } + ImGui::End(); + if (ImGui::Begin("UE PDSCH IQ")) { static auto iq_data = new IQData(); static auto pdsch_iq_hist = new IQHist("PDSCH IQ"); From 530812ff381092abd579a6a0bd13c0c12e21021a Mon Sep 17 00:00:00 2001 From: Nada Bouknana Date: Tue, 11 Nov 2025 14:01:23 +0100 Subject: [PATCH 180/275] add ImPlotAxisFlags_AutoFit flag to enable autofitting by default in imscope plots --- openair1/PHY/TOOLS/imscope/imscope.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openair1/PHY/TOOLS/imscope/imscope.cpp b/openair1/PHY/TOOLS/imscope/imscope.cpp index b70587614d..a858409d9e 100644 --- a/openair1/PHY/TOOLS/imscope/imscope.cpp +++ b/openair1/PHY/TOOLS/imscope/imscope.cpp @@ -100,6 +100,7 @@ class LLRPlot { ImScopeDataWrapper &scope_data = scope_array[type]; if (ImPlot::BeginPlot(label)) { + ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit); if (!frozen || next) { if (scope_data.is_data_ready) { iq_procedure_timer.Add(scope_data.data.time_taken_in_ns); @@ -205,6 +206,7 @@ class IQHist { if (plot_type == 0) { float x = ImGui::CalcItemWidth(); if (ImPlot::BeginPlot(label.c_str(), {x, x})) { + ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit); ImPlot::PlotHistogram2D(label.c_str(), iq_data->real.data(), iq_data->imag.data(), @@ -217,6 +219,7 @@ class IQHist { } else if (plot_type == 2) { float x = ImGui::CalcItemWidth(); if (ImPlot::BeginPlot(label.c_str(), {x, x})) { + ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit); int points_drawn = 0; while (points_drawn < iq_data->len) { // Limit the amount of data plotted with PlotScatter call (issue with vertices/draw call) @@ -232,6 +235,7 @@ class IQHist { } } else if (plot_type == 1) { if (ImPlot::BeginPlot(label.c_str())) { + ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit); ImPlot::PlotLine(label.c_str(), iq_data->power.data(), iq_data->len); ImPlot::EndPlot(); } @@ -364,6 +368,7 @@ class IQSlotHeatmap { static std::vector symbol_boundaries = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; if (ImPlot::BeginPlot(label.c_str(), {(float)ImGui::GetWindowWidth() * 0.9f, 0})) { auto num_sc = num_rb * NR_NB_SC_PER_RB; + ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit); ImPlot::SetupAxes("symbols", "subcarriers"); ImPlot::SetupAxisLimits(ImAxis_X1, num_symbols, 0); ImPlot::SetupAxisLimits(ImAxis_Y1, num_sc, 0); @@ -429,6 +434,7 @@ void ShowUeScope(void *data_void_ptr, float t) rbs_buffer.AddPoint(t, getKPIUE()->nofRBs); bler.AddPoint(t, (float)getKPIUE()->nb_nack / (float)getKPIUE()->nb_total); mcs.AddPoint(t, (float)getKPIUE()->dl_mcs); + ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit); ImPlot::SetupAxes("time", "noOfRbs"); ImPlot::SetupAxisLimits(ImAxis_X1, t - history, t, ImGuiCond_Always); ImPlot::SetupAxisLimits(ImAxis_Y1, 0, NR_MAX_RB); From 05570bb8d825560c3a93113136e65c651358bf89 Mon Sep 17 00:00:00 2001 From: Nada Bouknana Date: Tue, 11 Nov 2025 14:06:31 +0100 Subject: [PATCH 181/275] move global scope settings to a menu item --- openair1/PHY/TOOLS/imscope/imscope.cpp | 31 +++++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/openair1/PHY/TOOLS/imscope/imscope.cpp b/openair1/PHY/TOOLS/imscope/imscope.cpp index a858409d9e..a80b5cfd98 100644 --- a/openair1/PHY/TOOLS/imscope/imscope.cpp +++ b/openair1/PHY/TOOLS/imscope/imscope.cpp @@ -733,6 +733,7 @@ void *imscope_thread(void *data_void_ptr) static float t = 0; static bool show_imgui_demo_window = false; static bool show_implot_demo_window = false; + static bool show_scope_settings_window = false; ImGui::DockSpaceOverViewport(); if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { @@ -752,6 +753,12 @@ void *imscope_thread(void *data_void_ptr) } ImGui::EndMenu(); } + if (ImGui::BeginMenu("Settings")) { + if (ImGui::MenuItem("Global scope settings")) { + show_scope_settings_window = !show_scope_settings_window; + } + ImGui::EndMenu(); + } ImGui::EndMainMenuBar(); } @@ -762,16 +769,6 @@ void *imscope_thread(void *data_void_ptr) } ImGui::End(); - ImGui::Begin("Global scope settings"); - ImGui::ShowStyleSelector("ImGui Style"); - ImPlot::ShowStyleSelector("ImPlot Style"); - ImPlot::ShowColormapSelector("ImPlot Colormap"); - ImGui::SliderInt("FPS target", &target_fps, 12, 60); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Reduces scope flickering in unfrozen mode. Can reduce impact on perfromance of the modem"); - } - ImGui::End(); - t += ImGui::GetIO().DeltaTime; iq_procedure_timer.UpdateAverage(t); @@ -788,7 +785,19 @@ void *imscope_thread(void *data_void_ptr) ImPlot::ShowDemoWindow(); if (show_imgui_demo_window) ImGui::ShowDemoWindow(); - + // Settings + if (show_scope_settings_window) { + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); + ImGui::Begin("Global scope settings", &show_scope_settings_window); + ImGui::ShowStyleSelector("ImGui Style"); + ImPlot::ShowStyleSelector("ImPlot Style"); + ImPlot::ShowColormapSelector("ImPlot Colormap"); + ImGui::SliderInt("FPS target", &target_fps, 12, 60); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Reduces scope flickering in unfrozen mode. Can reduce impact on perfromance of the modem"); + } + ImGui::End(); + } // Rendering ImGui::Render(); glViewport(0, 0, display_w, display_h); From 1d235ce6fa72aa245bd76c895af39a85976d7c2e Mon Sep 17 00:00:00 2001 From: Nada Bouknana Date: Tue, 11 Nov 2025 14:38:30 +0100 Subject: [PATCH 182/275] modify the default imscope docking layout from imgui.ini --- openair1/PHY/TOOLS/imscope/imgui.ini | 286 ++++++++++++++++++++++----- 1 file changed, 232 insertions(+), 54 deletions(-) diff --git a/openair1/PHY/TOOLS/imscope/imgui.ini b/openair1/PHY/TOOLS/imscope/imgui.ini index 3fc08b6f7c..f3269ebfc6 100644 --- a/openair1/PHY/TOOLS/imscope/imgui.ini +++ b/openair1/PHY/TOOLS/imscope/imgui.ini @@ -1,13 +1,47 @@ +[Window][WindowOverViewport_11111111] +Pos=0,19 +Size=1920,927 +Collapsed=0 + [Window][Debug##Default] Pos=60,60 Size=400,400 Collapsed=0 +[Window][Status bar] +Pos=0,914 +Size=1920,32 +Collapsed=0 +DockId=0x00000004,0 + +[Window][Global scope settings] +Pos=697,153 +Size=545,478 +Collapsed=0 + +[Window][PUSCH SLOT IQ] +Pos=0,257 +Size=789,655 +Collapsed=0 +DockId=0x0000000B,0 + +[Window][PUSCH LLRs] +Pos=791,257 +Size=1129,655 +Collapsed=0 +DockId=0x0000000C,0 + +[Window][Time domain samples] +Pos=997,201 +Size=923,711 +Collapsed=0 +DockId=0x0000000E,0 + [Window][NR KPI] Pos=0,19 Size=1133,667 Collapsed=0 -DockId=0x00000005,0 +DockId=0x00000007,0 [Window][Dear ImGui Demo] Pos=531,19 @@ -21,79 +55,223 @@ Size=529,667 Collapsed=0 DockId=0x00000002,0 -[Window][WindowOverViewport_11111111] -Pos=0,19 -Size=1280,701 -Collapsed=0 - -[Window][Status bar] -Pos=0,688 -Size=1280,32 -Collapsed=0 -DockId=0x00000004,0 - -[Window][Global scope settings] -Pos=1038,19 -Size=242,667 -Collapsed=0 -DockId=0x00000006,0 - [Window][UE KPI] -Pos=532,19 -Size=528,667 +Pos=0,19 +Size=1920,180 Collapsed=0 -DockId=0x00000008,0 +DockId=0x00000007,0 [Window][UE PDSCH IQ] -Pos=0,19 -Size=530,667 +Pos=997,201 +Size=923,711 Collapsed=0 -DockId=0x00000005,1 - -[Window][Time domain samples] -Pos=0,19 -Size=1036,667 -Collapsed=0 -DockId=0x00000005,0 +DockId=0x0000000E,3 [Window][Time domain samples - before sync] -Pos=0,19 -Size=530,667 +Pos=0,201 +Size=995,711 Collapsed=0 -DockId=0x00000005,2 +DockId=0x0000000D,0 [Window][Broadcast channel] -Pos=0,19 -Size=530,667 +Pos=997,201 +Size=923,711 Collapsed=0 -DockId=0x00000005,3 +DockId=0x0000000E,1 [Window][RX IQ] Pos=532,19 Size=528,667 Collapsed=0 -DockId=0x00000008,0 +DockId=0x00000001,0 -[Window][PUSCH SLOT IQ] -Pos=0,19 -Size=1036,667 +[Window][UE PDSCH Chan est] +Pos=997,201 +Size=923,711 Collapsed=0 -DockId=0x00000005,1 +DockId=0x0000000E,2 -[Window][PUSCH LLRs] -Pos=0,19 -Size=1036,667 +[Window][UE PDSCH IQ before compensation] +Pos=0,201 +Size=995,711 Collapsed=0 -DockId=0x00000005,2 +DockId=0x0000000D,1 + +[Window][gNB Scope] +Pos=-4,30 +Size=1728,834 +Collapsed=0 + +[Window][UE Scope] +Pos=-1,23 +Size=1922,902 +Collapsed=0 + +[Window][Dear ImGui Demo/##Basket_379447C1] +IsChild=1 +Size=1202,260 + +[Window][Dear ImGui Demo/ResizableChild_D5443E47] +IsChild=1 +Size=1153,32 + +[Window][Dear ImGui Demo/Red_1D4E05CE] +IsChild=1 +Size=200,100 + +[Window][Delete?] +Pos=804,420 +Size=311,105 +Collapsed=0 + +[Window][Stacked 1] +Pos=755,387 +Size=409,172 +Collapsed=0 + +[Window][Example: Simple layout] +Pos=354,164 +Size=981,698 +Collapsed=0 + +[Window][Example: Simple layout/left pane_AED60EF8] +IsChild=1 +Size=150,644 + +[Window][Example: Log] +Pos=60,60 +Size=500,400 +Collapsed=0 + +[Window][Example: Auto-resizing window] +Pos=179,108 +Size=654,437 +Collapsed=0 + +[Window][gNB Scope/##pusch_time_domain_5637DE0C] +IsChild=1 +Size=1728,302 + +[Window][gNB Scope/##pusch_iq_903C5250] +IsChild=1 +Size=408,500 + +[Window][gNB Scope/##pusch_llr_FE553CCC] +IsChild=1 +Size=1443,502 + +[Window][Dear ImGui Demo/##Tree_C1CE970E] +IsChild=1 +Size=1132,260 + +[Window][Dear ImGui Demo/##Basket_97A64F14] +IsChild=1 +Size=1132,260 + +[Window][Dear ImGui Demo/##Basket_745C408A] +IsChild=1 +Size=1132,260 + +[Window][UE Scope/##ue_kpi_A22981AC] +IsChild=1 +Size=1839,183 + +[Window][UE Scope/##ue_iq_plots_F23AA7B4] +IsChild=1 +Size=814,517 + +[Window][UE Scope/##ue_plots0_D75041C0] +IsChild=1 +Size=1067,610 + +[Window][UE Scope/##ue_plots1_A0577156] +IsChild=1 +Size=733,604 + +[Window][UE PDCCH IQ] +Pos=997,201 +Size=923,711 +Collapsed=0 +DockId=0x0000000E,4 + +[Window][UE PDCCH LLR] +Pos=0,201 +Size=995,711 +Collapsed=0 +DockId=0x0000000D,2 + +[Table][0xC9935533,3] +Column 0 Weight=1.0000 +Column 1 Weight=1.0000 +Column 2 Weight=1.0000 + +[Table][0xE0773582,3] +Column 0 Weight=1.0000 +Column 1 Weight=1.0000 +Column 2 Weight=1.0000 + +[Table][0x45A0E60D,7] +RefScale=13 +Column 0 Width=49 +Column 1 Width=112 +Column 2 Width=112 +Column 3 Width=112 +Column 4 Width=112 +Column 5 Width=112 +Column 6 Width=112 + +[Table][0xDA36A7E0,6] +RefScale=13 +Column 0 Width=28 Sort=0v +Column 1 Width=42 +Column 2 Width=73 +Column 3 Width=68 +Column 4 Weight=1.0000 +Column 5 Width=-1 + +[Table][0x0919F5A2,14] +RefScale=13 +Column 0 Width=56 +Column 1 Width=19 +Column 2 Width=19 +Column 3 Width=19 +Column 4 Width=19 +Column 5 Width=19 +Column 6 Width=19 +Column 7 Width=19 +Column 8 Width=19 +Column 9 Width=19 +Column 10 Width=19 +Column 11 Width=19 +Column 12 Width=19 +Column 13 Width=19 + +[Table][0x8DFA6E86,2] +Column 0 Weight=1.0000 +Column 1 Weight=1.0000 + +[Table][0xFABAAEF7,2] +Column 0 Weight=1.0000 +Column 1 Weight=1.0000 + +[Table][0xC179E37C,3] +RefScale=13 +Column 0 Width=108 Sort=0v +Column 1 Width=63 +Column 2 Width=-1 [Docking][Data] -DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,19 Size=1280,701 Split=Y Selected=0x71C89FCB - DockNode ID=0x00000003 Parent=0x7C6B3D9B SizeRef=1280,667 Split=X - DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1319,720 Split=X Selected=0x6627CA6C - DockNode ID=0x00000007 Parent=0x00000001 SizeRef=530,667 Split=X Selected=0x93FDECFF - DockNode ID=0x00000005 Parent=0x00000007 SizeRef=1036,334 CentralNode=1 Selected=0x93FDECFF - DockNode ID=0x00000006 Parent=0x00000007 SizeRef=242,334 Selected=0xFAA8D9D5 - DockNode ID=0x00000008 Parent=0x00000001 SizeRef=528,667 Selected=0x6627CA6C - DockNode ID=0x00000002 Parent=0x00000003 SizeRef=529,720 Selected=0xB903C8C9 - DockNode ID=0x00000004 Parent=0x7C6B3D9B SizeRef=1280,32 HiddenTabBar=1 Selected=0xD9B9B9C7 +DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,19 Size=1920,927 Split=Y Selected=0x71C89FCB + DockNode ID=0x00000003 Parent=0x7C6B3D9B SizeRef=1280,667 Split=X + DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1319,720 Split=Y Selected=0x6627CA6C + DockNode ID=0x00000009 Parent=0x00000001 SizeRef=1920,236 Split=Y Selected=0x6627CA6C + DockNode ID=0x00000007 Parent=0x00000009 SizeRef=1920,180 CentralNode=1 Selected=0x6627CA6C + DockNode ID=0x00000008 Parent=0x00000009 SizeRef=1920,711 Split=X Selected=0x3A5D5B36 + DockNode ID=0x0000000D Parent=0x00000008 SizeRef=995,711 Selected=0xF0B5A18D + DockNode ID=0x0000000E Parent=0x00000008 SizeRef=923,711 Selected=0x752F58D8 + DockNode ID=0x0000000A Parent=0x00000001 SizeRef=1920,655 Split=X Selected=0x93FDECFF + DockNode ID=0x0000000B Parent=0x0000000A SizeRef=789,444 Selected=0x93FDECFF + DockNode ID=0x0000000C Parent=0x0000000A SizeRef=1129,444 Selected=0x43E927D5 + DockNode ID=0x00000002 Parent=0x00000003 SizeRef=529,720 Selected=0xB903C8C9 + DockNode ID=0x00000004 Parent=0x7C6B3D9B SizeRef=1280,32 HiddenTabBar=1 Selected=0xD9B9B9C7 From 812420348184073fa5b2d89ffbecba0e6c115c1e Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Mon, 10 Nov 2025 09:48:45 +0100 Subject: [PATCH 183/275] gNB: fix MAC TA command scheduling if measurement gaps are configured In the current implementation, if UE transmission is interrupted via nr_mac_interrupt_ue_transmission(), UE_sched_ctrl.ta_frame is modified to be the previous frame avoiding TA scheduling in the measurement gap. So the next scheduled TA is shifted to be in 99 frames. As there are other measurement gaps before that, the scheduled TA is always shifted, but actually never reached, effectively disabling the MAC TA commands. To fix this, UE_sched_ctrl.ta_frame is changed to not indicate the last TA command transmission, but the next one. This allows to check in nr_mac_interrupt_ue_transmission() if the next TA command transmission is within the measurement gap and if so shift it to just after that gap. --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 16 ++++++++-------- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 18 ++++++++++-------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 2 +- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 1 - 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index d848cf99c8..1466099ab1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -649,9 +649,8 @@ static void pf_dl(gNB_MAC_INST *mac, stats->current_bytes = 0; stats->current_rbs = 0; - /* Check if this UE should get TA (every 100 frames). If we add the CE, - * ta_apply will be reset */ - if (frame == ((sched_ctrl->ta_frame + 100) % 1024)) + /* Check if this UE should get TA. If we add the CE, ta_apply will be reset */ + if (frame == sched_ctrl->ta_frame) sched_ctrl->ta_apply = true; int total_rem_ues = 0; @@ -690,7 +689,7 @@ static void pf_dl(gNB_MAC_INST *mac, update_dlsch_buffer(pp_pdsch->frame, pp_pdsch->slot, UE); /* Check DL buffer and skip this UE if no bytes and no TA necessary */ - if (sched_ctrl->num_total_bytes == 0 && frame != (sched_ctrl->ta_frame + 100) % 1024) + if (sched_ctrl->num_total_bytes == 0 && sched_ctrl->ta_apply == false) continue; /* Calculate coeff */ @@ -871,8 +870,8 @@ static void pf_dl(gNB_MAC_INST *mac, // awaiting. Therefore, for the time being, we put a fixed overhead of 12 // (for 4 PDUs) and optionally + 2 for TA. Once RLC gives the number of // PDUs, we replace with 3 * numPDUs - const int oh = 3 * 4 + 2 * (frame == (sched_ctrl->ta_frame + 100) % 1024); - //const int oh = 3 * sched_ctrl->dl_pdus_total + 2 * (frame == (sched_ctrl->ta_frame + 100) % 1024); + const int oh = 3 * 4 + (sched_ctrl->ta_apply ? 2 : 0); + //const int oh = 3 * sched_ctrl->dl_pdus_total + (sched_ctrl->ta_apply ? 2 : 0); nr_find_nb_rb(sched_pdsch.Qm, sched_pdsch.R, 1, // no transform precoding for DL @@ -1349,8 +1348,9 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE // ta command is sent, values are reset if (sched_ctrl->ta_apply) { sched_ctrl->ta_apply = false; - sched_ctrl->ta_frame = frame; - LOG_D(NR_MAC, "%d.%2d UE %04x TA scheduled, resetting TA frame\n", frame, slot, UE->rnti); + sched_ctrl->ta_update = 31; + sched_ctrl->ta_frame = (frame + 100) % MAX_FRAME_NUMBER; + LOG_D(NR_MAC, "%d.%2d UE %04x TA scheduled, setting next TA frame to %d\n", frame, slot, UE->rnti, sched_ctrl->ta_frame); } T(T_GNB_MAC_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(rnti), diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index a81841d9c0..128f2db336 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -125,6 +125,8 @@ static const uint16_t cqi_table3[16][2] = {{0, 0}, 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); + uint8_t get_dl_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format) { // TODO check this but it should be enough for now @@ -3435,7 +3437,7 @@ void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot continue; const int slots_frame = nr_mac->frame_structure.numb_slots_frame; - const frame_t f = (frame + (slot + mgc->n_slots_advance) / slots_frame) % 1024; + const frame_t f = (frame + (slot + mgc->n_slots_advance) / slots_frame) % MAX_FRAME_NUMBER; const slot_t s = (slot + mgc->n_slots_advance) % slots_frame; // TS 38 331 - Section 5.5.2.9 Measurement gap configuration @@ -3446,7 +3448,7 @@ void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot // start a timer to stop scheduling UE during MeasGap, or extend timer for // duration of measGap with existing follow-up action if (!nr_timer_is_active(t) || nr_timer_remaining_time(t) < mgc->mgl_slots) { - nr_mac_interrupt_ue_transmission(nr_mac, UE, mgc->mgl_slots); + nr_mac_interrupt_ue_transmission(nr_mac, UE, mgc->mgl_slots, slots_frame); } } } @@ -3530,7 +3532,7 @@ int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs) return (delay_ms << scs) + sl_ahead; } -int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots) +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); @@ -3539,11 +3541,11 @@ int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int sl 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, set a variable as if we would have just - // sent it. This way, another TA command will for sure be sent in some - // frames, after the inactivity of the UE. - UE->UE_sched_ctrl.ta_frame = (mac->frame - 1 + 1024) % 1024; + // 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; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 94325bf26b..17a4ad5a42 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -811,7 +811,7 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, } // re-initialize ta update variables after RA procedure completion - UE->UE_sched_ctrl.ta_frame = frame; + UE->UE_sched_ctrl.ta_frame = (frame + 100) % MAX_FRAME_NUMBER; LOG_A(NR_MAC, "%4d.%2d PUSCH with TC_RNTI 0x%04x received correctly\n", frame, slot, rnti); diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index cfe02755b2..41f6fd5755 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -65,7 +65,6 @@ void nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, NR_UE_info_t *UE); bool add_new_UE_RA(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE); int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs); -int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots); int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info); void clear_nr_nfapi_information(gNB_MAC_INST *gNB, From 4057cdaa80218a71f2e0f8b22de6ba5ce54a1964 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Mon, 10 Nov 2025 13:32:36 +0000 Subject: [PATCH 184/275] NR UE: correctly provide measured SSB SINR value from PHY to MAC Fixes: e5ea77f629f7cc8be8756ae837de30b455a6d386 --- openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c | 1 + 1 file changed, 1 insertion(+) diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c b/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c index 7d6ab8e6f2..4c8c57d9fd 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c @@ -236,6 +236,7 @@ void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue, .meas_type = NFAPI_NR_SS_MEAS, .Nid_cell = ue->frame_parms.Nid_cell, .rsrp_dBm = ue->measurements.ssb_rsrp_dBm[ssb_index], + .sinr_dB = ue->measurements.ssb_sinr_dB[ssb_index], .ssb_index = ssb_index, .is_neighboring_cell = false, }; From 561fbf56368888e18a939aa813a8089370552f64 Mon Sep 17 00:00:00 2001 From: Reem Bahsoun Date: Wed, 12 Nov 2025 10:29:49 +0100 Subject: [PATCH 185/275] Modify aerial config file to improve UL throughput when using 2L layer. --- ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.conf b/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.conf index f26891d8f2..9446d08ebf 100644 --- a/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.conf +++ b/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.conf @@ -32,7 +32,7 @@ gNBs = pdsch_AntennaPorts_N1 = 2; pusch_AntennaPorts = 2; do_CSIRS = 1; - do_SRS = 0; + do_SRS = 1; min_rxtxtime = 2; servingCellConfigCommon = ( @@ -155,7 +155,7 @@ gNBs = nrofUplinkSlots = 3; #1; nrofUplinkSymbols = 0; - ssPBCH_BlockPower = -25; + ssPBCH_BlockPower = -15; } ); @@ -195,7 +195,7 @@ MACRLCs = ( remote_s_portd = 50010; // pnf p7 port [!] tr_s_preference = "aerial"; tr_n_preference = "local_RRC"; - pusch_TargetSNRx10 = 200; # 150; + pusch_TargetSNRx10 = 280; # 150; pucch_TargetSNRx10 = 200; #200; dl_max_mcs = 28; ul_max_mcs = 28; From 4cc6275a7d04016dcad1c43848ec82bba87e6c4d Mon Sep 17 00:00:00 2001 From: Reem Bahsoun Date: Wed, 12 Nov 2025 10:39:36 +0100 Subject: [PATCH 186/275] Update XML file to increase tested DL and UL throughput for CI. --- ci-scripts/xml_files/container_sa_aerial_quectel.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ci-scripts/xml_files/container_sa_aerial_quectel.xml b/ci-scripts/xml_files/container_sa_aerial_quectel.xml index 05d35d0171..bf3f702219 100644 --- a/ci-scripts/xml_files/container_sa_aerial_quectel.xml +++ b/ci-scripts/xml_files/container_sa_aerial_quectel.xml @@ -88,8 +88,8 @@ Iperf - iperf (DL/UDP/500M)(30 sec)(single-ue profile) - -u -b 500M -t 30 -R + iperf (DL/UDP/800M)(30 sec)(single-ue profile) + -u -b 800M -t 30 -R oc-cn5g-20897-aerial up2-aerial 25 @@ -98,8 +98,8 @@ Iperf - iperf (UL/UDP/40M)(30 sec)(single-ue profile) - -u -b 40M -t 30 -l 1428 + iperf (UL/UDP/150M)(30 sec)(single-ue profile) + -u -b 150M -t 30 -l 1428 oc-cn5g-20897-aerial up2-aerial 25 @@ -119,9 +119,9 @@ Iperf - Optional iperf (UL/UDP/90M)(30 sec)(single-ue profile) + Optional iperf (UL/UDP/200M)(30 sec)(single-ue profile) true - -u -b 90M -t 30 -l 1428 + -u -b 200M -t 30 -l 1428 oc-cn5g-20897-aerial up2-aerial 25 From 6471d91e2aaf687acab219d9d494508d1517b910 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 6 Nov 2025 13:23:49 +0100 Subject: [PATCH 187/275] Correct PNF config: sl_ahead to 2 Using nFAPI introduces additional delays. At the same time, RFsim does not run in real-time, and we can allow a low sl_ahead, as RFsim will "wait" for the gNB. Therefore, reduce sl_ahead to ensure that the response window does not run out during handling of RA in nFAPI. --- targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-pnf.band78.rfsim.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-pnf.band78.rfsim.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-pnf.band78.rfsim.conf index 887ff6eced..08691c3b62 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-pnf.band78.rfsim.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-pnf.band78.rfsim.conf @@ -22,6 +22,7 @@ RUs = ({ att_tx = 0 att_rx = 0; bands = [78]; + sl_ahead = 2; max_pdschReferenceSignalPower = -27; max_rxgain = 125; }); From 27203c9504553eda85fcc6da43ed67a572e7d449 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Nov 2025 14:44:15 +0100 Subject: [PATCH 188/275] Remove useless function definitions Unused code --- CMakeLists.txt | 8 +--- openair1/SCHED/nfapi_lte_dummy.c | 18 --------- openair1/SCHED/nfapi_nr_dummy.c | 49 ----------------------- openair1/SCHED_NR/fapi_nr_l1.c | 5 +-- openair1/SCHED_NR/fapi_nr_l1.h | 32 --------------- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 28 ++++++++----- 6 files changed, 22 insertions(+), 118 deletions(-) delete mode 100644 openair1/SCHED/nfapi_nr_dummy.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 57d165ea05..c5f54f1e67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1283,12 +1283,6 @@ set (MISC_NFAPI_LTE add_library(MISC_NFAPI_LTE_LIB ${MISC_NFAPI_LTE}) -set (MISC_NFAPI_NR - ${OPENAIR1_DIR}/SCHED/nfapi_nr_dummy.c - ) - -add_library(MISC_NFAPI_NR_LIB ${MISC_NFAPI_NR}) - add_library(L2 ${L2_SRC} ${MAC_SRC} @@ -2091,7 +2085,7 @@ if (${T_TRACER}) oai_eth_transpro HASHTABLE UTIL 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 MISC_NFAPI_NR_LIB + MISC_NFAPI_LTE_LIB PHY_COMMON PHY PHY_UE PHY_NR PHY_NR_COMMON PHY_NR_UE 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 diff --git a/openair1/SCHED/nfapi_lte_dummy.c b/openair1/SCHED/nfapi_lte_dummy.c index be532a0c54..3e56b51fc1 100644 --- a/openair1/SCHED/nfapi_lte_dummy.c +++ b/openair1/SCHED/nfapi_lte_dummy.c @@ -9,30 +9,12 @@ typedef struct pnf_p7_t pnf_p7_t; typedef struct vnf_t vnf_t; typedef struct vnf_p7_t vnf_p7_t; -void handle_nfapi_nr_pdcch_pdu(PHY_VARS_gNB *gNB, - int frame, int slot, - nfapi_nr_dl_tti_pdcch_pdu *pdcch_pdu){} - -void handle_nr_nfapi_ssb_pdu(PHY_VARS_gNB *gNB,int frame,int slot, - nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu){} - -void handle_nr_nfapi_pdsch_pdu(processingData_L1tx_t *msgTx, - nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu, - uint8_t *sdu){ -} -void handle_nfapi_nr_csirs_pdu(processingData_L1tx_t *msgTx, - int frame, int slot, - nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu){ - } int l1_north_init_gNB(void){return 0;} int slot_ahead = 6; //uint8_t nfapi_mode=0; NR_IF_Module_t *NR_IF_Module_init(int Mod_id) {return NULL;} -void handle_nfapi_nr_ul_dci_pdu(PHY_VARS_gNB *gNB, - int frame, int slot, - nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu){} void nr_fill_ulsch(PHY_VARS_gNB *gNB, int frame, int slot, diff --git a/openair1/SCHED/nfapi_nr_dummy.c b/openair1/SCHED/nfapi_nr_dummy.c deleted file mode 100644 index 7377a455dd..0000000000 --- a/openair1/SCHED/nfapi_nr_dummy.c +++ /dev/null @@ -1,49 +0,0 @@ -//Dummy NR defs to avoid linking errors - -#include "PHY/defs_gNB.h" -#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h" -#include "openair2/NR_PHY_INTERFACE/NR_IF_Module.h" -#include "openair1/PHY/LTE_TRANSPORT/transport_common.h" - -void handle_nfapi_nr_pdcch_pdu(PHY_VARS_gNB *gNB, - int frame, int slot, - nfapi_nr_dl_tti_pdcch_pdu *pdcch_pdu){} - -void handle_nr_nfapi_ssb_pdu(PHY_VARS_gNB *gNB,int frame,int slot, - nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu){} - -void handle_nr_nfapi_pdsch_pdu(processingData_L1tx_t *msgTx, - nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu, - uint8_t *sdu){ -} -void handle_nfapi_nr_csirs_pdu(processingData_L1tx_t *msgTx, - int frame, int slot, - nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu){ - } -int l1_north_init_gNB(void){return 0;} - -int slot_ahead = 6; -//uint8_t nfapi_mode=0; -NR_IF_Module_t *NR_IF_Module_init(int Mod_id) {return NULL;} - -void handle_nfapi_nr_ul_dci_pdu(PHY_VARS_gNB *gNB, - int frame, int slot, - nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu){} -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 install_nr_schedule_handlers(NR_IF_Module_t *if_inst){} - -//void nr_dump_frame_parms(NR_DL_FRAME_PARMS *fp){} - diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 9e4ab67efc..7b27b51c02 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -38,8 +38,7 @@ #include "openair2/NR_PHY_INTERFACE/nr_sched_response.h" #include "nfapi/oai_integration/nfapi_vnf.h" -void handle_nr_nfapi_ssb_pdu(processingData_L1tx_t *msgTx,int frame,int slot, - nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu) +static void handle_nr_nfapi_ssb_pdu(processingData_L1tx_t *msgTx, int frame, int slot, nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu) { AssertFatal(dl_tti_pdu->ssb_pdu.ssb_pdu_rel15.bchPayloadFlag== 1, "bchPayloadFlat %d != 1\n", @@ -56,7 +55,7 @@ void handle_nr_nfapi_ssb_pdu(processingData_L1tx_t *msgTx,int frame,int slot, } } -void handle_nfapi_nr_csirs_pdu(processingData_L1tx_t *msgTx, int frame, int slot, nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu) +static void handle_nfapi_nr_csirs_pdu(processingData_L1tx_t *msgTx, int frame, int slot, nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu) { int found = 0; diff --git a/openair1/SCHED_NR/fapi_nr_l1.h b/openair1/SCHED_NR/fapi_nr_l1.h index 59df4fb30d..b8f1b6e238 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.h +++ b/openair1/SCHED_NR/fapi_nr_l1.h @@ -34,36 +34,4 @@ #include "SCHED_NR/sched_nr.h" #include "nfapi_nr_interface_scf.h" -// added -void handle_nr_nfapi_ssb_pdu(processingData_L1tx_t *msgTx, - int frame,int slot, - nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu); - void nr_schedule_response(NR_Sched_Rsp_t *Sched_INFO); - -void handle_nfapi_nr_csirs_pdu(processingData_L1tx_t *msgTx, - int frame, int slot, - nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu); - -void handle_nfapi_nr_pdcch_pdu(PHY_VARS_gNB *gNB, - int frame, int subframe, - nfapi_nr_dl_tti_pdcch_pdu *dcl_dl_pdu); - -void handle_nr_nfapi_pdsch_pdu(processingData_L1tx_t *msgTx, - nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu, - uint8_t *sdu); - -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); -//added - -void handle_nfapi_nr_ul_dci_pdu(PHY_VARS_gNB *gNB, - int frame, int slot, - nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 51e8c771e5..5111394211 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -46,6 +46,16 @@ //#define DEBUG_RXDATA //#define SRS_IND_DEBUG +static void nr_fill_indication(PHY_VARS_gNB *gNB, + int frame, + int slot_rx, + int ULSCH_id, + uint8_t harq_pid, + uint8_t crc_flag, + int dtx_flag, + nfapi_nr_crc_t *crc, + nfapi_nr_rx_data_pdu_t *pdu); + int beam_index_allocation(bool das, int fapi_beam_index, nfapi_nr_analog_beamforming_ve_t *analog_bf, @@ -537,15 +547,15 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, boo return ret_nr_ulsch_decoding; } -void nr_fill_indication(PHY_VARS_gNB *gNB, - int frame, - int slot_rx, - int ULSCH_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, + int frame, + int slot_rx, + int ULSCH_id, + uint8_t harq_pid, + uint8_t crc_flag, + int dtx_flag, + nfapi_nr_crc_t *crc, + nfapi_nr_rx_data_pdu_t *pdu) { NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; NR_UL_gNB_HARQ_t *harq_process = ulsch->harq_process; From 383c94311e188ee6434f10b79286c55e5f7f67d3 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Nov 2025 15:17:04 +0100 Subject: [PATCH 189/275] Pass big struct through pointer --- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 10 +++++----- openair1/SCHED_NR/sched_nr.h | 2 +- openair1/SIMULATION/NR_PHY/pbchsim.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 5111394211..09415dd86f 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -94,10 +94,10 @@ int beam_index_allocation(bool das, return idx; } -void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_dl_tti_ssb_pdu ssb_pdu) +void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu) { NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu = &ssb_pdu.ssb_pdu_rel15; + const nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu = &ssb_pdu->ssb_pdu_rel15; uint8_t ssb_index = pdu->SsbBlockIndex; LOG_D(PHY,"common_signal_procedures: frame %d, slot %d ssb index %d\n", frame, slot, ssb_index); @@ -145,7 +145,7 @@ void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_n fp->ssb_start_subcarrier); LOG_D(PHY,"SS TX: frame %d, slot %d, start_symbol %d\n", frame, slot, ssb_start_symbol); - nfapi_nr_tx_precoding_and_beamforming_t *pb = &pdu->precoding_and_beamforming; + const nfapi_nr_tx_precoding_and_beamforming_t *pb = &pdu->precoding_and_beamforming; c16_t ***txdataF = gNB->common_vars.txdataF; int txdataF_offset = slot * fp->samples_per_slot_wCP; // beam number in a scenario with multiple concurrent beams @@ -183,7 +183,7 @@ void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_n #endif nr_generate_pbch(gNB, - &ssb_pdu, + ssb_pdu, &txdataF[beam_nb][0][txdataF_offset], ssb_start_symbol, n_hf, @@ -245,7 +245,7 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_COMMON_TX,1); for (int i = 0; i < fp->Lmax; i++) { if (msgTx->ssb[i].active) { - nr_common_signal_procedures(gNB, frame, slot, msgTx->ssb[i].ssb_pdu); + nr_common_signal_procedures(gNB, frame, slot, &msgTx->ssb[i].ssb_pdu); msgTx->ssb[i].active = false; } } diff --git a/openair1/SCHED_NR/sched_nr.h b/openair1/SCHED_NR/sched_nr.h index 40c32c2199..cbec9fa1db 100644 --- a/openair1/SCHED_NR/sched_nr.h +++ b/openair1/SCHED_NR/sched_nr.h @@ -36,7 +36,7 @@ void nr_set_ssb_first_subcarrier(nfapi_nr_config_request_scf_t *cfg, NR_DL_FRAME void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, int frame_tx, int slot_tx, int do_meas); 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 nr_common_signal_procedures (PHY_VARS_gNB *gNB,int frame,int slot,nfapi_nr_dl_tti_ssb_pdu ssb_pdu); +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); void nr_feptx_prec(RU_t *ru,int frame_tx,int tti_tx); diff --git a/openair1/SIMULATION/NR_PHY/pbchsim.c b/openair1/SIMULATION/NR_PHY/pbchsim.c index 4b44b34091..97421130af 100644 --- a/openair1/SIMULATION/NR_PHY/pbchsim.c +++ b/openair1/SIMULATION/NR_PHY/pbchsim.c @@ -541,7 +541,7 @@ int main(int argc, char **argv) for (aa=0; aaframe_parms.nb_antennas_tx; aa++) memset(gNB->common_vars.txdataF[0][aa], 0, frame_parms->samples_per_slot_wCP * sizeof(int32_t)); - nr_common_signal_procedures (gNB,frame,slot,msgDataTx.ssb[i].ssb_pdu); + nr_common_signal_procedures (gNB,frame,slot, &msgDataTx.ssb[i].ssb_pdu); int samp = frame_parms->get_samples_slot_timestamp(slot, frame_parms, 0); for (aa=0; aaframe_parms.nb_antennas_tx; aa++) { From 17ee9db07281d51a4cc7a681b1e454af3f022f89 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Nov 2025 18:29:36 +0100 Subject: [PATCH 190/275] MAC: total_prb_aggregate: collect over carrierBandwidth --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index d848cf99c8..1990eb2e26 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1387,8 +1387,8 @@ void nr_schedule_ue_spec(module_id_t module_id, return; NR_ServingCellConfigCommon_t *scc = gNB_mac->common_channels[CC_id].ServingCellConfigCommon; - const NR_BWP_t *initialDL = &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; - gNB_mac->mac_stats.total_prb_aggregate += NRRIV2BW(initialDL->locationAndBandwidth, MAX_BWP_SIZE); + int bw = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; + gNB_mac->mac_stats.total_prb_aggregate += bw; nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body; post_process_pdsch_t pdsch = { frame, slot, dl_req, TX_req }; From 021b546cf3f79663b2db9f65bead3b8767331cbf Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 24 Oct 2025 16:27:45 +0200 Subject: [PATCH 191/275] Add UL mac stats and create a common struct for both DL and UL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DL stats (aggregated total and used PRBs) previosuly used only for O1 interface, now finds the purpose for E2 interface as well. New UL stats (aggregated total and used PRBs) serve for E2 interface only. Co-authored-by: Teodora Vladiฤ‡ --- common/utils/telnetsrv/telnetsrv_o1.c | 8 ++++---- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 6 +++--- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 2 ++ openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 7 ++++++- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/common/utils/telnetsrv/telnetsrv_o1.c b/common/utils/telnetsrv/telnetsrv_o1.c index ac368885f6..9ff56e3521 100644 --- a/common/utils/telnetsrv/telnetsrv_o1.c +++ b/common/utils/telnetsrv/telnetsrv_o1.c @@ -105,10 +105,10 @@ static int get_stats(char *buf, int debug, telnet_printfunc_t prnt) int bw_index = get_supported_band_index(scs, fr, nrb); int bw_mhz = get_supported_bw_mhz(fr, bw_index); - const mac_stats_t *stat = &mac->mac_stats; - static mac_stats_t last = {0}; - int diff_used = stat->used_prb_aggregate - last.used_prb_aggregate; - int diff_total = stat->total_prb_aggregate - last.total_prb_aggregate; + const dlul_mac_stats_t *stat = &mac->mac_stats; + static dlul_mac_stats_t last = {0}; + int diff_used = stat->dl.used_prb_aggregate - last.dl.used_prb_aggregate; + int diff_total = stat->dl.total_prb_aggregate - last.dl.total_prb_aggregate; int load = diff_total > 0 ? 100 * diff_used / diff_total : 0; last = *stat; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 1990eb2e26..adf091ed7c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1224,7 +1224,7 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE T(T_GNB_MAC_RETRANSMISSION_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(rnti), T_INT(frame), T_INT(slot), T_INT(current_harq_pid), T_INT(harq->round), T_BUFFER(harq->transportBlock.buf, TBS)); UE->mac_stats.dl.total_rbs_retx += sched_pdsch->rbSize; - nr_mac->mac_stats.used_prb_aggregate += sched_pdsch->rbSize; + nr_mac->mac_stats.dl.used_prb_aggregate += sched_pdsch->rbSize; } else { /* initial transmission */ LOG_D(NR_MAC, "Initial HARQ transmission in %d.%d\n", frame, slot); uint8_t *buf = allocate_transportBlock_buffer(&harq->transportBlock, TBS); @@ -1338,7 +1338,7 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE UE->mac_stats.dl.num_mac_sdu += sdus; UE->mac_stats.dl.current_rbs = sched_pdsch->rbSize; UE->mac_stats.dl.total_sdu_bytes += dlsch_total_bytes; - nr_mac->mac_stats.used_prb_aggregate += sched_pdsch->rbSize; + nr_mac->mac_stats.dl.used_prb_aggregate += sched_pdsch->rbSize; /* save retransmission information */ harq->sched_pdsch = *sched_pdsch; @@ -1388,7 +1388,7 @@ void nr_schedule_ue_spec(module_id_t module_id, NR_ServingCellConfigCommon_t *scc = gNB_mac->common_channels[CC_id].ServingCellConfigCommon; int bw = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; - gNB_mac->mac_stats.total_prb_aggregate += bw; + gNB_mac->mac_stats.dl.total_prb_aggregate += bw; nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body; post_process_pdsch_t pdsch = { frame, slot, dl_req, TX_req }; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 94325bf26b..cf699d1bae 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -2455,6 +2455,7 @@ void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE } UE->mac_stats.ul.current_bytes = sched_pusch->tb_size; UE->mac_stats.ul.current_rbs = sched_pusch->rbSize; + nr_mac->mac_stats.ul.used_prb_aggregate += sched_pusch->rbSize; sched_ctrl->last_ul_frame = sched_pusch->frame; sched_ctrl->last_ul_slot = sched_pusch->slot; @@ -2697,6 +2698,7 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp int tda = seq_arr_dist(&nr_mac->ul_tda, seq_arr_front(&nr_mac->ul_tda), tda_info); AssertFatal(tda >= 0 && tda < 16, "illegal TDA index %d\n", tda); + nr_mac->mac_stats.ul.total_prb_aggregate += bw; int len[num_beams]; for (int i = 0; i < num_beams; i++) len[i] = rb_len; diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index d6a9ae1497..aa8b2b8644 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -870,6 +870,11 @@ typedef struct { uint64_t used_prb_aggregate; } mac_stats_t; +typedef struct dlul_mac_stats { + mac_stats_t dl; + mac_stats_t ul; +} dlul_mac_stats_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. @@ -996,7 +1001,7 @@ typedef struct gNB_MAC_INST_s { pthread_mutex_t sched_lock; - mac_stats_t mac_stats; + dlul_mac_stats_t mac_stats; uint64_t num_scheduled_prach_rx; } gNB_MAC_INST; From d0a582e73d0ac2c1f2c3f070daad8a53a22c3e5b Mon Sep 17 00:00:00 2001 From: Teodora Date: Fri, 24 Oct 2025 16:36:44 +0200 Subject: [PATCH 192/275] Implement E2 node level stats For now, only DL/UL MAC stats. This commit fixes the "RRU.PrbTotDl" and "RRU.PrbTotUl" measurement calculation to align with the TS 28.552 specifications. --- .../E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c | 16 ++++++-- .../RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c | 40 ++++++++++++------- .../RAN_FUNCTION/O-RAN/ran_func_kpm_subs.h | 10 ++++- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c index 02760c4984..8933b802a0 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c @@ -54,10 +54,12 @@ typedef struct { ue_id_e2sm_t* ue_id; // Optional - // only used to retreive MAC/RLC stats + // only used to retrieve MAC/RLC stats NR_UE_info_t** ue_info_list; }arr_ue_id_t; +static e2_node_level_stats_t node_stats[2] = {0}; // [0] node stats collected in previous reporting period; [1] current node stats + static meas_data_lst_t fill_kpm_meas_data_item(const meas_info_format_1_lst_t* meas_info_lst, const size_t len, const uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) { meas_data_lst_t data_item = {0}; @@ -74,7 +76,7 @@ static meas_data_lst_t fill_kpm_meas_data_item(const meas_info_format_1_lst_t* m char* meas_info_name_str = cp_ba_to_str(meas_info_lst[i].meas_type.name); - data_item.meas_record_lst[i] = get_kpm_meas_value(meas_info_name_str, gran_period_ms, ue_info, ue_idx); + data_item.meas_record_lst[i] = get_kpm_meas_value(meas_info_name_str, gran_period_ms, ue_info, ue_idx, node_stats); free(meas_info_name_str); } @@ -153,6 +155,8 @@ static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3(arr_ue_id_t* arr_ue_id, con msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = fill_kpm_ind_msg_frm_1(ue_info, i, act_def_fr_1); } + node_stats[0] = cp_node_level_stats(&node_stats[1]); + return msg_frm_3; } @@ -288,8 +292,10 @@ static arr_ue_id_t filter_ues_by_s_nssai_in_du_or_monolithic(const test_info_lst const ngran_node_t node_type = get_e2_node_type(); + gNB_MAC_INST *mac = RC.nrmac[0]; + NR_SCHED_LOCK(&mac->sched_lock); // Take MAC info - UE_iterator(RC.nrmac[0]->UE_info.connected_ue_list, ue) { + UE_iterator(mac->UE_info.connected_ue_list, ue) { NR_UE_sched_ctrl_t *sched_ctrl = &ue->UE_sched_ctrl; // UE matches if any of its DRBs matches for (size_t l = 0; l < seq_arr_size(&sched_ctrl->lc_config); ++l) { @@ -316,6 +322,10 @@ static arr_ue_id_t filter_ues_by_s_nssai_in_du_or_monolithic(const test_info_lst AssertFatal(arr_ue_id.sz < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n"); } + node_stats[1].mac_stats.dl.total_prb_aggregate = mac->mac_stats.dl.total_prb_aggregate; + node_stats[1].mac_stats.ul.total_prb_aggregate = mac->mac_stats.ul.total_prb_aggregate; + NR_SCHED_UNLOCK(&mac->sched_lock); + free(sd); // if NULL, nothing happens return arr_ue_id; diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c index 80579e1754..a8ebe580ab 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c @@ -23,6 +23,18 @@ #include +e2_node_level_stats_t cp_node_level_stats(const e2_node_level_stats_t *src) +{ + e2_node_level_stats_t dst = { + .mac_stats.dl.total_prb_aggregate = src->mac_stats.dl.total_prb_aggregate, + .mac_stats.dl.used_prb_aggregate = src->mac_stats.dl.used_prb_aggregate, + .mac_stats.ul.total_prb_aggregate = src->mac_stats.ul.total_prb_aggregate, + .mac_stats.ul.used_prb_aggregate = src->mac_stats.ul.used_prb_aggregate, + }; + + return dst; +} + /* measurements that need to store values from previous reporting period have a limitation when it comes to multiple subscriptions to the same UEs; ric_req_id is unique per subscription */ typedef struct uldlcounter { @@ -46,7 +58,7 @@ static nr_pdcp_statistics_t get_pdcp_stats_per_drb(const uint32_t rrc_ue_id, con /* 3GPP TS 28.522 - section 5.1.2.1.1.1 note: this measurement is calculated as per spec */ -static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, __attribute__((unused))e2_node_level_stats_t* node_stats) { meas_record_lst_t meas_record = {0}; @@ -65,7 +77,7 @@ static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_ /* 3GPP TS 28.522 - section 5.1.2.1.2.1 note: this measurement is calculated as per spec */ -static meas_record_lst_t fill_DRB_PdcpSduVolumeUL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +static meas_record_lst_t fill_DRB_PdcpSduVolumeUL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, __attribute__((unused))e2_node_level_stats_t* node_stats) { meas_record_lst_t meas_record = {0}; @@ -103,7 +115,7 @@ static nr_rlc_statistics_t get_rlc_stats_per_drb(const rnti_t rnti, const int rb /* 3GPP TS 28.522 - section 5.1.3.3.3 note: by default this measurement is calculated for previous 100ms (openair2/LAYER2/nr_rlc/nr_rlc_entity.c:118, 173, 213); please, update according to your needs */ -static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, __attribute__((unused))const size_t ue_idx) +static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, __attribute__((unused))const size_t ue_idx, __attribute__((unused))e2_node_level_stats_t* node_stats) { meas_record_lst_t meas_record = {0}; @@ -122,7 +134,7 @@ static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused))uint32_t /* 3GPP TS 28.522 - section 5.1.1.3.1 note: per spec, average UE throughput in DL (taken into consideration values from all UEs, and averaged) here calculated as: UE specific throughput in DL */ -static meas_record_lst_t fill_DRB_UEThpDl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +static meas_record_lst_t fill_DRB_UEThpDl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, __attribute__((unused))e2_node_level_stats_t* node_stats) { meas_record_lst_t meas_record = {0}; @@ -141,7 +153,7 @@ static meas_record_lst_t fill_DRB_UEThpDl(uint32_t gran_period_ms, cudu_ue_info_ /* 3GPP TS 28.522 - section 5.1.1.3.3 note: per spec, average UE throughput in UL (taken into consideration values from all UEs, and averaged) here calculated as: UE specific throughput in UL */ -static meas_record_lst_t fill_DRB_UEThpUl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +static meas_record_lst_t fill_DRB_UEThpUl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, __attribute__((unused))e2_node_level_stats_t* node_stats) { meas_record_lst_t meas_record = {0}; @@ -159,32 +171,30 @@ static meas_record_lst_t fill_DRB_UEThpUl(uint32_t gran_period_ms, cudu_ue_info_ } /* 3GPP TS 28.522 - section 5.1.1.2.1 - note: per spec, DL PRB usage [%] = (total used PRBs for DL traffic / total available PRBs for DL traffic) * 100 - here calculated as: aggregated DL PRBs (t) - aggregated DL PRBs (t-gran_period) */ -static meas_record_lst_t fill_RRU_PrbTotDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) + note: this measurement is calculated as per spec */ +static meas_record_lst_t fill_RRU_PrbTotDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, e2_node_level_stats_t* node_stats) { meas_record_lst_t meas_record = {0}; meas_record.value = INTEGER_MEAS_VALUE; // Get the number of DL PRBs - meas_record.int_val = ue_info.ue->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl; // [PRBs] + meas_record.int_val = (ue_info.ue->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl) * 100 / (node_stats[1].mac_stats.dl.total_prb_aggregate - node_stats[0].mac_stats.dl.total_prb_aggregate); // [%] last_total_prbs[ue_idx].dl = ue_info.ue->mac_stats.dl.total_rbs; return meas_record; } /* 3GPP TS 28.522 - section 5.1.1.2.2 - note: per spec, UL PRB usage [%] = (total used PRBs for UL traffic / total available PRBs for UL traffic) * 100 - here calculated as: aggregated UL PRBs (t) - aggregated UL PRBs (t-gran_period) */ -static meas_record_lst_t fill_RRU_PrbTotUl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) + note: this measurement is calculated as per spec */ +static meas_record_lst_t fill_RRU_PrbTotUl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, e2_node_level_stats_t* node_stats) { meas_record_lst_t meas_record = {0}; meas_record.value = INTEGER_MEAS_VALUE; // Get the number of UL PRBs - meas_record.int_val = ue_info.ue->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul; // [PRBs] + meas_record.int_val = (ue_info.ue->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul) * 100 / (node_stats[1].mac_stats.ul.total_prb_aggregate - node_stats[0].mac_stats.ul.total_prb_aggregate); // [%] last_total_prbs[ue_idx].ul = ue_info.ue->mac_stats.ul.total_rbs; return meas_record; @@ -217,7 +227,7 @@ void init_kpm_subs_data(void) } } -meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, e2_node_level_stats_t* node_stats) { assert(kpm_meas_name != NULL); @@ -226,7 +236,7 @@ meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_m assert(found_entry != NULL && "Unsupported KPM measurement name"); kv_measure_t *kv_found = (kv_measure_t *)found_entry->data; - meas_record_lst_t meas_record = kv_found->value(gran_period_ms, ue_info, ue_idx); + meas_record_lst_t meas_record = kv_found->value(gran_period_ms, ue_info, ue_idx, node_stats); return meas_record; } diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.h b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.h index 7dbd807fa1..e612ca2bab 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.h +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.h @@ -34,7 +34,13 @@ typedef struct { } cudu_ue_info_pair_t; -typedef meas_record_lst_t (*kpm_meas_fp)(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx); +typedef struct { + dlul_mac_stats_t mac_stats; +} e2_node_level_stats_t; + +e2_node_level_stats_t cp_node_level_stats(const e2_node_level_stats_t *src); + +typedef meas_record_lst_t (*kpm_meas_fp)(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, e2_node_level_stats_t* node_stats); typedef struct{ char* key; @@ -44,6 +50,6 @@ typedef struct{ void init_kpm_subs_data(void); -meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx); +meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, e2_node_level_stats_t* node_stats); #endif From f88b93d14729e911a6dab262967c3ca3147b5e0f Mon Sep 17 00:00:00 2001 From: Teodora Date: Mon, 3 Nov 2025 12:14:50 +0100 Subject: [PATCH 193/275] Use the correct unit for PDCP SDU volume: [Mb] instead of [kb] --- openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c index a8ebe580ab..d3381cadd3 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c @@ -69,7 +69,7 @@ static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_ meas_record.value = INTEGER_MEAS_VALUE; // Get DL data volume delivered to PDCP layer - meas_record.int_val = (pdcp.rxsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].dl)*8/1000; // [kb] + meas_record.int_val = (pdcp.rxsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].dl)*8/1000000; // [Mb] last_pdcp_sdu_total_bytes[ue_idx].dl = pdcp.rxsdu_bytes; return meas_record; @@ -88,7 +88,7 @@ static meas_record_lst_t fill_DRB_PdcpSduVolumeUL(__attribute__((unused))uint32_ meas_record.value = INTEGER_MEAS_VALUE; // Get UL data volume delivered from PDCP layer - meas_record.int_val = (pdcp.txsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].ul)*8/1000; // [kb] + meas_record.int_val = (pdcp.txsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].ul)*8/1000000; // [Mb] last_pdcp_sdu_total_bytes[ue_idx].ul = pdcp.txsdu_bytes; return meas_record; From e96cfbd88a8607aded713ecb8f287edd48b8cc41 Mon Sep 17 00:00:00 2001 From: Teodora Date: Thu, 6 Nov 2025 15:30:46 +0100 Subject: [PATCH 194/275] Update the FlexRIC submodule Needed notably due to https://gitlab.eurecom.fr/mosaic5g/flexric/-/merge_requests/68. --- openair2/E2AP/flexric | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/E2AP/flexric b/openair2/E2AP/flexric index df754a8553..6a595d8b24 160000 --- a/openair2/E2AP/flexric +++ b/openair2/E2AP/flexric @@ -1 +1 @@ -Subproject commit df754a85537f6db255b5330b8d83eda6a2742f25 +Subproject commit 6a595d8b243601b7148f60b5cb2cec47658e58c5 From b6bda6d97461dc1bffa6ef5f5f4e1e584cc5ea34 Mon Sep 17 00:00:00 2001 From: Teodora Date: Thu, 6 Nov 2025 15:26:49 +0100 Subject: [PATCH 195/275] RIC IP address within fr_args_t struct is not const anymore Related FlexRIC commit ID: https://gitlab.eurecom.fr/mosaic5g/flexric/-/commit/fd26e7474d18e90097bc1b2487d00598e9383400 --- executables/nr-cuup.c | 5 +++-- executables/nr-softmodem.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/executables/nr-cuup.c b/executables/nr-cuup.c index 0c2c9cdf3f..df2f633d95 100644 --- a/executables/nr-cuup.c +++ b/executables/nr-cuup.c @@ -51,8 +51,9 @@ static void initialize_agent(ngran_node_t node_type, e2_agent_args_t oai_args) printf("After RCconfig_NR_E2agent %s %s \n",oai_args.sm_dir, oai_args.ip ); - fr_args_t args = { .ip = oai_args.ip }; // init_fr_args(0, NULL); - memcpy(args.libs_dir, oai_args.sm_dir, 128); + fr_args_t args = {0}; + memcpy(args.ip, oai_args.ip, FR_IP_ADDRESS_LEN); + memcpy(args.libs_dir, oai_args.sm_dir, FR_CONF_FILE_LEN); sleep(1); diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 0bd583662b..945e7e4cae 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -473,8 +473,9 @@ static void initialize_agent(ngran_node_t node_type, e2_agent_args_t oai_args) printf("After RCconfig_NR_E2agent %s %s \n",oai_args.sm_dir, oai_args.ip ); - fr_args_t args = { .ip = oai_args.ip }; // init_fr_args(0, NULL); - memcpy(args.libs_dir, oai_args.sm_dir, 128); + fr_args_t args = {0}; + memcpy(args.ip, oai_args.ip, FR_IP_ADDRESS_LEN); + memcpy(args.libs_dir, oai_args.sm_dir, FR_CONF_FILE_LEN); sleep(1); const gNB_RRC_INST* rrc = RC.nrrrc[0]; From 8cf7dd860796d1b7937f376bccbe8df976fe09b2 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Nov 2025 17:49:54 +0100 Subject: [PATCH 196/275] CI: use intermediate variable for YAML file This harmonizes with DeployObject() (which uses the same variable) and shortens some lines for readability. --- ci-scripts/cls_containerize.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 3cebcb24d7..32ce498be0 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -760,17 +760,18 @@ class Containerize(): logging.debug(f'\u001B[1m Undeploying OAI Object from server: {node}\u001B[0m') yaml = self.yamlPath.strip('/') wd = f'{lSourcePath}/{yaml}' + wd_yaml = f'{wd}/docker-compose.y*ml' with cls_cmd.getConnection(node) as ssh: ExistEnvFilePrint(ssh, wd) - services = GetRunningServices(ssh, f"{wd}/docker-compose.y*ml") + services = GetRunningServices(ssh, wd_yaml) copyin_res = None if services is not None: all_serv = " ".join([s for s, _ in services]) - ssh.run(f'docker compose -f {wd}/docker-compose.y*ml stop -- {all_serv}') - copyin_res = [CopyinServiceLog(ssh, lSourcePath, s, f"{wd}/docker-compose.y*ml", ctx) for s, _ in services] + ssh.run(f'docker compose -f {wd_yaml} stop -- {all_serv}') + copyin_res = [CopyinServiceLog(ssh, lSourcePath, s, wd_yaml, ctx) for s, _ in services] else: logging.warning('could not identify services to stop => no log file') - ssh.run(f'docker compose -f {wd}/docker-compose.y*ml down -v') + ssh.run(f'docker compose -f {wd_yaml} down -v') ssh.run(f'rm {wd}/.env') if not copyin_res: HTML.CreateHtmlTestRowQueue('N/A', 'KO', ['Could not copy logfile(s)']) From 4b91022387d17a31931b207c7800f4abec13ff69 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Nov 2025 08:57:42 +0100 Subject: [PATCH 197/275] Pass read-only parameters as const --- common/utils/nr/nr_common.c | 6 +++--- common/utils/nr/nr_common.h | 4 ++-- openair1/PHY/NR_TRANSPORT/nr_dci.c | 2 +- openair1/PHY/NR_TRANSPORT/nr_dci.h | 2 +- openair1/PHY/NR_TRANSPORT/nr_dci_tools.c | 2 +- openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 14 +++++++------- openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c | 4 ++-- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 2 +- openair1/SCHED_NR/sched_nr.h | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index 8e6a16c97d..25f645d79f 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -541,8 +541,8 @@ int cce_to_reg_interleaving(const int R, int k, int n_shift, const int C, int L, return f; } -void get_coreset_rballoc(uint8_t *FreqDomainResource,int *n_rb,int *rb_offset) { - +void get_coreset_rballoc(const uint8_t *FreqDomainResource, int *n_rb, int *rb_offset) +{ uint8_t count=0, start=0, start_set=0; uint64_t bitmap = (((uint64_t)FreqDomainResource[0])<<37)| @@ -565,7 +565,7 @@ void get_coreset_rballoc(uint8_t *FreqDomainResource,int *n_rb,int *rb_offset) { } // According to 38.211 7.3.2.2 -int get_coreset_num_cces(uint8_t *FreqDomainResource, int duration) +int get_coreset_num_cces(const uint8_t *FreqDomainResource, int duration) { int num_rbs; int rb_offset; diff --git a/common/utils/nr/nr_common.h b/common/utils/nr/nr_common.h index e0d68a45ad..2a1e77cb30 100644 --- a/common/utils/nr/nr_common.h +++ b/common/utils/nr/nr_common.h @@ -280,8 +280,8 @@ uint32_t to_nrarfcn(int nr_bandP, uint64_t dl_CarrierFreq, uint8_t scs_index, ui int cce_to_reg_interleaving(const int R, int k, int n_shift, const int C, int L, const int N_regs); int get_SLIV(uint8_t S, uint8_t L); -void get_coreset_rballoc(uint8_t *FreqDomainResource,int *n_rb,int *rb_offset); -int get_coreset_num_cces(uint8_t *FreqDomainResource, int duration); +void get_coreset_rballoc(const uint8_t *FreqDomainResource, int *n_rb, int *rb_offset); +int get_coreset_num_cces(const uint8_t *FreqDomainResource, int duration); int get_nr_table_idx(int nr_bandP, uint8_t scs_index); int32_t get_delta_duplex(int nr_bandP, uint8_t scs_index); frame_type_t get_frame_type(uint16_t nr_bandP, uint8_t scs_index); diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.c b/openair1/PHY/NR_TRANSPORT/nr_dci.c index 0548c8fc10..0566eb428c 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dci.c @@ -52,7 +52,7 @@ static void nr_pdcch_scrambling(uint32_t *in, uint32_t size, uint32_t Nid, uint3 } static void nr_generate_dci(PHY_VARS_gNB *gNB, - nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15, + const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15, int txdataF_offset, NR_DL_FRAME_PARMS *frame_parms, int slot) diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.h b/openair1/PHY/NR_TRANSPORT/nr_dci.h index e735069975..2fb0f5aedd 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci.h +++ b/openair1/PHY/NR_TRANSPORT/nr_dci.h @@ -42,6 +42,6 @@ void nr_fill_ul_dci(PHY_VARS_gNB *gNB, int slot, nfapi_nr_ul_dci_request_pdus_t *pdcch_pdu); -void nr_fill_reg_list(int cce_list[MAX_DCI_CORESET][NR_MAX_PDCCH_AGG_LEVEL * NR_NB_REG_PER_CCE], nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15); +void nr_fill_reg_list(int cce_list[MAX_DCI_CORESET][NR_MAX_PDCCH_AGG_LEVEL * NR_NB_REG_PER_CCE], const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15); #endif //__PHY_NR_TRANSPORT_DCI__H diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c b/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c index 538b54ac0b..c2d2ab7a24 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c @@ -42,7 +42,7 @@ int compfunc(const void *a, const void *b) return (*(int *)a - *(int *)b); } -void nr_fill_reg_list(int reg_list[MAX_DCI_CORESET][NR_MAX_PDCCH_AGG_LEVEL * NR_NB_REG_PER_CCE], nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15) +void nr_fill_reg_list(int reg_list[MAX_DCI_CORESET][NR_MAX_PDCCH_AGG_LEVEL * NR_NB_REG_PER_CCE], const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15) { int bsize = pdcch_pdu_rel15->RegBundleSize; int R = pdcch_pdu_rel15->InterleaverSize; diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index 535c68f419..c5b981d301 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -51,7 +51,7 @@ static void nr_pdsch_codeword_scrambling(uint8_t *in, uint32_t size, uint8_t q, nr_codeword_scrambling(in, size, q, Nid, n_RNTI, out); } -static int do_ptrs_symbol(nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15, +static int do_ptrs_symbol(const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15, int start_sc, int symbol_sz, c16_t *txF, @@ -339,7 +339,7 @@ static inline void neg_dmrs(c16_t *in, c16_t *out, int sz) static inline int do_onelayer(NR_DL_FRAME_PARMS *frame_parms, int slot, - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15, + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15, int layer, c16_t *output, c16_t *txl_start, @@ -457,7 +457,7 @@ static inline void do_txdataF(c16_t **txdataF, int symbol_sz, c16_t txdataF_precoding[][symbol_sz], PHY_VARS_gNB *gNB, - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15, + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15, int ant, int start_sc, int txdataF_offset_per_symbol) @@ -465,7 +465,7 @@ static inline void do_txdataF(c16_t **txdataF, NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; int rb = 0; uint16_t subCarrier = start_sc; - nfapi_nr_tx_precoding_and_beamforming_t *pb = &rel15->precodingAndBeamforming; + const nfapi_nr_tx_precoding_and_beamforming_t *pb = &rel15->precodingAndBeamforming; while (rb < rel15->rbSize) { // get pmi info const int pmi = (pb->prg_size > 0) ? (pb->prgs_list[(int)rb / pb->prg_size].pm_idx) : 0; @@ -556,7 +556,7 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC time_stats_t *dlsch_scrambling_stats = &gNB->dlsch_scrambling_stats; time_stats_t *dlsch_modulation_stats = &gNB->dlsch_modulation_stats; NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; const int layerSz = frame_parms->N_RB_DL * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB; const int symbol_sz=frame_parms->ofdm_symbol_size; const int dmrs_Type = rel15->dmrsConfigType; @@ -680,7 +680,7 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC // pmi = prgs_list[rbidx/prg_size].pm_idx, rbidx =0,...,rbSize-1 // The Precoding matrix: // The Codebook Type I - nfapi_nr_tx_precoding_and_beamforming_t *pb = &rel15->precodingAndBeamforming; + const nfapi_nr_tx_precoding_and_beamforming_t *pb = &rel15->precodingAndBeamforming; // beam number in multi-beam scenario (concurrent beams) int bitmap = SL_to_bitmap(rel15->StartSymbolIndex, rel15->NrOfSymbols); int beam_nb = beam_index_allocation(gNB->enable_analog_das, @@ -795,7 +795,7 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot) for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { NR_gNB_DLSCH_t *dlsch = msgTx->dlsch[dlsch_id]; NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; LOG_D(PHY, "pdsch: BWPStart %d, BWPSize %d, rbStart %d, rbsize %d\n", diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index 56a849a4af..4cbfdde2e2 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -141,7 +141,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; unsigned int crc = 1; - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; uint32_t A = rel15->TBSize[0] << 3; unsigned char *a = harq->pdu; if (rel15->rnti != SI_RNTI) { @@ -231,7 +231,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { NR_gNB_DLSCH_t *dlsch = msgTx->dlsch[dlsch_id]; NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[dlsch_id]; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 09415dd86f..e515f15b02 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -58,7 +58,7 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, int beam_index_allocation(bool das, int fapi_beam_index, - nfapi_nr_analog_beamforming_ve_t *analog_bf, + const nfapi_nr_analog_beamforming_ve_t *analog_bf, NR_gNB_COMMON *common_vars, int slot, int symbols_per_slot, diff --git a/openair1/SCHED_NR/sched_nr.h b/openair1/SCHED_NR/sched_nr.h index cbec9fa1db..aade6e766c 100644 --- a/openair1/SCHED_NR/sched_nr.h +++ b/openair1/SCHED_NR/sched_nr.h @@ -49,7 +49,7 @@ void nr_phy_free_RU(RU_t *ru); void clear_slot_beamid(PHY_VARS_gNB *gNB, int slot); int beam_index_allocation(bool das, int fapi_beam_index, - nfapi_nr_analog_beamforming_ve_t *analog_bf, + const nfapi_nr_analog_beamforming_ve_t *analog_bf, NR_gNB_COMMON *common_vars, int slot, int symbols_per_slot, From 34aa69828adddbb0f18434fdb60ce647f7874a82 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Nov 2025 16:59:07 +0100 Subject: [PATCH 198/275] Harmonize UL PDCCH PDU type for further refactoring Both (DL) PDCCH and UL PDCCH PDU types are fundamentally of the same type, so correct the array's type to facilitate refactoring later (a later commit will reuse a single "generate DCI" function for both cases). --- openair1/PHY/NR_TRANSPORT/nr_dci.c | 2 +- openair1/PHY/defs_gNB.h | 3 ++- openair1/SCHED_NR/fapi_nr_l1.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.c b/openair1/PHY/NR_TRANSPORT/nr_dci.c index 0566eb428c..d43bc04752 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dci.c @@ -244,7 +244,7 @@ void nr_generate_dci_top(processingData_L1tx_t *msgTx, int slot, int txdataF_off NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; start_meas(&gNB->dci_generation_stats); for (int i = 0; i < msgTx->num_ul_pdcch; i++) - nr_generate_dci(msgTx->gNB, &msgTx->ul_pdcch_pdu[i].pdcch_pdu.pdcch_pdu_rel15, txdataF_offset, frame_parms, slot); + nr_generate_dci(msgTx->gNB, &msgTx->ul_pdcch_pdu[i].pdcch_pdu_rel15, txdataF_offset, frame_parms, slot); for (int i = 0; i < msgTx->num_dl_pdcch; i++) nr_generate_dci(msgTx->gNB, &msgTx->pdcch_pdu[i].pdcch_pdu_rel15, txdataF_offset, frame_parms, slot); stop_meas(&gNB->dci_generation_stats); diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index ef12f20ce5..ed10ff70cd 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -600,7 +600,8 @@ typedef struct processingData_L1tx { openair0_timestamp timestamp_tx; PHY_VARS_gNB *gNB; nfapi_nr_dl_tti_pdcch_pdu pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; - nfapi_nr_ul_dci_request_pdus_t ul_pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; + /// corresponds to UL_dci_req->ul_dci_pdu_list + nfapi_nr_dl_tti_pdcch_pdu ul_pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; NR_gNB_CSIRS_t csirs_pdu[NR_SYMBOLS_PER_SLOT]; NR_gNB_DLSCH_t **dlsch; NR_gNB_SSB_t ssb[64]; diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 7b27b51c02..01955a0c18 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -174,7 +174,7 @@ void nr_schedule_ul_dci_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_dci_request_t *UL_dci msgTx->num_ul_pdcch = UL_dci_req->numPdus; for (int i = 0; i < UL_dci_req->numPdus; i++) - msgTx->ul_pdcch_pdu[i] = UL_dci_req->ul_dci_pdu_list[i]; + msgTx->ul_pdcch_pdu[i] = UL_dci_req->ul_dci_pdu_list[i].pdcch_pdu; } void nr_schedule_response(NR_Sched_Rsp_t *Sched_INFO) From be9e7987490c0c77e11f082406bd99cfce9a68ed Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Mon, 17 Feb 2025 18:44:53 +0000 Subject: [PATCH 199/275] E1AP: Add PDU Session Resource To Remove List to Bearer Context Modification enc/dec According to TS 38.463 - clause 9.2.2.4 and 9.3.3.12. Also, fix response message by encoding choice IE only with presence of modified PDU sessions. Co-authored-by: rmagueta --- openair2/COMMON/e1ap_messages_types.h | 11 + .../E1AP/lib/e1ap_bearer_context_management.c | 277 ++++++++++-------- openair2/E1AP/lib/e1ap_lib_includes.h | 1 + openair2/E1AP/tests/e1ap_lib_test.c | 10 + 4 files changed, 180 insertions(+), 119 deletions(-) diff --git a/openair2/COMMON/e1ap_messages_types.h b/openair2/COMMON/e1ap_messages_types.h index 788236ca05..6906d54704 100644 --- a/openair2/COMMON/e1ap_messages_types.h +++ b/openair2/COMMON/e1ap_messages_types.h @@ -475,6 +475,14 @@ typedef struct pdu_session_to_mod_s { DRB_nGRAN_to_mod_t DRBnGRanModList[E1AP_MAX_NUM_DRBS]; } pdu_session_to_mod_t; +/** PDU Session Resource To Remove List (3GPP TS 38.463 clause 9.3.3.12) */ +typedef struct { + // PDU Session ID (M) + long sessionId; + // Cause (O) + e1ap_cause_t cause; +} pdu_session_to_remove_t; + /** * Bearer Context Setup Request message, clause 9.2.2.1 of 3GPP TS 38.463 */ @@ -534,6 +542,9 @@ typedef struct e1ap_bearer_mod_req_s { // NG-RAN PDU Session Resource To Modify List (O) int numPDUSessionsMod; pdu_session_to_mod_t pduSessionMod[E1AP_MAX_NUM_PDU_SESSIONS]; + // NG-RAN PDU Session Resource To Remove List (O) + int numPDUSessionsRem; + pdu_session_to_remove_t pduSessionRem[E1AP_MAX_NUM_PDU_SESSIONS]; } e1ap_bearer_mod_req_t; typedef struct e1ap_bearer_release_cmd_s { diff --git a/openair2/E1AP/lib/e1ap_bearer_context_management.c b/openair2/E1AP/lib/e1ap_bearer_context_management.c index 109b0008d3..f2593f103c 100644 --- a/openair2/E1AP/lib/e1ap_bearer_context_management.c +++ b/openair2/E1AP/lib/e1ap_bearer_context_management.c @@ -1631,6 +1631,20 @@ E1AP_E1AP_PDU_t *encode_E1_bearer_context_mod_request(const e1ap_bearer_mod_req_ *ieC3_1 = e1_encode_pdu_session_to_mod_item(i); } } + + // NG-RAN PDU Session Resource To Remove List (O) + if (in->numPDUSessionsRem) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, remList); + E1AP_NG_RAN_BearerContextModificationRequest_t *msgNGRAN = encode_ng_ran_to_mod(remList); + msgNGRAN->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Remove_List; + msgNGRAN->criticality = E1AP_Criticality_reject; + msgNGRAN->value.present = E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Remove_List; + E1AP_PDU_Session_Resource_To_Remove_List_t *pdu2Remove = &msgNGRAN->value.choice.PDU_Session_Resource_To_Remove_List; + for (const pdu_session_to_remove_t *i = in->pduSessionRem; i < in->pduSessionRem + in->numPDUSessionsRem; i++) { + asn1cSequenceAdd(pdu2Remove->list, E1AP_PDU_Session_Resource_To_Remove_Item_t, ieC4_1); + ieC4_1->pDU_Session_ID = i->sessionId; + } + } return pdu; } @@ -1854,6 +1868,19 @@ bool decode_E1_bearer_context_mod_request(const E1AP_E1AP_PDU_t *pdu, e1ap_beare CHECK_E1AP_DEC(e1_decode_pdu_session_to_mod_item(out->pduSessionMod + i, modList->list.array[i])); } break; + case E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Remove_List: + /* PDU Session Resource To Remove List (see 9.3.3.12 of TS 38.463) */ + _E1_EQ_CHECK_INT(msgNGRAN->value.present, + E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Remove_List); + E1AP_PDU_Session_Resource_To_Remove_List_t *remList = &msgNGRAN->value.choice.PDU_Session_Resource_To_Remove_List; + out->numPDUSessionsRem = remList->list.count; + // Loop through all PDU sessions to remove + for (int i = 0; i < remList->list.count; i++) { + E1AP_PDU_Session_Resource_To_Remove_Item_t *remItem = remList->list.array[i]; + pdu_session_to_remove_t *pduRem = &out->pduSessionRem[i]; + pduRem->sessionId = remItem->pDU_Session_ID; + } + break; default: PRINT_ERROR("Unknown msgNGRAN->id in E1 Setup Modification Request\n"); return false; @@ -1924,6 +1951,9 @@ e1ap_bearer_mod_req_t cp_bearer_context_mod_request(const e1ap_bearer_mod_req_t cp.pduSession[i] = cp_pdu_session_item(&msg->pduSession[i]); for (int i = 0; i < msg->numPDUSessionsMod; i++) cp.pduSessionMod[i] = cp_pdu_session_to_mod_item(&msg->pduSessionMod[i]); + // Copy PDU sessions to remove (simple struct copy, no deep copy needed) + for (int i = 0; i < msg->numPDUSessionsRem; i++) + cp.pduSessionRem[i] = msg->pduSessionRem[i]; return cp; } @@ -2037,6 +2067,15 @@ bool eq_bearer_context_mod_request(const e1ap_bearer_mod_req_t *a, const e1ap_be if (!eq_pdu_session_to_mod_item(&a->pduSessionMod[i], &b->pduSessionMod[i])) return false; } + // PDU Sessions to Remove + _E1_EQ_CHECK_INT(a->numPDUSessionsRem, b->numPDUSessionsRem); + for (int i = 0; i < a->numPDUSessionsRem; i++) { + const pdu_session_to_remove_t *pduRemA = &a->pduSessionRem[i]; + const pdu_session_to_remove_t *pduRemB = &b->pduSessionRem[i]; + _E1_EQ_CHECK_LONG(pduRemA->sessionId, pduRemB->sessionId); + _E1_EQ_CHECK_INT(pduRemA->cause.type, pduRemB->cause.type); + _E1_EQ_CHECK_INT(pduRemA->cause.value, pduRemB->cause.value); + } return true; } @@ -2114,129 +2153,129 @@ struct E1AP_E1AP_PDU *encode_E1_bearer_context_mod_response(const e1ap_bearer_mo ie2->criticality = E1AP_Criticality_reject; ie2->value.present = E1AP_BearerContextModificationResponseIEs__value_PR_GNB_CU_UP_UE_E1AP_ID; ie2->value.choice.GNB_CU_UP_UE_E1AP_ID = msg->gNB_cu_up_ue_id; - // NG-RAN PDU Session Resource Modified List (O) - asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationResponseIEs_t, ie3); - ie3->id = E1AP_ProtocolIE_ID_id_System_BearerContextModificationResponse; - ie3->criticality = E1AP_Criticality_ignore; - // CHOICE System (O) - ie3->value.present = E1AP_BearerContextModificationResponseIEs__value_PR_System_BearerContextModificationResponse; - E1AP_System_BearerContextModificationResponse_t *sys = &ie3->value.choice.System_BearerContextModificationResponse; - // NG-RAN - E1AP_ProtocolIE_Container_4932P29_t *ngran = calloc_or_fail(1, sizeof(*ngran)); - sys->present = E1AP_System_BearerContextModificationResponse_PR_nG_RAN_BearerContextModificationResponse; - sys->choice.nG_RAN_BearerContextModificationResponse = (struct E1AP_ProtocolIE_Container *)ngran; - asn1cSequenceAdd(ngran->list, E1AP_NG_RAN_BearerContextModificationResponse_t, list); - // PDU Session Resource Modified List (O) - list->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_Modified_List; - list->criticality = E1AP_Criticality_reject; - list->value.present = E1AP_NG_RAN_BearerContextModificationResponse__value_PR_PDU_Session_Resource_Modified_List; - E1AP_PDU_Session_Resource_Modified_List_t *pdu_mod_l = &list->value.choice.PDU_Session_Resource_Modified_List; - for (int i = 0; i < msg->numPDUSessionsMod; ++i) { - // PDU Session Resource Modified Item (1..maxnoofPDUSessionResource) - const pdu_session_modif_t *pdu = &msg->pduSessionMod[i]; - asn1cSequenceAdd(pdu_mod_l->list, E1AP_PDU_Session_Resource_Modified_Item_t, iePdu); - // PDU Session ID (M) - iePdu->pDU_Session_ID = pdu->id; - // Security Result (O) - if (pdu->confidentialityProtectionIndication || pdu->integrityProtectionIndication) { - iePdu->securityResult = calloc_or_fail(1, sizeof(*iePdu->securityResult)); - if (pdu->confidentialityProtectionIndication) - iePdu->securityResult->confidentialityProtectionResult = *pdu->confidentialityProtectionIndication; - if (pdu->integrityProtectionIndication) - iePdu->securityResult->integrityProtectionResult = *pdu->integrityProtectionIndication; - } - // NG DL UP Transport Layer Information (O) - if (pdu->ng_DL_UP_TL_info) { - iePdu->nG_DL_UP_TNL_Information = calloc_or_fail(1, sizeof(*iePdu->nG_DL_UP_TNL_Information)); - *iePdu->nG_DL_UP_TNL_Information = e1_encode_up_tnl_info(pdu->ng_DL_UP_TL_info); - } - // DRB Modified List (O) - if (pdu->numDRBModified > 0) { - iePdu->dRB_Modified_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Modified_List_NG_RAN)); - E1AP_DRB_Modified_List_NG_RAN_t *drb_mod_l = iePdu->dRB_Modified_List_NG_RAN; - for (int j = 0; j < pdu->numDRBModified; ++j) { - const DRB_nGRAN_modified_t *drb = &pdu->DRBnGRanModList[j]; - asn1cSequenceAdd(drb_mod_l->list, E1AP_DRB_Modified_Item_NG_RAN_t, drb_mod); - // DRB ID (M) - drb_mod->dRB_ID = drb->id; - // Flow Setup List (O) - if (drb->numQosFlowSetup) - drb_mod->flow_Setup_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Setup_List)); - for (int q = 0; q < drb->numQosFlowSetup; ++q) { - asn1cSequenceAdd(drb_mod->flow_Setup_List->list, E1AP_QoS_Flow_Item_t, qos_mod); - qos_mod->qoS_Flow_Identifier = drb->qosFlows[q].qfi; - } - // Flow Failed List (O) - if (drb->numQosFlowFailed) - drb_mod->flow_Failed_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Failed_List)); - for (int q = 0; q < drb->numQosFlowFailed; ++q) { - asn1cSequenceAdd(drb_mod->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail); - encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]); - } - - // PDCP Status Information (O) - if (drb->pdcp_status) { - asn1cCalloc(drb_mod->pDCP_SN_Status_Information, pdcp); - *pdcp = encode_pdcp_status_info(drb->pdcp_status); - } - - // UL UP Parameters (O) - if (drb->numUpParam) - drb_mod->uL_UP_Transport_Parameters = calloc_or_fail(1, sizeof(*drb_mod->uL_UP_Transport_Parameters)); - for (const up_params_t *k = drb->ul_UP_Params; k < drb->ul_UP_Params + drb->numUpParam; k++) { - asn1cSequenceAdd(drb_mod->uL_UP_Transport_Parameters->list, E1AP_UP_Parameters_Item_t, up); - up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info); - up->cell_Group_ID = k->cell_group_id; + if (msg->numPDUSessionsMod > 0) { + // NG-RAN PDU Session Resource Modified List (O) + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationResponseIEs_t, ie3); + ie3->id = E1AP_ProtocolIE_ID_id_System_BearerContextModificationResponse; + ie3->criticality = E1AP_Criticality_ignore; + // CHOICE System (O) + ie3->value.present = E1AP_BearerContextModificationResponseIEs__value_PR_System_BearerContextModificationResponse; + E1AP_System_BearerContextModificationResponse_t *sys = &ie3->value.choice.System_BearerContextModificationResponse; + // NG-RAN + E1AP_ProtocolIE_Container_4932P29_t *ngran = calloc_or_fail(1, sizeof(*ngran)); + sys->present = E1AP_System_BearerContextModificationResponse_PR_nG_RAN_BearerContextModificationResponse; + sys->choice.nG_RAN_BearerContextModificationResponse = (struct E1AP_ProtocolIE_Container *)ngran; + asn1cSequenceAdd(ngran->list, E1AP_NG_RAN_BearerContextModificationResponse_t, list); + // PDU Session Resource Modified List (O) + list->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_Modified_List; + list->criticality = E1AP_Criticality_reject; + list->value.present = E1AP_NG_RAN_BearerContextModificationResponse__value_PR_PDU_Session_Resource_Modified_List; + E1AP_PDU_Session_Resource_Modified_List_t *pdu_mod_l = &list->value.choice.PDU_Session_Resource_Modified_List; + for (int i = 0; i < msg->numPDUSessionsMod; ++i) { + // PDU Session Resource Modified Item (1..maxnoofPDUSessionResource) + const pdu_session_modif_t *pdu = &msg->pduSessionMod[i]; + asn1cSequenceAdd(pdu_mod_l->list, E1AP_PDU_Session_Resource_Modified_Item_t, iePdu); + // PDU Session ID (M) + iePdu->pDU_Session_ID = pdu->id; + // Security Result (O) + if (pdu->confidentialityProtectionIndication || pdu->integrityProtectionIndication) { + iePdu->securityResult = calloc_or_fail(1, sizeof(*iePdu->securityResult)); + if (pdu->confidentialityProtectionIndication) + iePdu->securityResult->confidentialityProtectionResult = *pdu->confidentialityProtectionIndication; + if (pdu->integrityProtectionIndication) + iePdu->securityResult->integrityProtectionResult = *pdu->integrityProtectionIndication; + } + // NG DL UP Transport Layer Information (O) + if (pdu->ng_DL_UP_TL_info) { + iePdu->nG_DL_UP_TNL_Information = calloc_or_fail(1, sizeof(*iePdu->nG_DL_UP_TNL_Information)); + *iePdu->nG_DL_UP_TNL_Information = e1_encode_up_tnl_info(pdu->ng_DL_UP_TL_info); + } + // DRB Modified List (O) + if (pdu->numDRBModified > 0) { + iePdu->dRB_Modified_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Modified_List_NG_RAN)); + E1AP_DRB_Modified_List_NG_RAN_t *drb_mod_l = iePdu->dRB_Modified_List_NG_RAN; + for (int j = 0; j < pdu->numDRBModified; ++j) { + const DRB_nGRAN_modified_t *drb = &pdu->DRBnGRanModList[j]; + asn1cSequenceAdd(drb_mod_l->list, E1AP_DRB_Modified_Item_NG_RAN_t, drb_mod); + // DRB ID (M) + drb_mod->dRB_ID = drb->id; + // Flow Setup List (O) + if (drb->numQosFlowSetup) + drb_mod->flow_Setup_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Setup_List)); + for (int q = 0; q < drb->numQosFlowSetup; ++q) { + asn1cSequenceAdd(drb_mod->flow_Setup_List->list, E1AP_QoS_Flow_Item_t, qos_mod); + qos_mod->qoS_Flow_Identifier = drb->qosFlows[q].qfi; + } + // Flow Failed List (O) + if (drb->numQosFlowFailed) + drb_mod->flow_Failed_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Failed_List)); + for (int q = 0; q < drb->numQosFlowFailed; ++q) { + asn1cSequenceAdd(drb_mod->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail); + encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]); + } + // PDCP Status Information (O) + if (drb->pdcp_status) { + asn1cCalloc(drb_mod->pDCP_SN_Status_Information, pdcp); + *pdcp = encode_pdcp_status_info(drb->pdcp_status); + } + // UL UP Parameters (O) + if (drb->numUpParam) + drb_mod->uL_UP_Transport_Parameters = calloc_or_fail(1, sizeof(*drb_mod->uL_UP_Transport_Parameters)); + for (const up_params_t *k = drb->ul_UP_Params; k < drb->ul_UP_Params + drb->numUpParam; k++) { + asn1cSequenceAdd(drb_mod->uL_UP_Transport_Parameters->list, E1AP_UP_Parameters_Item_t, up); + up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info); + up->cell_Group_ID = k->cell_group_id; + } } } - } - // DRB Failed To Modify List (O) - if (pdu->numDRBFailedToMod > 0) { - iePdu->dRB_Failed_To_Modify_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_To_Modify_List_NG_RAN)); - E1AP_DRB_Failed_To_Modify_List_NG_RAN_t *ngran = iePdu->dRB_Failed_To_Modify_List_NG_RAN; - for (int j = 0; j < pdu->numDRBFailedToMod; ++j) { - const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedModList[j]; - asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_To_Modify_Item_NG_RAN_t, fail_to_mod); - encode_drb_failed_item((E1AP_DRB_Failed_Item_NG_RAN_t *)fail_to_mod, drb); - } - } - // DRB Setup List (O) - if (pdu->numDRBSetup > 0) { - iePdu->dRB_Setup_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Setup_List_NG_RAN)); - E1AP_DRB_Setup_List_NG_RAN_t *drb_setup = iePdu->dRB_Setup_List_NG_RAN; - for (int j = 0; j < pdu->numDRBModified; ++j) { - const DRB_nGRAN_setup_t *drb = &pdu->DRBnGRanSetupList[j]; - asn1cSequenceAdd(drb_setup->list, E1AP_DRB_Setup_Item_NG_RAN_t, item); - // DRB ID (M) - item->dRB_ID = drb->id; - // UL UP Parameters (M) - for (const up_params_t *k = drb->UpParamList; k < drb->UpParamList + drb->numUpParam; k++) { - asn1cSequenceAdd(item->uL_UP_Transport_Parameters.list, E1AP_UP_Parameters_Item_t, up); - up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info); - up->cell_Group_ID = drb->UpParamList->cell_group_id; - } - // Flow Setup List (M) - for (int q = 0; q < drb->numQosFlowSetup; ++q) { - asn1cSequenceAdd(item->flow_Setup_List.list, E1AP_QoS_Flow_Item_t, qos); - qos->qoS_Flow_Identifier = drb->qosFlows[q].qfi; - } - // Flow Failed List (O) - if (drb->numQosFlowFailed) - item->flow_Failed_List = calloc_or_fail(1, sizeof(*item->flow_Failed_List)); - for (int q = 0; q < drb->numQosFlowFailed; ++q) { - asn1cSequenceAdd(item->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail); - encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]); + // DRB Failed To Modify List (O) + if (pdu->numDRBFailedToMod > 0) { + iePdu->dRB_Failed_To_Modify_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_To_Modify_List_NG_RAN)); + E1AP_DRB_Failed_To_Modify_List_NG_RAN_t *ngran = iePdu->dRB_Failed_To_Modify_List_NG_RAN; + for (int j = 0; j < pdu->numDRBFailedToMod; ++j) { + const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedModList[j]; + asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_To_Modify_Item_NG_RAN_t, fail_to_mod); + encode_drb_failed_item((E1AP_DRB_Failed_Item_NG_RAN_t *)fail_to_mod, drb); } } - } - // DRB Failed List (O) - if (pdu->numDRBFailed) { - iePdu->dRB_Failed_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_List_NG_RAN)); - E1AP_DRB_Failed_List_NG_RAN_t *ngran = iePdu->dRB_Failed_List_NG_RAN; - for (int j = 0; j < pdu->numDRBFailed; ++j) { - const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedList[j]; - asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_Item_NG_RAN_t, fail); - encode_drb_failed_item(fail, drb); + // DRB Setup List (O) + if (pdu->numDRBSetup > 0) { + iePdu->dRB_Setup_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Setup_List_NG_RAN)); + E1AP_DRB_Setup_List_NG_RAN_t *drb_setup = iePdu->dRB_Setup_List_NG_RAN; + for (int j = 0; j < pdu->numDRBModified; ++j) { + const DRB_nGRAN_setup_t *drb = &pdu->DRBnGRanSetupList[j]; + asn1cSequenceAdd(drb_setup->list, E1AP_DRB_Setup_Item_NG_RAN_t, item); + // DRB ID (M) + item->dRB_ID = drb->id; + // UL UP Parameters (M) + for (const up_params_t *k = drb->UpParamList; k < drb->UpParamList + drb->numUpParam; k++) { + asn1cSequenceAdd(item->uL_UP_Transport_Parameters.list, E1AP_UP_Parameters_Item_t, up); + up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info); + up->cell_Group_ID = drb->UpParamList->cell_group_id; + } + // Flow Setup List (M) + for (int q = 0; q < drb->numQosFlowSetup; ++q) { + asn1cSequenceAdd(item->flow_Setup_List.list, E1AP_QoS_Flow_Item_t, qos); + qos->qoS_Flow_Identifier = drb->qosFlows[q].qfi; + } + // Flow Failed List (O) + if (drb->numQosFlowFailed) + item->flow_Failed_List = calloc_or_fail(1, sizeof(*item->flow_Failed_List)); + for (int q = 0; q < drb->numQosFlowFailed; ++q) { + asn1cSequenceAdd(item->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail); + encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]); + } + } + } + // DRB Failed List (O) + if (pdu->numDRBFailed) { + iePdu->dRB_Failed_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_List_NG_RAN)); + E1AP_DRB_Failed_List_NG_RAN_t *ngran = iePdu->dRB_Failed_List_NG_RAN; + for (int j = 0; j < pdu->numDRBFailed; ++j) { + const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedList[j]; + asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_Item_NG_RAN_t, fail); + encode_drb_failed_item(fail, drb); + } } } } diff --git a/openair2/E1AP/lib/e1ap_lib_includes.h b/openair2/E1AP/lib/e1ap_lib_includes.h index 028993e6fd..adafbe0ae5 100644 --- a/openair2/E1AP/lib/e1ap_lib_includes.h +++ b/openair2/E1AP/lib/e1ap_lib_includes.h @@ -78,6 +78,7 @@ #include "E1AP_DRB-To-Modify-Item-NG-RAN.h" #include "E1AP_PDU-Session-Resource-To-Setup-Mod-Item.h" #include "E1AP_DRB-To-Setup-Mod-Item-NG-RAN.h" +#include "E1AP_PDU-Session-Resource-To-Remove-Item.h" // E1 Bearer Context Modification Response #include "E1AP_PDU-Session-Resource-Modified-Item.h" #include "E1AP_DRB-Modified-List-NG-RAN.h" diff --git a/openair2/E1AP/tests/e1ap_lib_test.c b/openair2/E1AP/tests/e1ap_lib_test.c index b168ff9e82..50116514fb 100644 --- a/openair2/E1AP/tests/e1ap_lib_test.c +++ b/openair2/E1AP/tests/e1ap_lib_test.c @@ -538,6 +538,14 @@ static void test_bearer_context_modification_request(void) .DRBnGRanList[0] = drb_to_setup, }; + // PDU Session to Remove + pdu_session_to_remove_t pdu_session_to_remove = { + .sessionId = 2, + // Note: cause is optional extension IE, currently encoding/decoding only handles sessionId + .cause.type = 0, + .cause.value = 0, + }; + // Initialize the Bearer Context Modification Request e1ap_bearer_mod_req_t orig = { .gNB_cu_cp_ue_id = 0x1234, @@ -548,6 +556,8 @@ static void test_bearer_context_modification_request(void) .pduSession[0] = pdusession_setup_item, .numPDUSessionsMod = 1, .pduSessionMod[0] = pdusession_mod_item, + .numPDUSessionsRem = 1, + .pduSessionRem[0] = pdu_session_to_remove, }; *orig.bearerContextStatus = BEARER_SUSPEND; *orig.inactivityTimer = 1000; From 1f313db0513d96d1e85fc4eadb13105540adde74 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 13 Aug 2025 14:43:49 +0200 Subject: [PATCH 200/275] RRC: add only DRBs to add/mod in createDRBlist Do not add PDU Session with status higher than PDU_SESSION_STATUS_TOMODIFY which means, do not add "failed" or "to release" PDU Sessions. --- openair2/RRC/NR/rrc_gNB.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index dcc17ec2c0..bc4cca38f1 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -366,6 +366,10 @@ NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do LOG_D(NR_RRC, "PDU Session %d not found, skip\n", drb->pdusession_id); continue; } + if (pduSession->status > PDU_SESSION_STATUS_TOMODIFY) { + LOG_D(NR_RRC, "PDU Session %d is not to add/mod\n", drb->pdusession_id); + continue; + } pdusession_t *session = &pduSession->param; NR_DRB_ToAddMod_t *drb_ToAddMod = calloc_or_fail(1, sizeof(*drb_ToAddMod)); drb_ToAddMod->drb_Identity = drb->drb_id; From de744ddf35d3a87c90981693ccc5211d2fdb736d Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Thu, 13 Nov 2025 20:46:06 +0000 Subject: [PATCH 201/275] Enable 2 layers and increase tested UL throughput in Aerial UL heavy test --- .../gnb-vnf.sa.band78.273prb.aerial.ul-heavy.conf | 6 +++--- .../xml_files/container_sa_aerial_quectel_ul_heavy.xml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.ul-heavy.conf b/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.ul-heavy.conf index 90662226ed..43207d63ec 100644 --- a/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.ul-heavy.conf +++ b/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.ul-heavy.conf @@ -24,9 +24,9 @@ gNBs = pdsch_AntennaPorts_N1 = 2; pusch_AntennaPorts = 2; do_CSIRS = 1; - do_SRS = 0; + do_SRS = 1; min_rxtxtime = 2; - + force_UL256qam_off = 1; servingCellConfigCommon = ( { #spCellConfigCommon @@ -187,7 +187,7 @@ MACRLCs = ( remote_s_portd = 50010; // pnf p7 port [!] tr_s_preference = "aerial"; tr_n_preference = "local_RRC"; - pusch_TargetSNRx10 = 220; # 150; + pusch_TargetSNRx10 = 260; # 150; pucch_TargetSNRx10 = 200; #200; dl_max_mcs = 28; ul_max_mcs = 28; diff --git a/ci-scripts/xml_files/container_sa_aerial_quectel_ul_heavy.xml b/ci-scripts/xml_files/container_sa_aerial_quectel_ul_heavy.xml index ab38b72295..baf525f167 100644 --- a/ci-scripts/xml_files/container_sa_aerial_quectel_ul_heavy.xml +++ b/ci-scripts/xml_files/container_sa_aerial_quectel_ul_heavy.xml @@ -97,8 +97,8 @@ Iperf - iperf (UL/UDP/200M)(30 sec)(single-ue profile) - -u -b 200M -t 30 -l 1428 + iperf (UL/UDP/300M)(30 sec)(single-ue profile) + -u -b 300M -t 30 -l 1428 oc-cn5g-20897-aerial up2-aerial 10 @@ -107,9 +107,9 @@ Iperf - iperf (UL/UDP/220M)(30 sec)(single-ue profile) + iperf (UL/UDP/380M)(30 sec)(single-ue profile) true - -u -b 220M -t 30 -l 1428 + -u -b 380M -t 30 -l 1428 oc-cn5g-20897-aerial up2-aerial 10 From 3445d3bb12783ce94ceacd88483f41d1d93b86ba Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 3 Sep 2025 15:24:47 +0200 Subject: [PATCH 202/275] OAI UE: add reusable function to release RLC entity --- openair2/RRC/NR_UE/rrc_UE.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 15c713cb26..2e97244202 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -1651,6 +1651,22 @@ static void nr_rrc_signal_maxrtxindication(int ue_id) itti_send_msg_to_task(TASK_RRC_NRUE, ue_id, msg); } +/** @brief Release all active RLC entities for a UE and set to inactive. + * This is typically used when tearing down all DRBs at UE side, + * such as after a PDU session release or full reconfiguration. + * @param rrc Pointer to NR_UE_RRC_INST_t structure + * @param id Logical Channel ID (must be in range [0, NR_MAX_NUM_LCID-1]) */ +static void nr_rrc_release_rlc_entity(NR_UE_RRC_INST_t *rrc, int id) +{ + DevAssert(rrc); + DevAssert(id >= 0 && id < NR_MAX_NUM_LCID); + if (rrc->active_RLC_entity[id]) { + rrc->active_RLC_entity[id] = false; + nr_rlc_release_entity(rrc->ue_id, id); + LOG_I(RLC, "Released RLC entity: ue_id=%ld, lc_id=%d\n", rrc->ue_id, id); + } +} + static void nr_rrc_manage_rlc_bearers(NR_UE_RRC_INST_t *rrc, const NR_CellGroupConfig_t *cellGroupConfig) { if (cellGroupConfig->rlc_BearerToReleaseList != NULL) { @@ -1830,10 +1846,7 @@ static void nr_rrc_rrcsetup_fallback(NR_UE_RRC_INST_t *rrc) } } for (int i = 1; i < NR_MAX_NUM_LCID; i++) { - if (rrc->active_RLC_entity[i]) { - rrc->active_RLC_entity[i] = false; - nr_rlc_release_entity(rrc->ue_id, i); - } + nr_rrc_release_rlc_entity(rrc, i); } nr_sdap_delete_ue_entities(rrc->ue_id); @@ -2938,10 +2951,7 @@ void nr_rrc_going_to_IDLE(NR_UE_RRC_INST_t *rrc, } } for (int i = 0; i < NR_MAX_NUM_LCID; i++) { - if (rrc->active_RLC_entity[i]) { - rrc->active_RLC_entity[i] = false; - nr_rlc_release_entity(rrc->ue_id, i); - } + nr_rrc_release_rlc_entity(rrc, i); } for (int i = 0; i < NB_CNX_UE; i++) { From c5b21d42a4b31b2781b781ff906a08ff65cc3e04 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 3 Sep 2025 15:25:03 +0200 Subject: [PATCH 203/275] OAI UE: set RLC entity status to false when releasing entity in nr_rrc_manage_rlc_bearers The previously introduced function rrc_release_rlc_entity has been adopted to deactvate the RLC entities after release. --- openair2/RRC/NR_UE/rrc_UE.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 2e97244202..289309419b 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -1673,7 +1673,7 @@ static void nr_rrc_manage_rlc_bearers(NR_UE_RRC_INST_t *rrc, const NR_CellGroupC for (int i = 0; i < cellGroupConfig->rlc_BearerToReleaseList->list.count; i++) { NR_LogicalChannelIdentity_t *lcid = cellGroupConfig->rlc_BearerToReleaseList->list.array[i]; AssertFatal(lcid, "LogicalChannelIdentity shouldn't be null here\n"); - nr_rlc_release_entity(rrc->ue_id, *lcid); + nr_rrc_release_rlc_entity(rrc, *lcid); } } From 228ca89f3e122b57d61e57ab9a06a8eb73541bd0 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 2 Oct 2025 12:51:48 +0200 Subject: [PATCH 204/275] Fix GTP-U TEID logging: prevent negative values and use hexadecimal format Problem: - GTP-U logs showed negative TEID values (e.g., -1943516315) due to signed/unsigned conversion. Due to wrong conversion from uint32_t to int and overflow caused negative values. - TEID values were inconsistently formatted across different log statements, due to mixed use of %d, %u, %x formats for TEID logging. Made debugging and troubleshooting difficult Changes: 1. Fixed signed/unsigned conversion bug: use teid_t. Prevents overflow when TEID values exceed int range. 2. Standardized TEID logging format: all TEID values now use '0x%x' hexadecimal format. Consistent 'TEID' capitalization throughout --- openair3/ocp-gtpu/gtp_itf.cpp | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index c524129098..ab14ee6241 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -292,7 +292,7 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, DevAssert(bearer->ip.ss_family == AF_INET); struct sockaddr_in *to = (struct sockaddr_in *)&bearer->ip; LOG_D(GTPU, - "Peer IP:" IPV4_ADDR " port:%u outgoing teid:%x\n", + "Peer IP:" IPV4_ADDR " port:%u outgoing TEID:0x%x\n", IPV4_ADDR_FORMAT(to->sin_addr.s_addr), htons(to->sin_port), bearer->teid_outgoing); @@ -332,7 +332,7 @@ static void _gtpv1uSendDirect(instance_t instance, } LOG_D(GTPU, - "[%ld] sending a packet to UE:RAB:teid %lx/%x/%x, len %lu, oldseq %d, oldnum %d\n", + "[%ld] sending a packet to UE:RAB:TEID %lx/%d/0x%x, len %lu, oldseq %d, oldnum %d\n", instance, ue_id, bearer_id, @@ -492,7 +492,7 @@ static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *re } LOG_D(GTPU, - "[%ld] sending a end packet packet to UE:RAB:teid %lx/%x/%x\n", + "[%ld] sending a end packet packet to UE:RAB:TEID %lx/%d/0x%x\n", instance, ue_id, bearer_id, @@ -641,7 +641,7 @@ void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance, struct sockaddr_in *sa4 = (struct sockaddr_in *)sockaddr; struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sockaddr; LOG_I(GTPU, - "[%ld] UE ID %ld: Update tunnel TEID incoming %x outgoing %x to remote IPv4 %s, IPv6 %s, port %d\n", + "[%ld] UE ID %ld: Update tunnel TEID incoming 0x%x outgoing 0x%x to remote IPv4 %s, IPv6 %s, port %d\n", instance, ue_id, ptr2->second.teid_incoming, @@ -676,7 +676,7 @@ teid_t newGtpuCreateTunnel(instance_t instance, teid_t incoming_teid = gtpv1uNewTeid(); while (globGtp.te2ue_mapping.find(incoming_teid) != globGtp.te2ue_mapping.end()) { - LOG_W(GTPU, "[%ld] generated a random Teid that exists, re-generating (%x)\n", instance, incoming_teid); + LOG_W(GTPU, "[%ld] generated a random TEID that exists, re-generating (0x%x)\n", instance, incoming_teid); incoming_teid = gtpv1uNewTeid(); }; @@ -721,7 +721,7 @@ teid_t newGtpuCreateTunnel(instance_t instance, char ip4[INET_ADDRSTRLEN]; char ip6[INET6_ADDRSTRLEN]; LOG_I(GTPU, - "[%ld] UE ID %ld: Create tunnel TEID incoming %x outgoing %x to remote IPv4 %s, IPv6 %s, port %d\n", + "[%ld] UE ID %ld: Create tunnel TEID incoming 0x%x outgoing 0x%x to remote IPv4 %s, IPv6 %s, port %d\n", instance, ue_id, bearer.teid_incoming, @@ -825,7 +825,7 @@ int gtpv1u_create_ngu_tunnel(const instance_t instance, gtpCallbackSDAP callBackSDAP) { LOG_D(GTPU, - "[%ld] Start create tunnels for ue id %lu, num_tunnels %d, sgw_S1u_teid %x\n", + "[%ld] Start create tunnels for ue id %lu, num_tunnels %d, TEID 0x%x\n", instance, create_tunnel_req->ue_id, create_tunnel_req->num_tunnels, @@ -909,7 +909,7 @@ int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id) pthread_mutex_unlock(&globGtp.gtp_lock); return !GTPNOK; } - int teid = rb_it->second.teid_incoming; + teid_t teid = rb_it->second.teid_incoming; globGtp.te2ue_mapping.erase(teid); ue_it->second.bearers.erase(rb_id); pthread_mutex_unlock(&globGtp.gtp_lock); @@ -1043,7 +1043,7 @@ static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, const } uint16_t seq = ntohs(*(uint16_t *)(msgHdr + 1)); - LOG_D(GTPU, "[%d] Received a echo request, TEID: %d, seq: %hu\n", h, msgHdr->teid, seq); + LOG_D(GTPU, "[%d] Received a echo request, TEID: 0x%x, seq: %hu\n", h, msgHdr->teid, seq); uint8_t recovery[2] = {14, 0}; gtpv1u_bearer_t bearer = create_bearer(h, addr, ntohl(msgHdr->teid), seq); return gtpv1uCreateAndSendMsg(&bearer, @@ -1061,10 +1061,11 @@ static int Gtpv1uHandleError(int h, uint8_t *msgBuf, uint32_t msgBufLen, const s if (msgBufLen < sizeof(Gtpv1uError)) LOG_E(GTPU, "Received GTP error indication with truncated size %u (mini size: %lu)\n", msgBufLen,sizeof(Gtpv1uError)+4); Gtpv1uError *msg = ( Gtpv1uError *)msgBuf; - LOG_E(GTPU, "Received GTP error indication: \n" - " TEID %u (must be 0 from TS 29.281)\n" - " TV id for TEID %u (must be 16)\n" - " TEID in error %u (should be a TEID we sent)\n" + LOG_E(GTPU, + "Received GTP error indication: \n" + " TEID 0x%x (must be 0 from TS 29.281)\n" + " TV id for TEID 0x%x (must be 16)\n" + " TEID in error 0x%x (should be a TEID we sent)\n" " TV id for GTP addr %u (should be 133)\n" " len for addr of UPF %u (should be IPv4 or IPv6 len)" " (TS 29.281 Sec 7.3.1 Error Handling not implemented)\n", @@ -1103,7 +1104,7 @@ static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, con auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid)); if (tunnel == globGtp.te2ue_mapping.end()) { - LOG_E(GTPU, "[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, msgHdr->teid); + LOG_E(GTPU, "[%d] Received a incoming packet on unknown TEID (0x%x) Dropping!\n", h, msgHdr->teid); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } @@ -1132,7 +1133,7 @@ static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, con if (!tunnel->second.callBack(&ctxt, srb_flag, rb_id, mui, confirm, 0, NULL, mode, &sourceL2Id, &destinationL2Id)) LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h); - LOG_D(GTPU, "[%d] Received END marker packet for: teid:%x\n", h, ntohl(msgHdr->teid)); + LOG_D(GTPU, "[%d] Received END marker packet for: TEID:0x%x\n", h, ntohl(msgHdr->teid)); return !GTPNOK; } @@ -1149,7 +1150,7 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid)); if (tunnel == globGtp.te2ue_mapping.end()) { - LOG_E(GTPU, "[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, ntohl(msgHdr->teid)); + LOG_E(GTPU, "[%d] Received a incoming packet on unknown TEID (0x%x) Dropping!\n", h, ntohl(msgHdr->teid)); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } @@ -1282,7 +1283,7 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st 1); } - LOG_D(GTPU, "[%d] Received a %d bytes packet for: teid:%x\n", h, msgBufLen - offset, ntohl(msgHdr->teid)); + LOG_D(GTPU, "[%d] Received a %d bytes packet for: TEID:0x%x\n", h, msgBufLen - offset, ntohl(msgHdr->teid)); return !GTPNOK; } From 19301afcb18c9bfaac6d71edb3b3ef448b66af95 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Fri, 14 Nov 2025 19:21:19 +0800 Subject: [PATCH 205/275] Fix bug for SRS generation when freqDomainShift (n_shift) is not 0 According to TS 38.211 section 6.4.1.4.3, the frequency domain shift value n_shift is from higher-layer parameter freqDomainShift. The quantity n_RRC is given by higher-layer parameter freqDomainPosition. Also corrected k_0_overbar_p calculation formula to align with standard. --- openair1/PHY/nr_phy_common/src/nr_phy_common_srs.c | 2 +- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 4 ++-- 2 files changed, 3 insertions(+), 3 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 ba5d792428..ae22eb25d7 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 @@ -293,7 +293,7 @@ bool generate_srs_nr(NR_DL_FRAME_PARMS *frame_parms, } uint8_t k_l_offset = 0; // If the SRS is configured by the IE SRS-PosResource-r16, the quantity k_l_offset is // given by TS 38.211 - Table 6.4.1.4.3-2, otherwise k_l_offset = 0. - uint8_t k_0_overbar_p = (nr_srs_info->n_shift * NR_NB_SC_PER_RB + (K_TC_p + k_l_offset)) % K_TC; + uint8_t k_0_overbar_p = nr_srs_info->n_shift * NR_NB_SC_PER_RB + (K_TC_p + k_l_offset) % K_TC; uint8_t k_0_p = k_0_overbar_p + K_TC * M_sc_b_SRS * sum_n_b; nr_srs_info->k_0_p[p_index][l_line] = k_0_p; diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 51c7bf2c55..43fef06ef7 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -278,8 +278,8 @@ static void configure_srs_info(fapi_nr_ul_config_srs_pdu *srs_config_pdu, nr_srs nr_srs_info->n_SRS_cs = srs_config_pdu->cyclic_shift; nr_srs_info->n_ID_SRS = srs_config_pdu->sequence_id; // It adjusts the SRS allocation to align with the common resource block grid in multiples of four - nr_srs_info->n_shift = srs_config_pdu->frequency_position; - nr_srs_info->n_RRC = srs_config_pdu->frequency_shift; + nr_srs_info->n_shift = srs_config_pdu->frequency_shift; + nr_srs_info->n_RRC = srs_config_pdu->frequency_position; 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; From bae9e0f1e81ce182a03d34924704125755e6a71f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Nov 2025 17:07:10 +0100 Subject: [PATCH 206/275] Rewrite nr_generate_dci_top() without msgTx and shorter --- doc/SW_archi.md | 2 +- openair1/PHY/NR_TRANSPORT/nr_dci.c | 23 +++++------------------ openair1/PHY/NR_TRANSPORT/nr_dci.h | 6 +++++- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 9 +++++++-- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/doc/SW_archi.md b/doc/SW_archi.md index c47a3cbc91..78004a5a74 100644 --- a/doc/SW_archi.md +++ b/doc/SW_archi.md @@ -177,7 +177,7 @@ it would be better to call **NR_UL_indication()** now instead of before (on prev * nr_common_signal_procedures() generate common signals {: .func4} -* nr_generate_dci_top() +* nr_generate_dci() generate DCI: the scheduling informtion for each UE in both DL and UL {: .func4} * nr_generate_pdsch() diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.c b/openair1/PHY/NR_TRANSPORT/nr_dci.c index d43bc04752..2df2df8eb0 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dci.c @@ -51,11 +51,11 @@ static void nr_pdcch_scrambling(uint32_t *in, uint32_t size, uint32_t Nid, uint3 out[i] = in[i] ^ seq[i]; } -static void nr_generate_dci(PHY_VARS_gNB *gNB, - const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15, - int txdataF_offset, - NR_DL_FRAME_PARMS *frame_parms, - int slot) +void nr_generate_dci(PHY_VARS_gNB *gNB, + const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15, + int txdataF_offset, + NR_DL_FRAME_PARMS *frame_parms, + int slot) { // fill reg list per symbol int reg_list[MAX_DCI_CORESET][NR_MAX_PDCCH_AGG_LEVEL * NR_NB_REG_PER_CCE]; @@ -237,16 +237,3 @@ static void nr_generate_dci(PHY_VARS_gNB *gNB, *(unsigned long long *)dci_pdu->Payload); } // for (int d=0;dnumDlDci;d++) } - -void nr_generate_dci_top(processingData_L1tx_t *msgTx, int slot, int txdataF_offset) -{ - PHY_VARS_gNB *gNB = msgTx->gNB; - NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - start_meas(&gNB->dci_generation_stats); - for (int i = 0; i < msgTx->num_ul_pdcch; i++) - nr_generate_dci(msgTx->gNB, &msgTx->ul_pdcch_pdu[i].pdcch_pdu_rel15, txdataF_offset, frame_parms, slot); - for (int i = 0; i < msgTx->num_dl_pdcch; i++) - nr_generate_dci(msgTx->gNB, &msgTx->pdcch_pdu[i].pdcch_pdu_rel15, txdataF_offset, frame_parms, slot); - stop_meas(&gNB->dci_generation_stats); -} - diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.h b/openair1/PHY/NR_TRANSPORT/nr_dci.h index 2fb0f5aedd..57651f03a2 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci.h +++ b/openair1/PHY/NR_TRANSPORT/nr_dci.h @@ -26,7 +26,11 @@ #include "PHY/NR_REFSIG/nr_refsig.h" #include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface.h" -void nr_generate_dci_top(processingData_L1tx_t *msgTx, int slot, int txdataF_offset); +void nr_generate_dci(PHY_VARS_gNB *gNB, + const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15, + int txdataF_offset, + NR_DL_FRAME_PARMS *frame_parms, + int slot); int16_t find_nr_pdcch(int frame,int slot, PHY_VARS_gNB *gNB,find_type_t type); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index e515f15b02..bf743c60f6 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -255,12 +255,17 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, int num_pdcch_pdus = msgTx->num_ul_pdcch + msgTx->num_dl_pdcch; if (num_pdcch_pdus > 0) { - LOG_D(PHY, "[gNB %d] Frame %d slot %d Calling nr_generate_dci_top (number of UL/DL PDCCH PDUs %d/%d)\n", + LOG_D(PHY, "[gNB %d] Frame %d slot %d Calling nr_generate_dci() (number of UL/DL PDCCH PDUs %d/%d)\n", gNB->Mod_id, frame, slot, msgTx->num_ul_pdcch, msgTx->num_dl_pdcch); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_gNB_PDCCH_TX,1); - nr_generate_dci_top(msgTx, slot, txdataF_offset); + start_meas(&gNB->dci_generation_stats); + for (int i = 0; i < msgTx->num_dl_pdcch; ++i) + nr_generate_dci(gNB, &msgTx->pdcch_pdu[i].pdcch_pdu_rel15, txdataF_offset, &gNB->frame_parms, slot); + for (int i = 0; i < msgTx->num_ul_pdcch; ++i) + nr_generate_dci(gNB, &msgTx->ul_pdcch_pdu[i].pdcch_pdu_rel15, txdataF_offset, &gNB->frame_parms, slot); + stop_meas(&gNB->dci_generation_stats); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_gNB_PDCCH_TX,0); } From d0f4997aa00222c86aabe3febb2ca7caf03ee320 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Nov 2025 17:15:02 +0100 Subject: [PATCH 207/275] Remove msgTx->ssb and replace with normal array There is no point in tracking the "active" state: the SSB PDU will be sent from MAC to L1, encoded, and then "discarded" (by marking the number of SSB PDUs as 0). Hence, remove this intermediate type. --- executables/nr-gnb.c | 1 - openair1/PHY/defs_gNB.h | 8 ++------ openair1/SCHED_NR/fapi_nr_l1.c | 7 +------ openair1/SCHED_NR/phy_procedures_nr_gNB.c | 9 +++------ openair1/SIMULATION/NR_PHY/dlsim.c | 5 ++--- openair1/SIMULATION/NR_PHY/pbchsim.c | 12 ++++++------ 6 files changed, 14 insertions(+), 28 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index b866a83222..c4ad405655 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -362,7 +362,6 @@ void init_gNB_Tpool(int inst) processingData_L1tx_t *msgDataTx = (processingData_L1tx_t *)NotifiedFifoData(msgL1Tx); memset(msgDataTx, 0, sizeof(processingData_L1tx_t)); init_DLSCH_struct(gNB, msgDataTx); - memset(msgDataTx->ssb, 0, 64 * sizeof(NR_gNB_SSB_t)); // this will be removed when the msgDataTx is not necessary anymore gNB->msgDataTx = msgDataTx; diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index ed10ff70cd..01a9bd99f2 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -129,11 +129,6 @@ typedef struct { uint32_t G; } NR_gNB_DLSCH_t; -typedef struct { - bool active; - nfapi_nr_dl_tti_ssb_pdu ssb_pdu; -} NR_gNB_SSB_t; - typedef struct { uint8_t NumPRSResources; prs_config_t prs_cfg[NR_MAX_PRS_RESOURCES_PER_SET]; @@ -604,7 +599,8 @@ typedef struct processingData_L1tx { nfapi_nr_dl_tti_pdcch_pdu ul_pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; NR_gNB_CSIRS_t csirs_pdu[NR_SYMBOLS_PER_SLOT]; NR_gNB_DLSCH_t **dlsch; - NR_gNB_SSB_t ssb[64]; + nfapi_nr_dl_tti_ssb_pdu ssb_pdu[64]; + int n_ssb_pdu; uint16_t num_pdsch_slot; int num_dl_pdcch; int num_ul_pdcch; diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 01955a0c18..b00cdf416d 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -47,12 +47,7 @@ static void handle_nr_nfapi_ssb_pdu(processingData_L1tx_t *msgTx, int frame, int uint8_t i_ssb = dl_tti_pdu->ssb_pdu.ssb_pdu_rel15.SsbBlockIndex; LOG_D(NR_PHY,"%d.%d : ssb index %d pbch_pdu: %x\n",frame,slot,i_ssb,dl_tti_pdu->ssb_pdu.ssb_pdu_rel15.bchPayload); - if (msgTx->ssb[i_ssb].active) - AssertFatal(1==0,"SSB PDU with index %d already active\n",i_ssb); - else { - msgTx->ssb[i_ssb].active = true; - memcpy((void*)&msgTx->ssb[i_ssb].ssb_pdu,&dl_tti_pdu->ssb_pdu,sizeof(dl_tti_pdu->ssb_pdu)); - } + msgTx->ssb_pdu[msgTx->n_ssb_pdu++] = dl_tti_pdu->ssb_pdu; } static void handle_nfapi_nr_csirs_pdu(processingData_L1tx_t *msgTx, int frame, int slot, nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index bf743c60f6..570a0785a4 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -243,12 +243,9 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_COMMON_TX,1); - for (int i = 0; i < fp->Lmax; i++) { - if (msgTx->ssb[i].active) { - nr_common_signal_procedures(gNB, frame, slot, &msgTx->ssb[i].ssb_pdu); - msgTx->ssb[i].active = false; - } - } + for (int i = 0; i < msgTx->n_ssb_pdu; i++) + nr_common_signal_procedures(gNB, frame, slot, &msgTx->ssb_pdu[i]); + msgTx->n_ssb_pdu = 0; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_COMMON_TX,0); diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index c03e3c0046..678b330513 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -979,7 +979,6 @@ int main(int argc, char **argv) init_DLSCH_struct(gNB, msgDataTx); msgDataTx->slot = slot; msgDataTx->frame = frame; - memset(msgDataTx->ssb, 0, 64*sizeof(NR_gNB_SSB_t)); gNB->msgDataTx = msgDataTx; // Buffers to store internal memory of slot process @@ -1117,8 +1116,8 @@ int main(int argc, char **argv) LOG_D(PHY,"[DLSIM] PTRS Symbols in a slot: %2u, RE per Symbol: %3u, RE in a slot %4d\n", ptrsSymbPerSlot, ptrsRePerSymb, ptrsSymbPerSlot * ptrsRePerSymb); } - msgDataTx->ssb[0].ssb_pdu.ssb_pdu_rel15.bchPayload=0x001234; - msgDataTx->ssb[0].ssb_pdu.ssb_pdu_rel15.SsbBlockIndex = 0; + msgDataTx->ssb_pdu[0].ssb_pdu_rel15.bchPayload = 0x001234; + msgDataTx->ssb_pdu[0].ssb_pdu_rel15.SsbBlockIndex = 0; msgDataTx->gNB = gNB; start_meas(&gNB->phy_proc_tx); diff --git a/openair1/SIMULATION/NR_PHY/pbchsim.c b/openair1/SIMULATION/NR_PHY/pbchsim.c index 97421130af..b40cfc8176 100644 --- a/openair1/SIMULATION/NR_PHY/pbchsim.c +++ b/openair1/SIMULATION/NR_PHY/pbchsim.c @@ -530,10 +530,10 @@ int main(int argc, char **argv) const int sc_offset = frame_parms->freq_range == FR1 ? ssb_subcarrier_offset<freq_range == FR1 ? gNB->gNB_config.ssb_table.ssb_offset_point_a.value<gNB_config.ssb_table.ssb_offset_point_a.value << (mu - 2); - msgDataTx.ssb[i].ssb_pdu.ssb_pdu_rel15.bchPayload = 0x55dd33; - msgDataTx.ssb[i].ssb_pdu.ssb_pdu_rel15.SsbBlockIndex = i; - msgDataTx.ssb[i].ssb_pdu.ssb_pdu_rel15.SsbSubcarrierOffset = sc_offset; - msgDataTx.ssb[i].ssb_pdu.ssb_pdu_rel15.ssbOffsetPointA = prb_offset; + msgDataTx.ssb_pdu[i].ssb_pdu_rel15.bchPayload = 0x55dd33; + msgDataTx.ssb_pdu[i].ssb_pdu_rel15.SsbBlockIndex = i; + msgDataTx.ssb_pdu[i].ssb_pdu_rel15.SsbSubcarrierOffset = sc_offset; + msgDataTx.ssb_pdu[i].ssb_pdu_rel15.ssbOffsetPointA = prb_offset; start_symbol = nr_get_ssb_start_symbol(frame_parms,i); int slot = start_symbol/14; @@ -541,7 +541,7 @@ int main(int argc, char **argv) for (aa=0; aaframe_parms.nb_antennas_tx; aa++) memset(gNB->common_vars.txdataF[0][aa], 0, frame_parms->samples_per_slot_wCP * sizeof(int32_t)); - nr_common_signal_procedures (gNB,frame,slot, &msgDataTx.ssb[i].ssb_pdu); + nr_common_signal_procedures (gNB,frame,slot, &msgDataTx.ssb_pdu[i]); int samp = frame_parms->get_samples_slot_timestamp(slot, frame_parms, 0); for (aa=0; aaframe_parms.nb_antennas_tx; aa++) { @@ -744,7 +744,7 @@ int main(int argc, char **argv) gNB->gNB_config.ssb_table.ssb_subcarrier_offset.value, frame_parms->Lmax); int payload_ret = (result.xtra_byte == xtra_byte); - nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu = &msgDataTx.ssb[ssb_index].ssb_pdu.ssb_pdu_rel15; + nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu = &msgDataTx.ssb_pdu[ssb_index].ssb_pdu_rel15; for (int i = 0; i < 3; i++) payload_ret += (result.decoded_output[i] == ((pdu->bchPayload >> (8 * i)) & 0xff)); // printf("ret %d\n", payload_ret); From 62333cd3cfb7af097a4e3e828263c24214c4e4fd Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Nov 2025 17:27:32 +0100 Subject: [PATCH 208/275] Refactor csi-rs handling Similarly to the parent commit, simplify the array type to the FAPI PDU towards further refactoring, and encapsulate the code in a function for easier reuse in a later refactoring step. Note on ZP-CSI: after this commit, we continue treating CSI PDUs. The prior code was returning, which I think is wrong (as this information is only for the currently treated CSI-RS PDU). --- openair1/PHY/defs_gNB.h | 8 +-- openair1/SCHED_NR/fapi_nr_l1.c | 15 +--- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 86 +++++++++++------------ 3 files changed, 46 insertions(+), 63 deletions(-) diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 01a9bd99f2..cb4edddeca 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -72,11 +72,6 @@ typedef struct { uint32_t unav_res; } NR_DL_gNB_HARQ_t; -typedef struct { - uint8_t active; - nfapi_nr_dl_tti_csi_rs_pdu csirs_pdu; -} NR_gNB_CSIRS_t; - typedef struct { int dump_frame; int round_trials[8]; @@ -597,10 +592,11 @@ typedef struct processingData_L1tx { nfapi_nr_dl_tti_pdcch_pdu pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; /// corresponds to UL_dci_req->ul_dci_pdu_list nfapi_nr_dl_tti_pdcch_pdu ul_pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; - NR_gNB_CSIRS_t csirs_pdu[NR_SYMBOLS_PER_SLOT]; + nfapi_nr_dl_tti_csi_rs_pdu csirs_pdu[NFAPI_NR_MAX_NB_CORESETS]; NR_gNB_DLSCH_t **dlsch; nfapi_nr_dl_tti_ssb_pdu ssb_pdu[64]; int n_ssb_pdu; + int n_csirs_pdu; uint16_t num_pdsch_slot; int num_dl_pdcch; int num_ul_pdcch; diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index b00cdf416d..0d122cedf5 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -52,20 +52,7 @@ static void handle_nr_nfapi_ssb_pdu(processingData_L1tx_t *msgTx, int frame, int static void handle_nfapi_nr_csirs_pdu(processingData_L1tx_t *msgTx, int frame, int slot, nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu) { - int found = 0; - - for (int id = 0; id < NR_NUMBER_OF_SYMBOLS_PER_SLOT; id++) { - NR_gNB_CSIRS_t *csirs = &msgTx->csirs_pdu[id]; - if (csirs->active == 0) { - LOG_D(NR_PHY,"Frame %d Slot %d CSI_RS with ID %d is now active\n",frame,slot,id); - csirs->active = 1; - memcpy((void*)&csirs->csirs_pdu, (void*)csirs_pdu, sizeof(nfapi_nr_dl_tti_csi_rs_pdu)); - found = 1; - break; - } - } - if (found == 0) - LOG_E(MAC,"CSI-RS list is full\n"); + msgTx->csirs_pdu[msgTx->n_csirs_pdu++] = *csirs_pdu; } void nr_schedule_dl_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_dl_tti_request_t *DL_req) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 570a0785a4..6c94a3005e 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -203,6 +203,46 @@ void clear_slot_beamid(PHY_VARS_gNB *gNB, int slot) } } +static void nr_generate_csi_rs_gNB(PHY_VARS_gNB *gNB, + int slot, + const nfapi_nr_config_request_scf_t *cfg, + const nfapi_nr_dl_tti_csi_rs_pdu *csi_rs_pdu) +{ + const nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *csi_params = &csi_rs_pdu->csi_rs_pdu_rel15; + if (csi_params->csi_type == 2) // ZP-CSI + return; + + csi_mapping_parms_t mapping_parms = + get_csi_mapping_parms(csi_params->row, csi_params->freq_domain, csi_params->symb_l0, csi_params->symb_l1); + const nfapi_nr_tx_precoding_and_beamforming_t *pb = &csi_params->precodingAndBeamforming; + int csi_bitmap = 0; + int lprime_num = mapping_parms.lprime + 1; + for (int j = 0; j < mapping_parms.size; j++) + csi_bitmap |= ((1 << lprime_num) - 1) << mapping_parms.loverline[j]; + int beam_nb = beam_index_allocation(gNB->enable_analog_das, + pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, + &cfg->analog_beamforming_ve, + &gNB->common_vars, + slot, + gNB->frame_parms.symbols_per_slot, + csi_bitmap); + + nr_generate_csi_rs(&gNB->frame_parms, + &mapping_parms, + gNB->TX_AMP, + slot, + csi_params->freq_density, + csi_params->start_rb, + csi_params->nr_of_rbs, + csi_params->symb_l0, + csi_params->symb_l1, + csi_params->row, + csi_params->scramb_id, + csi_params->power_control_offset_ss, + csi_params->cdm_type, + gNB->common_vars.txdataF[beam_nb]); +} + void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, int frame, int slot, @@ -277,49 +317,9 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, } msgTx->num_pdsch_slot = 0; - for (int i = 0; i < NR_SYMBOLS_PER_SLOT; i++){ - NR_gNB_CSIRS_t *csirs = &msgTx->csirs_pdu[i]; - if (csirs->active == 1) { - LOG_D(PHY, "CSI-RS generation started in frame %d.%d\n",frame,slot); - nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *csi_params = &csirs->csirs_pdu.csi_rs_pdu_rel15; - if (csi_params->csi_type == 2) { // ZP-CSI - csirs->active = 0; - return; - } - csi_mapping_parms_t mapping_parms = get_csi_mapping_parms(csi_params->row, - csi_params->freq_domain, - csi_params->symb_l0, - csi_params->symb_l1); - nfapi_nr_tx_precoding_and_beamforming_t *pb = &csi_params->precodingAndBeamforming; - int csi_bitmap = 0; - int lprime_num = mapping_parms.lprime + 1; - for (int j = 0; j < mapping_parms.size; j++) - csi_bitmap |= ((1 << lprime_num) - 1) << mapping_parms.loverline[j]; - int beam_nb = beam_index_allocation(gNB->enable_analog_das, - pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, - &cfg->analog_beamforming_ve, - &gNB->common_vars, - slot, - fp->symbols_per_slot, - csi_bitmap); - - nr_generate_csi_rs(&gNB->frame_parms, - &mapping_parms, - gNB->TX_AMP, - slot, - csi_params->freq_density, - csi_params->start_rb, - csi_params->nr_of_rbs, - csi_params->symb_l0, - csi_params->symb_l1, - csi_params->row, - csi_params->scramb_id, - csi_params->power_control_offset_ss, - csi_params->cdm_type, - gNB->common_vars.txdataF[beam_nb]); - csirs->active = 0; - } - } + for (int i = 0; i < msgTx->n_csirs_pdu; i++) + nr_generate_csi_rs_gNB(gNB, slot, cfg, &msgTx->csirs_pdu[i]); + msgTx->n_csirs_pdu = 0; //apply the OFDM symbol rotation here start_meas(&gNB->phase_comp_stats); From c06033697c29dbded5850e289a4cdeb10fe85b55 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 6 Nov 2025 09:56:35 +0100 Subject: [PATCH 209/275] Remove unused or write-only variables --- openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c | 1 - openair1/PHY/defs_gNB.h | 8 -------- 2 files changed, 9 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index 4cbfdde2e2..79538c52cd 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -203,7 +203,6 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, // The harq_pid is not unique among the active HARQ processes in the instance so we use dlsch_id instead TB_parameters->harq_unique_pid = dlsch_id; TB_parameters->BG = rel15->maintenance_parms_v3.ldpcBaseGraph; - TB_parameters->Z = harq->Z; TB_parameters->A = A; start_meas(dlsch_segmentation_stats); TB_parameters->Kb = nr_segmentation(harq->b, diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index cb4edddeca..246d3aaa6f 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -62,12 +62,8 @@ typedef struct { uint8_t *b; /// Pointers to transport block segments uint8_t **c; - /// Frame where current HARQ round was sent - uint32_t frame; /// Interleaver outputs uint8_t *f; - /// LDPC lifting size - uint32_t Z; /// REs unavailable for DLSCH (overlapping with PTRS, CSIRS etc.) uint32_t unav_res; } NR_DL_gNB_HARQ_t; @@ -118,10 +114,6 @@ typedef struct { typedef struct { /// Pointers to variables related to DLSCH harq process NR_DL_gNB_HARQ_t harq_process; - /// Active flag for baseband transmitter processing - uint8_t active; - /// Number of soft channel bits - uint32_t G; } NR_gNB_DLSCH_t; typedef struct { From 09e0344b5ba0b63f02cd12e90406cd4f3fd71369 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Nov 2025 18:23:47 +0100 Subject: [PATCH 210/275] Remove unused computation unav_res is already computed in nr_generate_pdsch(), and then used inside of nr_dlsch_encoding() right after. --- openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index c5b981d301..171aeec73f 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -580,7 +580,6 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC /* PTRS */ uint16_t dlPtrsSymPos = 0; int n_ptrs = 0; - uint32_t ptrsSymbPerSlot = 0; if (rel15->pduBitmap & 0x1) { set_ptrs_symb_idx(&dlPtrsSymPos, rel15->NrOfSymbols, @@ -588,9 +587,7 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC 1 << rel15->PTRSTimeDensity, rel15->dlDmrsSymbPos); n_ptrs = (rel15->rbSize + rel15->PTRSFreqDensity - 1) / rel15->PTRSFreqDensity; - ptrsSymbPerSlot = get_ptrs_symbols_in_slot(dlPtrsSymPos, rel15->StartSymbolIndex, rel15->NrOfSymbols); } - harq->unav_res = ptrsSymbPerSlot * n_ptrs; #ifdef DEBUG_DLSCH printf("PDSCH encoding:\nPayload:\n"); From 2b2b3cd3470ee344ebdeacf7bc2203452b882fd8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 13 Nov 2025 19:26:02 +0100 Subject: [PATCH 211/275] Simplify gNB DLSCH type DLSCH encoding is not concerned with HARQ processes directly. Instead, it gets the redundancy version and encodes a specific redundancy version for a given TB. The rest is up to the MAC, the L1 is stateless in that regard. Hence, remove the HARQ type and put it into DLSCH, which also simplifies the code elsewhere. --- openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 16 ++--- openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c | 71 ++++++++++----------- openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c | 10 ++- openair1/PHY/defs_gNB.h | 29 ++++----- openair1/SIMULATION/NR_PHY/dlschsim.c | 4 +- openair1/SIMULATION/NR_PHY/dlsim.c | 6 +- 6 files changed, 60 insertions(+), 76 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index 171aeec73f..55cd8fad11 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -555,8 +555,7 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC time_stats_t *dlsch_scrambling_stats = &gNB->dlsch_scrambling_stats; time_stats_t *dlsch_modulation_stats = &gNB->dlsch_modulation_stats; - NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; - const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; const int layerSz = frame_parms->N_RB_DL * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB; const int symbol_sz=frame_parms->ofdm_symbol_size; const int dmrs_Type = rel15->dmrsConfigType; @@ -591,9 +590,9 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC #ifdef DEBUG_DLSCH printf("PDSCH encoding:\nPayload:\n"); - for (int i = 0; i < (harq->B >> 3); i += 16) { + for (int i = 0; i < (dlsch->B >> 3); i += 16) { for (int j = 0; j < 16; j++) - printf("0x%02x\t", harq->pdu[i + j]); + printf("0x%02x\t", dlsch->pdu[i + j]); printf("\n"); } printf("\nEncoded payload:\n"); @@ -606,7 +605,7 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC #endif if (IS_SOFTMODEM_DLSIM) - memcpy(harq->f, input_ptr, (encoded_length + 7) >> 3); + memcpy(dlsch->f, input_ptr, (encoded_length + 7) >> 3); c16_t mod_symbs[rel15->NrOfCodewords][encoded_length] __attribute__((aligned(64))); for (int codeWord = 0; codeWord < rel15->NrOfCodewords; codeWord++) { @@ -791,8 +790,7 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot) for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { NR_gNB_DLSCH_t *dlsch = msgTx->dlsch[dlsch_id]; - NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; - const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; LOG_D(PHY, "pdsch: BWPStart %d, BWPSize %d, rbStart %d, rbsize %d\n", @@ -816,10 +814,10 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot) n_ptrs = (rel15->rbSize + rel15->PTRSFreqDensity - 1) / rel15->PTRSFreqDensity; ptrsSymbPerSlot = get_ptrs_symbols_in_slot(dlPtrsSymPos, rel15->StartSymbolIndex, rel15->NrOfSymbols); } - harq->unav_res = ptrsSymbPerSlot * n_ptrs; + dlsch->unav_res = ptrsSymbPerSlot * n_ptrs; /// CRC, coding, interleaving and rate matching - AssertFatal(harq->pdu != NULL, "%4d.%2d no HARQ PDU for PDSCH generation\n", msgTx->frame, msgTx->slot); + AssertFatal(dlsch->pdu != NULL, "%4d.%2d no PDU for PDSCH generation\n", msgTx->frame, msgTx->slot); /* output and its parts for each dlsch should be aligned on 64 bytes (or 8 * 64 bits) * => size_output is a sum of parts sizes rounded up to a multiple of 8 * 64 diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index 79538c52cd..eb70819ff4 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -52,20 +52,19 @@ void free_gNB_dlsch(NR_gNB_DLSCH_t *dlsch, uint16_t N_RB, const NR_DL_FRAME_PARM a_segments = a_segments / 273 + 1; } - NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; - if (harq->b) { - free16(harq->b, a_segments * 1056); - harq->b = NULL; + if (dlsch->b) { + free16(dlsch->b, a_segments * 1056); + dlsch->b = NULL; } - if (harq->f) { - free16(harq->f, N_RB * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB * 8 * NR_MAX_NB_LAYERS); - harq->f = NULL; + if (dlsch->f) { + free16(dlsch->f, N_RB * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB * 8 * NR_MAX_NB_LAYERS); + dlsch->f = NULL; } for (int r = 0; r < a_segments; r++) { - free(harq->c[r]); - harq->c[r] = NULL; + free(dlsch->c[r]); + dlsch->c[r] = NULL; } - free(harq->c); + free(dlsch->c); } NR_gNB_DLSCH_t new_gNB_dlsch(NR_DL_FRAME_PARMS *frame_parms, uint16_t N_RB) @@ -80,28 +79,26 @@ NR_gNB_DLSCH_t new_gNB_dlsch(NR_DL_FRAME_PARMS *frame_parms, uint16_t N_RB) LOG_D(PHY, "Allocating %d segments (MAX %d, N_PRB %d)\n", a_segments, MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER, N_RB); uint32_t dlsch_bytes = a_segments * 1056; // allocated bytes per segment - NR_gNB_DLSCH_t dlsch; + NR_gNB_DLSCH_t dlsch = {0}; - NR_DL_gNB_HARQ_t *harq = &dlsch.harq_process; - bzero(harq, sizeof(NR_DL_gNB_HARQ_t)); - harq->b = malloc16(dlsch_bytes); - AssertFatal(harq->b, "cannot allocate memory for harq->b\n"); - bzero(harq->b, dlsch_bytes); + dlsch.b = malloc16(dlsch_bytes); + AssertFatal(dlsch.b, "cannot allocate memory for dlsch.b\n"); + bzero(dlsch.b, dlsch_bytes); - harq->c = (uint8_t **)malloc16(a_segments * sizeof(uint8_t *)); + dlsch.c = (uint8_t **)malloc16(a_segments * sizeof(uint8_t *)); for (int r = 0; r < a_segments; r++) { // account for filler in first segment and CRCs for multiple segment case // [hna] 8448 is the maximum CB size in NR // 68*348 = 68*(maximum size of Zc) // In section 5.3.2 in 38.212, the for loop is up to N + 2*Zc (maximum size of N is 66*Zc, therefore 68*Zc) - harq->c[r] = malloc16(8448); - AssertFatal(harq->c[r], "cannot allocate harq->c[%d]\n", r); - bzero(harq->c[r], 8448); + dlsch.c[r] = malloc16(8448); + AssertFatal(dlsch.c[r], "cannot allocate dlsch.c[%d]\n", r); + bzero(dlsch.c[r], 8448); } - harq->f = malloc16(N_RB * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB * 8 * NR_MAX_NB_LAYERS); - AssertFatal(harq->f, "cannot allocate harq->f\n"); - bzero(harq->f, N_RB * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB * 8 * NR_MAX_NB_LAYERS); + dlsch.f = malloc16(N_RB * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB * 8 * NR_MAX_NB_LAYERS); + AssertFatal(dlsch.f, "cannot allocate dlsch->f\n"); + bzero(dlsch.f, N_RB * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB * 8 * NR_MAX_NB_LAYERS); return (dlsch); } @@ -139,11 +136,10 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { NR_gNB_DLSCH_t *dlsch = msgTx->dlsch[dlsch_id]; - NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; unsigned int crc = 1; - const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; uint32_t A = rel15->TBSize[0] << 3; - unsigned char *a = harq->pdu; + unsigned char *a = dlsch->pdu; if (rel15->rnti != SI_RNTI) { ws_trace_t tmp = {.nr = true, .direction = DIRECTION_DOWNLINK, @@ -182,9 +178,8 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, // printf("CRC %x (A %d)\n",crc,A); // printf("a0 %d a1 %d a2 %d\n", a[A>>3], a[1+(A>>3)], a[2+(A>>3)]); B = A + 24; - // harq->b = a; AssertFatal((A / 8) + 4 <= max_bytes, "A %d is too big (A/8+4 = %d > %d)\n", A, (A / 8) + 4, max_bytes); - memcpy(harq->b, a, (A / 8) + 4); // why is this +4 if the CRC is only 3 bytes? + memcpy(dlsch->b, a, (A / 8) + 4); // why is this +4 if the CRC is only 3 bytes? } else { // Add 16-bit crc (polynomial A) to payload crc = crc16(a, A) >> 16; @@ -193,9 +188,8 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, // printf("CRC %x (A %d)\n",crc,A); // printf("a0 %d a1 %d \n", a[A>>3], a[1+(A>>3)]); B = A + 16; - // harq->b = a; AssertFatal((A / 8) + 3 <= max_bytes, "A %d is too big (A/8+3 = %d > %d)\n", A, (A / 8) + 3, max_bytes); - memcpy(harq->b, a, (A / 8) + 3); // using 3 bytes to mimic the case of 24 bit crc + memcpy(dlsch->b, a, (A / 8) + 3); // using 3 bytes to mimic the case of 24 bit crc } nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[dlsch_id]; @@ -205,8 +199,8 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, TB_parameters->BG = rel15->maintenance_parms_v3.ldpcBaseGraph; TB_parameters->A = A; start_meas(dlsch_segmentation_stats); - TB_parameters->Kb = nr_segmentation(harq->b, - harq->c, + TB_parameters->Kb = nr_segmentation(dlsch->b, + dlsch->c, B, &TB_parameters->C, &TB_parameters->K, @@ -229,18 +223,17 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { NR_gNB_DLSCH_t *dlsch = msgTx->dlsch[dlsch_id]; - NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; - const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[dlsch_id]; #ifdef DEBUG_DLSCH_CODING for (int r = 0; r < TB_parameters->C; r++) { - LOG_D(PHY, "Encoder: B %d F %d \n", harq->B, TB_parameters->F); + LOG_D(PHY, "Encoder: B %d F %d \n", dlsch->B, TB_parameters->F); LOG_D(PHY, "start ldpc encoder segment %d/%d\n", r, TB_parameters->C); - LOG_D(PHY, "input %d %d %d %d %d \n", harq->c[r][0], harq->c[r][1], harq->c[r][2], harq->c[r][3], harq->c[r][4]); + LOG_D(PHY, "input %d %d %d %d %d \n", dlsch->c[r][0], dlsch->c[r][1], dlsch->c[r][2], dlsch->c[r][3], dlsch->c[r][4]); for (int cnt = 0; cnt < 22 * (TB_parameters->Z) / 8; cnt++) { - LOG_D(PHY, "%d ", harq->c[r][cnt]); + LOG_D(PHY, "%d ", dlsch->c[r][cnt]); } LOG_D(PHY, "\n"); } @@ -258,7 +251,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, rel15->NrOfSymbols, nb_re_dmrs, get_num_dmrs(rel15->dlDmrsSymbPos), - harq->unav_res, + dlsch->unav_res, rel15->qamModOrder[0], rel15->nrOfLayers); @@ -269,7 +262,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, for (int r = 0; r < TB_parameters->C; r++) { nrLDPC_segment_encoding_parameters_t *segment_parameters = &TB_parameters->segments[r]; - segment_parameters->c = harq->c[r]; + segment_parameters->c = dlsch->c[r]; segment_parameters->E = nr_get_E(TB_parameters->G, TB_parameters->C, TB_parameters->Qm, rel15->nrOfLayers, r); reset_meas(&segment_parameters->ts_interleave); diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c index 3f1d988c53..45a064f571 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c @@ -37,15 +37,14 @@ void nr_fill_dlsch_dl_tti_req(processingData_L1tx_t *msgTx, nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu) { NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[msgTx->num_pdsch_slot][0]; - NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; /// DLSCH struct - memcpy((void*)&harq->pdsch_pdu, (void*)pdsch_pdu, sizeof(nfapi_nr_dl_tti_pdsch_pdu)); + memcpy((void*)&dlsch->pdsch_pdu, (void*)pdsch_pdu, sizeof(nfapi_nr_dl_tti_pdsch_pdu)); AssertFatal(msgTx->num_pdsch_slot == pdsch_pdu->pdsch_pdu_rel15.pduIndex, "PDSCH PDU index %d does not match msgTx index %d\n", pdsch_pdu->pdsch_pdu_rel15.pduIndex, msgTx->num_pdsch_slot); msgTx->num_pdsch_slot++; - harq->pdu = NULL; + dlsch->pdu = NULL; } void nr_fill_dlsch_tx_req(processingData_L1tx_t *msgTx, int idx, uint8_t *sdu) @@ -55,8 +54,7 @@ void nr_fill_dlsch_tx_req(processingData_L1tx_t *msgTx, int idx, uint8_t *sdu) /* not sure if FAPI could transmit DL_TTI_req and TX_req in different orders. * for the moment, assume they are in the same order (and check!) */ NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[idx][0]; - NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process; - nfapi_nr_dl_tti_pdsch_pdu *pdsch = &harq->pdsch_pdu; + nfapi_nr_dl_tti_pdsch_pdu *pdsch = &dlsch->pdsch_pdu; AssertFatal(pdsch->pdsch_pdu_rel15.pduIndex == idx, "PDSCH PDU index %d does not match %d\n", pdsch->pdsch_pdu_rel15.pduIndex, idx); - harq->pdu = sdu; + dlsch->pdu = sdu; } diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 246d3aaa6f..fe4d3869d2 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -53,21 +53,6 @@ typedef struct { int lut[MAX_PUCCH0_NID][160][14]; } NR_gNB_PUCCH0_LUT_t; -typedef struct { - /// Nfapi DLSCH PDU - nfapi_nr_dl_tti_pdsch_pdu pdsch_pdu; - /// pointer to pdu from MAC interface (this is "a" in 36.212) - uint8_t *pdu; - /// Pointer to the payload - uint8_t *b; - /// Pointers to transport block segments - uint8_t **c; - /// Interleaver outputs - uint8_t *f; - /// REs unavailable for DLSCH (overlapping with PTRS, CSIRS etc.) - uint32_t unav_res; -} NR_DL_gNB_HARQ_t; - typedef struct { int dump_frame; int round_trials[8]; @@ -112,8 +97,18 @@ typedef struct { } NR_gNB_PHY_STATS_t; typedef struct { - /// Pointers to variables related to DLSCH harq process - NR_DL_gNB_HARQ_t harq_process; + /// Nfapi DLSCH PDU + nfapi_nr_dl_tti_pdsch_pdu pdsch_pdu; + /// pointer to pdu from MAC interface (this is "a" in 36.212) + uint8_t *pdu; + /// Pointer to the payload + uint8_t *b; + /// Pointers to transport block segments + uint8_t **c; + /// Interleaver outputs + uint8_t *f; + /// REs unavailable for DLSCH (overlapping with PTRS, CSIRS etc.) + uint32_t unav_res; } NR_gNB_DLSCH_t; typedef struct { diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c index c475bfc039..7a2f120043 100644 --- a/openair1/SIMULATION/NR_PHY/dlschsim.c +++ b/openair1/SIMULATION/NR_PHY/dlschsim.c @@ -442,7 +442,7 @@ int main(int argc, char **argv) processingData_L1tx_t msgDataTx; init_DLSCH_struct(gNB, &msgDataTx); NR_gNB_DLSCH_t *dlsch = &msgDataTx.dlsch[0][0]; - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->harq_process.pdsch_pdu.pdsch_pdu_rel15; + nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; //time_stats_t *rm_stats, *te_stats, *i_stats; unsigned int TBS = 8424; uint8_t nb_re_dmrs = 6; // No data in dmrs symbol @@ -498,7 +498,7 @@ int main(int argc, char **argv) printf("harq process ue mcs = %d Qm = %d, symb %d\n", dlsch0_ue->dlsch_config.mcs, dlsch0_ue->dlsch_config.qamModOrder, nb_symb_sch); uint8_t test_input[TBS / 8 + 4]; // + 3 for CRC + 1 additional byte, see nr_dlsch_encoding() - dlsch->harq_process.pdu = test_input; + dlsch->pdu = test_input; //unsigned char test_input[TBS / 8] __attribute__ ((aligned(16))); for (i = 0; i < TBS / 8; i++) test_input[i] = (unsigned char) rand(); diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 678b330513..cd8fef35ff 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -1059,7 +1059,7 @@ int main(int argc, char **argv) if (n_trials== 1) num_rounds = 1; NR_gNB_DLSCH_t *gNB_dlsch = &msgDataTx->dlsch[0][0]; - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &gNB_dlsch->harq_process.pdsch_pdu.pdsch_pdu_rel15; + nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &gNB_dlsch->pdsch_pdu.pdsch_pdu_rel15; for (trial = 0; trial < n_trials && !stop; trial++) { @@ -1255,7 +1255,7 @@ int main(int argc, char **argv) } for (i = 0; i < available_bits; i++) { - const uint8_t current_bit = (gNB_dlsch->harq_process.f[i / 8] & (1 << (i & 7))) >> (i & 7); + const uint8_t current_bit = (gNB_dlsch->f[i / 8] & (1 << (i & 7))) >> (i & 7); if (((current_bit == 0) && (UE_llr[i] <= 0)) || ((current_bit == 1) && (UE_llr[i] >= 0))) { if (errors_scrambling[round] == 0) { LOG_D(PHY,"First bit in error in unscrambling = %d\n",i); @@ -1275,7 +1275,7 @@ int main(int argc, char **argv) for (i = 0; i < TBS; i++) { estimated_output_bit[i] = (UE->phy_sim_dlsch_b[i/8] & (1 << (i & 7))) >> (i & 7); - test_input_bit[i] = (gNB_dlsch->harq_process.b[i / 8] & (1 << (i & 7))) >> (i & 7); // Further correct for multiple segments + test_input_bit[i] = (gNB_dlsch->b[i / 8] & (1 << (i & 7))) >> (i & 7); // Further correct for multiple segments if (estimated_output_bit[i] != test_input_bit[i]) { if(errors_bit == 0) From 751cab3d89fab95b963296ab3f18688db86d5483 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 13 Nov 2025 19:28:00 +0100 Subject: [PATCH 212/275] Simplify DLSCH: only one codeword supported --- openair1/PHY/INIT/nr_init.c | 14 +++----------- openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 4 ++-- openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c | 4 ++-- openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c | 4 ++-- openair1/PHY/defs_gNB.h | 2 +- openair1/SIMULATION/NR_PHY/dlschsim.c | 2 +- openair1/SIMULATION/NR_PHY/dlsim.c | 2 +- 7 files changed, 12 insertions(+), 20 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 7a360e30ff..e87a1d13db 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -408,14 +408,10 @@ void init_DLSCH_struct(PHY_VARS_gNB *gNB, processingData_L1tx_t *msg) uint16_t grid_size = cfg->carrier_config.dl_grid_size[fp->numerology_index].value; msg->num_pdsch_slot = 0; - msg->dlsch = malloc16(gNB->max_nb_pdsch * sizeof(NR_gNB_DLSCH_t *)); - int num_cw = NR_MAX_NB_LAYERS > 4? 2:1; + msg->dlsch = calloc(gNB->max_nb_pdsch, sizeof(*msg->dlsch)); for (int i = 0; i < gNB->max_nb_pdsch; i++) { LOG_D(PHY, "Allocating Transport Channel Buffers for DLSCH %d/%d\n", i, gNB->max_nb_pdsch); - msg->dlsch[i] = (NR_gNB_DLSCH_t *)malloc16(num_cw * sizeof(NR_gNB_DLSCH_t)); - for (int j = 0; j < num_cw; j++) { - msg->dlsch[i][j] = new_gNB_dlsch(fp, grid_size); - } + msg->dlsch[i] = new_gNB_dlsch(fp, grid_size); } } @@ -424,12 +420,8 @@ void reset_DLSCH_struct(const PHY_VARS_gNB *gNB, processingData_L1tx_t *msg) const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; const nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; const uint16_t grid_size = cfg->carrier_config.dl_grid_size[fp->numerology_index].value; - int num_cw = NR_MAX_NB_LAYERS > 4? 2:1; for (int i = 0; i < gNB->max_nb_pdsch; i++) { - for (int j = 0; j < num_cw; j++) { - free_gNB_dlsch(&msg->dlsch[i][j], grid_size, fp); - } - free(msg->dlsch[i]); + free_gNB_dlsch(&msg->dlsch[i], grid_size, fp); } free(msg->dlsch); } diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index 55cd8fad11..30137d8e35 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -789,7 +789,7 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot) size_t size_output = 0; for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { - NR_gNB_DLSCH_t *dlsch = msgTx->dlsch[dlsch_id]; + NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[dlsch_id]; const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; LOG_D(PHY, @@ -850,7 +850,7 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot) unsigned char *output_ptr = output; for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { - output_ptr += do_one_dlsch(output_ptr, gNB, msgTx->dlsch[dlsch_id], slot); + output_ptr += do_one_dlsch(output_ptr, gNB, &msgTx->dlsch[dlsch_id], slot); } } diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index eb70819ff4..c26b56da3a 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -134,7 +134,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, int num_segments = 0; for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { - NR_gNB_DLSCH_t *dlsch = msgTx->dlsch[dlsch_id]; + NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[dlsch_id]; unsigned int crc = 1; const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; @@ -222,7 +222,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, size_t dlsch_offset = 0; for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { - NR_gNB_DLSCH_t *dlsch = msgTx->dlsch[dlsch_id]; + NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[dlsch_id]; const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[dlsch_id]; diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c index 45a064f571..244ab5363b 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c @@ -36,7 +36,7 @@ void nr_fill_dlsch_dl_tti_req(processingData_L1tx_t *msgTx, nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu) { - NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[msgTx->num_pdsch_slot][0]; + NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[msgTx->num_pdsch_slot]; /// DLSCH struct memcpy((void*)&dlsch->pdsch_pdu, (void*)pdsch_pdu, sizeof(nfapi_nr_dl_tti_pdsch_pdu)); AssertFatal(msgTx->num_pdsch_slot == pdsch_pdu->pdsch_pdu_rel15.pduIndex, @@ -53,7 +53,7 @@ void nr_fill_dlsch_tx_req(processingData_L1tx_t *msgTx, int idx, uint8_t *sdu) /* not sure if FAPI could transmit DL_TTI_req and TX_req in different orders. * for the moment, assume they are in the same order (and check!) */ - NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[idx][0]; + NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[idx]; nfapi_nr_dl_tti_pdsch_pdu *pdsch = &dlsch->pdsch_pdu; AssertFatal(pdsch->pdsch_pdu_rel15.pduIndex == idx, "PDSCH PDU index %d does not match %d\n", pdsch->pdsch_pdu_rel15.pduIndex, idx); dlsch->pdu = sdu; diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index fe4d3869d2..af4f0d5707 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -580,7 +580,7 @@ typedef struct processingData_L1tx { /// corresponds to UL_dci_req->ul_dci_pdu_list nfapi_nr_dl_tti_pdcch_pdu ul_pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; nfapi_nr_dl_tti_csi_rs_pdu csirs_pdu[NFAPI_NR_MAX_NB_CORESETS]; - NR_gNB_DLSCH_t **dlsch; + NR_gNB_DLSCH_t *dlsch; nfapi_nr_dl_tti_ssb_pdu ssb_pdu[64]; int n_ssb_pdu; int n_csirs_pdu; diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c index 7a2f120043..ad0120cb52 100644 --- a/openair1/SIMULATION/NR_PHY/dlschsim.c +++ b/openair1/SIMULATION/NR_PHY/dlschsim.c @@ -441,7 +441,7 @@ int main(int argc, char **argv) unsigned char harq_pid = 0; //dlsch->harq_ids[subframe]; processingData_L1tx_t msgDataTx; init_DLSCH_struct(gNB, &msgDataTx); - NR_gNB_DLSCH_t *dlsch = &msgDataTx.dlsch[0][0]; + NR_gNB_DLSCH_t *dlsch = &msgDataTx.dlsch[0]; nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; //time_stats_t *rm_stats, *te_stats, *i_stats; unsigned int TBS = 8424; diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index cd8fef35ff..a4c4351764 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -1058,7 +1058,7 @@ int main(int argc, char **argv) n_false_positive = 0; if (n_trials== 1) num_rounds = 1; - NR_gNB_DLSCH_t *gNB_dlsch = &msgDataTx->dlsch[0][0]; + NR_gNB_DLSCH_t *gNB_dlsch = &msgDataTx->dlsch[0]; nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &gNB_dlsch->pdsch_pdu.pdsch_pdu_rel15; for (trial = 0; trial < n_trials && !stop; trial++) { From 2be5ff361f58b24e65277032d8ec53055b28aed7 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 13 Nov 2025 19:29:10 +0100 Subject: [PATCH 213/275] Don't use msgTx inside nr_dlsch_encoding() and nr_generate_pdsch() A later commit will remove "msgTx" altogether. Push it out from L1 functions. --- openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 16 ++++++------ openair1/PHY/NR_TRANSPORT/nr_dlsch.h | 7 +++--- openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c | 27 +++++++++++---------- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 2 +- openair1/SIMULATION/NR_PHY/dlschsim.c | 3 +-- 5 files changed, 27 insertions(+), 28 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index 30137d8e35..7afbed41b4 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -773,9 +773,8 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC return ((size_output_tb + 511) >> 9) << 6; } -void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot) +void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_array, int frame, int slot) { - PHY_VARS_gNB *gNB = msgTx->gNB; NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; time_stats_t *dlsch_encoding_stats = &gNB->dlsch_encoding_stats; time_stats_t *tinput = &gNB->tinput; @@ -788,8 +787,8 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot) size_t size_output = 0; - for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { - NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[dlsch_id]; + for (int i = 0; i < n_dlsch; i++) { + NR_gNB_DLSCH_t *dlsch = &dlsch_array[i]; const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; LOG_D(PHY, @@ -817,7 +816,7 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot) dlsch->unav_res = ptrsSymbPerSlot * n_ptrs; /// CRC, coding, interleaving and rate matching - AssertFatal(dlsch->pdu != NULL, "%4d.%2d no PDU for PDSCH generation\n", msgTx->frame, msgTx->slot); + AssertFatal(dlsch->pdu != NULL, "%4d.%2d no PDU for PDSCH generation\n", frame, slot); /* output and its parts for each dlsch should be aligned on 64 bytes (or 8 * 64 bits) * => size_output is a sum of parts sizes rounded up to a multiple of 8 * 64 @@ -831,7 +830,8 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot) start_meas(dlsch_encoding_stats); if (nr_dlsch_encoding(gNB, - msgTx, + n_dlsch, + dlsch_array, frame, slot, frame_parms, @@ -849,8 +849,8 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot) stop_meas(dlsch_encoding_stats); unsigned char *output_ptr = output; - for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { - output_ptr += do_one_dlsch(output_ptr, gNB, &msgTx->dlsch[dlsch_id], slot); + for (int i = 0; i < n_dlsch; i++) { + output_ptr += do_one_dlsch(output_ptr, gNB, &dlsch_array[i], slot); } } diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.h b/openair1/PHY/NR_TRANSPORT/nr_dlsch.h index 15f48825f5..443035274b 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.h +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.h @@ -38,12 +38,11 @@ void nr_fill_dlsch_dl_tti_req(processingData_L1tx_t *msgTx, nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu); void nr_fill_dlsch_tx_req(processingData_L1tx_t *msgTx, int idx, uint8_t *sdu); -void nr_generate_pdsch(processingData_L1tx_t *msgTx, - int frame, - int slot); +void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_array, int frame, int slot); int nr_dlsch_encoding(PHY_VARS_gNB *gNB, - processingData_L1tx_t *msgTx, + int n_dlsch, + NR_gNB_DLSCH_t *dlsch_array, int frame, uint8_t slot, NR_DL_FRAME_PARMS *frame_parms, diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index c26b56da3a..67f3dd4363 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -104,7 +104,8 @@ NR_gNB_DLSCH_t new_gNB_dlsch(NR_DL_FRAME_PARMS *frame_parms, uint16_t N_RB) } int nr_dlsch_encoding(PHY_VARS_gNB *gNB, - processingData_L1tx_t *msgTx, + int n_dlsch, + NR_gNB_DLSCH_t *dlsch_array, int frame, uint8_t slot, NR_DL_FRAME_PARMS *frame_parms, @@ -119,11 +120,11 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_DLSCH_ENCODING, VCD_FUNCTION_IN); - nrLDPC_TB_encoding_parameters_t TBs[msgTx->num_pdsch_slot]; + nrLDPC_TB_encoding_parameters_t TBs[n_dlsch]; memset(TBs, 0, sizeof(TBs)); nrLDPC_slot_encoding_parameters_t slot_parameters = {.frame = frame, .slot = slot, - .nb_TBs = msgTx->num_pdsch_slot, + .nb_TBs = n_dlsch, .threadPool = &gNB->threadPool, .tinput = tinput, .tprep = tprep, @@ -133,8 +134,8 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, int num_segments = 0; - for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { - NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[dlsch_id]; + for (int i = 0; i < n_dlsch; i++) { + NR_gNB_DLSCH_t *dlsch = &dlsch_array[i]; unsigned int crc = 1; const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; @@ -192,10 +193,10 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, memcpy(dlsch->b, a, (A / 8) + 3); // using 3 bytes to mimic the case of 24 bit crc } - nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[dlsch_id]; + nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[i]; - // The harq_pid is not unique among the active HARQ processes in the instance so we use dlsch_id instead - TB_parameters->harq_unique_pid = dlsch_id; + // The harq_pid is not unique among the active HARQ processes in the instance so we use i instead + TB_parameters->harq_unique_pid = i; TB_parameters->BG = rel15->maintenance_parms_v3.ldpcBaseGraph; TB_parameters->A = A; start_meas(dlsch_segmentation_stats); @@ -221,11 +222,11 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, size_t segments_offset = 0; size_t dlsch_offset = 0; - for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { - NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[dlsch_id]; + for (int i = 0; i < n_dlsch; i++) { + NR_gNB_DLSCH_t *dlsch = &dlsch_array[i]; const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; - nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[dlsch_id]; + nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[i]; #ifdef DEBUG_DLSCH_CODING for (int r = 0; r < TB_parameters->C; r++) { @@ -281,8 +282,8 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, gNB->nrLDPC_coding_interface.nrLDPC_coding_encoder(&slot_parameters); - for (int dlsch_id = 0; dlsch_id < msgTx->num_pdsch_slot; dlsch_id++) { - nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[dlsch_id]; + for (int i = 0; i < n_dlsch; i++) { + nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[i]; for (int r = 0; r < TB_parameters->C; r++) { nrLDPC_segment_encoding_parameters_t *segment_parameters = &TB_parameters->segments[r]; merge_meas(dlsch_interleaving_stats, &segment_parameters->ts_interleave); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 6c94a3005e..f3f80e2ae4 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -312,7 +312,7 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, if (msgTx->num_pdsch_slot > 0) { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,1); LOG_D(PHY, "PDSCH generation started (%d) in frame %d.%d\n", msgTx->num_pdsch_slot,frame,slot); - nr_generate_pdsch(msgTx, frame, slot); + nr_generate_pdsch(gNB, msgTx->num_pdsch_slot, msgTx->dlsch, frame, slot); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,0); } msgTx->num_pdsch_slot = 0; diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c index ad0120cb52..0dbc230f09 100644 --- a/openair1/SIMULATION/NR_PHY/dlschsim.c +++ b/openair1/SIMULATION/NR_PHY/dlschsim.c @@ -515,8 +515,7 @@ int main(int argc, char **argv) unsigned char output[rel15->rbSize * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB * NR_MAX_NB_LAYERS] __attribute__((aligned(64))); bzero(output, sizeof(output)); if (input_fd == NULL) { - msgDataTx.num_pdsch_slot = 1; - nr_dlsch_encoding(gNB, &msgDataTx, frame, slot, frame_parms, output, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + nr_dlsch_encoding(gNB, 1, dlsch, frame, slot, frame_parms, output, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } for (SNR = snr0; SNR < snr1 && !stop; SNR += snr_step) { From 8d2a5c6fd607ee6c4d6b7133e78e4edf6c04e856 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 6 Nov 2025 12:26:06 +0100 Subject: [PATCH 214/275] First slot indication makes no sense: tx thread should trigger --- nfapi/oai_integration/nfapi_pnf.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/nfapi/oai_integration/nfapi_pnf.c b/nfapi/oai_integration/nfapi_pnf.c index 3de11d8003..bad2d036c9 100644 --- a/nfapi/oai_integration/nfapi_pnf.c +++ b/nfapi/oai_integration/nfapi_pnf.c @@ -1825,13 +1825,6 @@ int nr_start_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nf nfapi_nr_send_pnf_start_resp(config, p7_config->phy_id); printf("[PNF] Sending first P7 slot indication\n"); #endif -#if 1 - nfapi_pnf_p7_slot_ind(p7_config, p7_config->phy_id, 0, 0); - printf("[PNF] Sent first P7 slot ind\n"); -#else - nfapi_pnf_p7_subframe_ind(p7_config, p7_config->phy_id, 0); // SFN_SF set to zero - correct??? - printf("[PNF] Sent first P7 subframe ind\n"); -#endif return 0; } From 247e908b6eb97c82800d4602ff45506c6a13a693 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Nov 2025 14:31:23 +0100 Subject: [PATCH 215/275] Simplify UndeployObject() and GetDeployedObjects() GetRunningServices() was returning all services, running or stopped. Hence, rename to reflect what this function does, and rewrite slightly to better capture what is actually happening. Also, don't return the container ID, as we don't use it. Call stop on all services, as we will down the deployment right after. The list of services is still used to retrieve logs. Remove a stray "return False", which would sometimes prevent us from seeing all error messages. --- ci-scripts/cls_containerize.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 32ce498be0..ee295b1f79 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -165,25 +165,26 @@ def CopyinServiceLog(ssh, lSourcePath, svcName, wd_yaml, ctx): ssh.run(f'docker compose -f {wd_yaml} logs {svcName} --no-log-prefix &> {remote_filename}') return archiveArtifact(ssh, ctx, remote_filename) -def GetRunningServices(ssh, file): +def GetDeployedServices(ssh, file): ret = ssh.run(f'docker compose -f {file} config --services') if ret.returncode != 0: + logging.error("could not get services") return None allServices = ret.stdout.splitlines() - running_services = [] + deployed_services = [] for s in allServices: - # outputs the hash if the container is running - ret = ssh.run(f'docker compose -f {file} ps --all --quiet -- {s}') + # outputs the hash if the container has been deployed (but might be stopped) + ret = ssh.run(f'docker compose -f {file} ps --all --quiet -- {s}', silent=True) if ret.returncode != 0: - logging.info(f"service {s}: {ret.stdout}") + # error: should not happen as we iterate over docker-provided service list + logging.error(f"service {s}: {ret.stdout}") elif ret.stdout == "": - logging.warning(f"could not retrieve information for service {s}") + logging.info(f"service {s} not deployed") else: c = ret.stdout - logging.debug(f'running service {s} with container id {c}') - running_services.append((s, c)) - logging.info(f'stopping services: {running_services}') - return running_services + logging.info(f'service {s} with container id {c}') + deployed_services.append(s) + return deployed_services def CheckLogs(self, filename, HTML, RAN): success = True @@ -763,19 +764,19 @@ class Containerize(): wd_yaml = f'{wd}/docker-compose.y*ml' with cls_cmd.getConnection(node) as ssh: ExistEnvFilePrint(ssh, wd) - services = GetRunningServices(ssh, wd_yaml) + services = GetDeployedServices(ssh, wd_yaml) copyin_res = None + ssh.run(f'docker compose -f {wd_yaml} stop') if services is not None: - all_serv = " ".join([s for s, _ in services]) - ssh.run(f'docker compose -f {wd_yaml} stop -- {all_serv}') - copyin_res = [CopyinServiceLog(ssh, lSourcePath, s, wd_yaml, ctx) for s, _ in services] + copyin_res = [CopyinServiceLog(ssh, lSourcePath, s, wd_yaml, ctx) for s in services] else: logging.warning('could not identify services to stop => no log file') ssh.run(f'docker compose -f {wd_yaml} down -v') ssh.run(f'rm {wd}/.env') if not copyin_res: HTML.CreateHtmlTestRowQueue('N/A', 'KO', ['Could not copy logfile(s)']) - return False + logging.error(f"could not copy all files: {copyin_res=} {services=}") + success = False else: log_results = [CheckLogs(self, f, HTML, RAN) for f in copyin_res] success = all(log_results) From ddcb216a3543d96a1c86f4b3a8c5bcb19d342f4f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 13 Nov 2025 15:04:14 +0100 Subject: [PATCH 216/275] Improve logging in CI Undeploy/DeployObject() Consistently print requested services (if applicable) and outcome of the step. --- ci-scripts/cls_containerize.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index ee295b1f79..367bb47eb5 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -716,7 +716,6 @@ class Containerize(): def DeployObject(self, ctx, node, HTML): num_attempts = self.num_attempts lSourcePath = self.eNBSourceCodePath - logging.debug(f'Deploying OAI Object on server: {node}') yaml = self.yamlPath.strip('/') wd = f'{lSourcePath}/{yaml}' wd_yaml = f'{wd}/docker-compose.y*ml' @@ -727,6 +726,7 @@ class Containerize(): logging.error(msg) HTML.CreateHtmlTestRowQueue('N/A', 'KO', [msg]) return False + logging.info(f'\u001B[1mDeploying object(s) "{services}" on server {node}\u001B[0m') ExistEnvFilePrint(ssh, wd) WriteEnvFile(ssh, services, wd, self.deploymentTag, self.flexricTag) if num_attempts <= 0: @@ -751,14 +751,16 @@ class Containerize(): imagesInfo = info.stdout.splitlines()[1:] logging.debug(f'{info.stdout.splitlines()[1:]}') if deployed: - HTML.CreateHtmlTestRowQueue('N/A', 'OK', ['\n'.join(imagesInfo)]) + HTML.CreateHtmlTestRowQueue(self.services, 'OK', ['\n'.join(imagesInfo)]) + logging.info('\u001B[1m Deploying objects Pass\u001B[0m') else: - HTML.CreateHtmlTestRowQueue('N/A', 'KO', ['\n'.join(imagesInfo)]) + HTML.CreateHtmlTestRowQueue(self.services, 'KO', ['\n'.join(imagesInfo)]) + logging.error('\u001B[1m Deploying objects Failed\u001B[0m') return deployed def UndeployObject(self, ctx, node, HTML, RAN): lSourcePath = self.eNBSourceCodePath - logging.debug(f'\u001B[1m Undeploying OAI Object from server: {node}\u001B[0m') + logging.info(f'\u001B[1m Undeploying all objects from server {node}\u001B[0m') yaml = self.yamlPath.strip('/') wd = f'{lSourcePath}/{yaml}' wd_yaml = f'{wd}/docker-compose.y*ml' @@ -781,7 +783,7 @@ class Containerize(): log_results = [CheckLogs(self, f, HTML, RAN) for f in copyin_res] success = all(log_results) if success: - logging.info('\u001B[1m Undeploying OAI Object Pass\u001B[0m') + logging.info('\u001B[1m Undeploying objects Pass\u001B[0m') else: - logging.error('\u001B[1m Undeploying OAI Object Failed\u001B[0m') + logging.error('\u001B[1m Undeploying objects Failed\u001B[0m') return success From de8d0991ec290b537b1d515808d62edaa48c837b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 13 Nov 2025 11:34:30 +0100 Subject: [PATCH 217/275] Remove services to be undeployed from XML files In all these cases, the intention is to undeploy all services. Correspondingly, remove the services from the XML to avoid any ambiguity. --- ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml | 1 - ci-scripts/xml_files/container_sa_n310_2X2_100MHz_quectel.xml | 1 - ci-scripts/xml_files/container_sa_n310_2X2_60MHz_quectel.xml | 1 - ci-scripts/xml_files/container_sa_n310_4X4_60MHz_quectel.xml | 1 - 4 files changed, 4 deletions(-) diff --git a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml index d563b3aab4..86b9040b63 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml @@ -144,7 +144,6 @@ true Undeploy all OAI 5G stack localhost - oai-vnf oai-pnf ci-scripts/yaml_files/5g_rfsimulator_u0_25prb 1,0,0,0 1,0,0,0 diff --git a/ci-scripts/xml_files/container_sa_n310_2X2_100MHz_quectel.xml b/ci-scripts/xml_files/container_sa_n310_2X2_100MHz_quectel.xml index a4d72398e1..ad98afcd32 100644 --- a/ci-scripts/xml_files/container_sa_n310_2X2_100MHz_quectel.xml +++ b/ci-scripts/xml_files/container_sa_n310_2X2_100MHz_quectel.xml @@ -146,7 +146,6 @@ Undeploy gNB matix ci-scripts/yaml_files/5g_sa_n310_2x2_100MHz - oai-gnb 10,100,100,100 10,100,100,100 diff --git a/ci-scripts/xml_files/container_sa_n310_2X2_60MHz_quectel.xml b/ci-scripts/xml_files/container_sa_n310_2X2_60MHz_quectel.xml index ab7188c023..a809fafa2c 100644 --- a/ci-scripts/xml_files/container_sa_n310_2X2_60MHz_quectel.xml +++ b/ci-scripts/xml_files/container_sa_n310_2X2_60MHz_quectel.xml @@ -146,7 +146,6 @@ Undeploy gNB matix ci-scripts/yaml_files/5g_sa_n310_2x2_60MHz - oai-gnb 15,100,100,100 20,100,100,100 diff --git a/ci-scripts/xml_files/container_sa_n310_4X4_60MHz_quectel.xml b/ci-scripts/xml_files/container_sa_n310_4X4_60MHz_quectel.xml index d7cf992aa4..d4d0d51258 100644 --- a/ci-scripts/xml_files/container_sa_n310_4X4_60MHz_quectel.xml +++ b/ci-scripts/xml_files/container_sa_n310_4X4_60MHz_quectel.xml @@ -146,7 +146,6 @@ Undeploy VNF-PNF matix ci-scripts/yaml_files/5g_sa_n310_4x4_60MHz - oai-vnf oai-pnf 10,100,100,100 10,100,100,100 From 81680e29a9f7d91c2fe6bf46971a4566af7655d8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 13 Nov 2025 15:04:10 +0100 Subject: [PATCH 218/275] Implement StopObject() For some test cases, we might want to stop a service earlier. This functionality is done by StopObject(). Test log collection is still done by UndeployObject(), which will collect the logs of all services (at that point, stopped or not). --- ci-scripts/cls_containerize.py | 29 +++++++++++++++++++ ci-scripts/main.py | 4 ++- ci-scripts/tests/deployment.py | 27 +++++++++++++++++ .../tests/simple-undep/docker-compose.yml | 23 +++++++++++++++ ci-scripts/xml_class_list.yml | 1 + 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 ci-scripts/tests/simple-undep/docker-compose.yml diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 367bb47eb5..c94b0c8dfb 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -758,6 +758,35 @@ class Containerize(): logging.error('\u001B[1m Deploying objects Failed\u001B[0m') return deployed + def StopObject(self, ctx, node, HTML): + lSourcePath = self.eNBSourceCodePath + if not self.services: + raise ValueError(f'no services provided') + logging.info(f'\u001B[1m Stopping objects "{self.services}" from server: {node}\u001B[0m') + reqServices = self.services.split() + yaml = self.yamlPath.strip('/') + wd = f'{lSourcePath}/{yaml}' + wd_yaml = f'{wd}/docker-compose.y*ml' + with cls_cmd.getConnection(node) as ssh: + ExistEnvFilePrint(ssh, wd) + services = GetDeployedServices(ssh, wd_yaml) + success = [] + fail = [] + for s in reqServices: + if s in services: + ssh.run(f'docker compose -f {wd_yaml} stop -- {s}') + success.append(s) + else: + logging.error(f"no such service {s}") + fail.append(s) + if success == reqServices: + logging.info('\u001B[1m Stopping object Pass\u001B[0m') + HTML.CreateHtmlTestRowQueue(self.services, 'OK', [f'Stopped {self.services}']) + else: + logging.error('\u001B[1m Stopping object Failed\u001B[0m') + HTML.CreateHtmlTestRowQueue(self.services, 'KO', [f'Failed stopping {" ".join(fail)}, succeeded {" ".join(success)}']) + return success + def UndeployObject(self, ctx, node, HTML, RAN): lSourcePath = self.eNBSourceCodePath logging.info(f'\u001B[1m Undeploying all objects from server {node}\u001B[0m') diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 2428df83af..8aadad76bb 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -214,7 +214,7 @@ def ExecuteActionWithParam(action, ctx, node): core_op = getattr(cls_oaicitest.OaiCiTest, action) success = core_op(cn_id, ctx, HTML) - elif action == 'Deploy_Object' or action == 'Undeploy_Object' or action == "Create_Workspace": + elif action == 'Deploy_Object' or action == 'Undeploy_Object' or action == "Create_Workspace" or action == "Stop_Object": CONTAINERS.yamlPath = test.findtext('yaml_path') string_field=test.findtext('d_retx_th') if (string_field is not None): @@ -227,6 +227,8 @@ def ExecuteActionWithParam(action, ctx, node): CONTAINERS.deploymentTag = cls_containerize.CreateTag(CONTAINERS.ranCommitID, CONTAINERS.ranBranch, CONTAINERS.ranAllowMerge) if action == 'Deploy_Object': success = CONTAINERS.DeployObject(ctx, node, HTML) + elif action == 'Stop_Object': + success = CONTAINERS.StopObject(ctx, node, HTML) elif action == 'Undeploy_Object': success = CONTAINERS.UndeployObject(ctx, node, HTML, RAN) elif action == 'Create_Workspace': diff --git a/ci-scripts/tests/deployment.py b/ci-scripts/tests/deployment.py index e7efc707bf..10bd2c843f 100644 --- a/ci-scripts/tests/deployment.py +++ b/ci-scripts/tests/deployment.py @@ -60,6 +60,33 @@ class TestDeploymentMethods(unittest.TestCase): self.assertTrue(deploy) self.assertTrue(undeploy) + def test_stop_services(self): + self.cont.yamlPath = 'tests/simple-undep/' + self.cont.deploymentTag = "noble" + # should deploy both testA and testB + deploy = self.cont.DeployObject(self.ctx, self.node, self.html) + # should fail (no such service) + self.cont.services = "testC" + stopC = self.cont.StopObject(self.ctx, self.node, self.html) + # should stop testA + self.cont.services = "testA" + stopA = self.cont.StopObject(self.ctx, self.node, self.html) + # should (re-)stop testA (no-op) + self.cont.services = "testA" + stopA2 = self.cont.StopObject(self.ctx, self.node, self.html) + # should deploy testB + self.cont.services = "testB" + stopB = self.cont.StopObject(self.ctx, self.node, self.html) + # should not undeploy anything (everything already stopped) + self.cont.services = None + undeployAll = self.cont.UndeployObject(self.ctx, self.node, self.html, self.ran) + self.assertTrue(deploy) + self.assertFalse(stopC) + self.assertTrue(stopA) + self.assertTrue(stopA2) + self.assertTrue(stopB) + self.assertTrue(undeployAll) + def test_deployfails(self): # fails reliably old = self.cont.yamlPath diff --git a/ci-scripts/tests/simple-undep/docker-compose.yml b/ci-scripts/tests/simple-undep/docker-compose.yml new file mode 100644 index 0000000000..6f88826a9a --- /dev/null +++ b/ci-scripts/tests/simple-undep/docker-compose.yml @@ -0,0 +1,23 @@ +services: + testA: + image: ubuntu:${TAG:-jammy} + container_name: test_container_A + cap_drop: + - ALL + entrypoint: /bin/bash -c "sleep infinity" + healthcheck: + test: /bin/bash -c "true" + interval: 1s + timeout: 1s + retries: 1 + testB: + image: ubuntu:${TAG:-jammy} + container_name: test_container_B + cap_drop: + - ALL + entrypoint: /bin/bash -c "sleep infinity" + healthcheck: + test: /bin/bash -c "true" + interval: 1s + timeout: 1s + retries: 1 diff --git a/ci-scripts/xml_class_list.yml b/ci-scripts/xml_class_list.yml index 615454bbea..a20b78fc99 100755 --- a/ci-scripts/xml_class_list.yml +++ b/ci-scripts/xml_class_list.yml @@ -20,6 +20,7 @@ - Build_Image - Build_Run_Tests - Deploy_Object + - Stop_Object - Undeploy_Object - Cppcheck_Analysis - Deploy_Run_OC_PhySim From 6a6588e9cbb13ffdaa01c88537293d431be9f96b Mon Sep 17 00:00:00 2001 From: Teodora Date: Thu, 13 Nov 2025 12:11:03 +0100 Subject: [PATCH 219/275] OAI-FLEXRIC-RAN-Integration-Test CI pipeline 1. Rename oai-flexric to nearRT-RIC. FlexRIC is a project combining nearRT-RIC, xApps, and E2 agent emulators. 2. Instead of undeploying all the containers at the same time, and not checking the logs for xApps and nearRT-RIC, do the following: - Stop containers (docker compose stop) - one or subgroup of running containers in the following order: a) xapp-kpm-moni b) xapp-kpm-rc c) xapp-gtp-mac-rlc-pdcp-moni d) oai-nr-ue oai-nr-ue2 e) oai-gnb f) nearRT-RIC - Undeploy containers (docker compose down), collect and analyze all the logs. --- ci-scripts/cls_containerize.py | 19 +++++ .../xml_files/container_5g_flexric_rfsim.xml | 79 +++++++++++++++++-- .../5g_rfsimulator_flexric/docker-compose.yml | 14 ++-- 3 files changed, 97 insertions(+), 15 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index c94b0c8dfb..cd07851319 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -47,6 +47,7 @@ import helpreadme as HELP import constants as CONST import cls_oaicitest from cls_ci_helper import archiveArtifact +from collections import deque #----------------------------------------------------------- # Helper functions used here and in other classes @@ -216,6 +217,24 @@ def CheckLogs(self, filename, HTML, RAN): else: HTML.CreateHtmlTestRowQueue(opt, 'OK', [HTML.htmleNBFailureMsg]) HTML.htmleNBFailureMsg = "" + elif 'xapp' in name: + opt = f"Undeploy {name}" + with open(f'{filename}', "r") as f: + last_line = deque(f, maxlen=1).pop() + if ('Test xApp run SUCCESSFULLY' in last_line): + HTML.CreateHtmlTestRowQueue(opt, 'OK', ["xApp run successfully"]) + else: + HTML.CreateHtmlTestRowQueue(opt, 'KO', ["xApp didn't run successfully"]) + success = False + elif 'RIC' in name: + opt = f"Undeploy {name}" + with open(f'{filename}', 'r') as f: + last_line = deque(f, maxlen=1).pop() + if ('Removing E2 Node' in last_line): + HTML.CreateHtmlTestRowQueue(opt, 'OK', ["nearRT-RIC run successfully"]) + else: + HTML.CreateHtmlTestRowQueue(opt, 'KO', ["nearRT-RIC didn't run successfully"]) + success = False else: logging.info(f"Skipping analysis of log '{filename}': no submatch for xNB/UE") logging.debug(f"log check: file {filename} passed analysis {success}") diff --git a/ci-scripts/xml_files/container_5g_flexric_rfsim.xml b/ci-scripts/xml_files/container_5g_flexric_rfsim.xml index 536952a2bc..135eab6b68 100644 --- a/ci-scripts/xml_files/container_5g_flexric_rfsim.xml +++ b/ci-scripts/xml_files/container_5g_flexric_rfsim.xml @@ -42,6 +42,13 @@ 002006 444444 100002 + 100003 + 100004 + 100005 + 100006 + 100007 + 100008 + 100009 100001 222221 222222 @@ -95,10 +102,10 @@ Deploy_Object - Deploy OAI FlexRIC + Deploy OAI nearRT-RIC localhost ci-scripts/yaml_files/5g_rfsimulator_flexric - oai-flexric + nearRT-RIC @@ -151,16 +158,72 @@ + Stop_Object + Stop RC monitoring xApp + localhost + ci-scripts/yaml_files/5g_rfsimulator_flexric + xapp-rc-moni + + + + Stop_Object + Stop KPM monitoring xApp + localhost + ci-scripts/yaml_files/5g_rfsimulator_flexric + xapp-kpm-moni + + + + Stop_Object + Stop KPM monitoring and RC control xApp + localhost + ci-scripts/yaml_files/5g_rfsimulator_flexric + xapp-kpm-rc + + + + Stop_Object + Stop custom SMs monitoring xApp + localhost + ci-scripts/yaml_files/5g_rfsimulator_flexric + xapp-gtp-mac-rlc-pdcp-moni + + + + Stop_Object + Stop OAI UEs + localhost + ci-scripts/yaml_files/5g_rfsimulator_flexric + oai-nr-ue oai-nr-ue2 + + + + Stop_Object + Stop OAI gNB + localhost + ci-scripts/yaml_files/5g_rfsimulator_flexric + oai-gnb + + + + Stop_Object + Stop nearRT-RIC + localhost + ci-scripts/yaml_files/5g_rfsimulator_flexric + nearRT-RIC + + + Undeploy_Object - Undeploy RAN and Flexric + Undeploy RAN and FlexRIC localhost ci-scripts/yaml_files/5g_rfsimulator_flexric true - + Deploy_Object - RC monitoring + Deploy RC monitoring localhost ci-scripts/yaml_files/5g_rfsimulator_flexric xapp-rc-moni @@ -168,7 +231,7 @@ Deploy_Object - KPM monitoring + Deploy KPM monitoring localhost ci-scripts/yaml_files/5g_rfsimulator_flexric xapp-kpm-moni @@ -176,7 +239,7 @@ Deploy_Object - KPM monitoring and RC control + Deploy KPM monitoring and RC control localhost ci-scripts/yaml_files/5g_rfsimulator_flexric xapp-kpm-rc @@ -184,7 +247,7 @@ Deploy_Object - Custom SMs monitoring + Deploy custom SMs monitoring localhost ci-scripts/yaml_files/5g_rfsimulator_flexric xapp-gtp-mac-rlc-pdcp-moni diff --git a/ci-scripts/yaml_files/5g_rfsimulator_flexric/docker-compose.yml b/ci-scripts/yaml_files/5g_rfsimulator_flexric/docker-compose.yml index 8e1688dff3..5bb8d0246d 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_flexric/docker-compose.yml +++ b/ci-scripts/yaml_files/5g_rfsimulator_flexric/docker-compose.yml @@ -1,7 +1,7 @@ services: - oai-flexric: + nearRT-RIC: image: ${REGISTRY-oaisoftwarealliance/}oai-flexric:${FLEXRIC_TAG:-develop} - container_name: oai-flexric + container_name: nearRT-RIC networks: public_net: ipv4_address: 192.168.70.150 @@ -17,7 +17,7 @@ services: command: "stdbuf -o0 /usr/local/flexric/xApp/c/monitor/xapp_rc_moni" container_name: xapp-rc-moni depends_on: - - oai-flexric + - nearRT-RIC - oai-gnb networks: public_net: @@ -34,7 +34,7 @@ services: command: "stdbuf -o0 /usr/local/flexric/xApp/c/monitor/xapp_kpm_moni" container_name: xapp-kpm-moni depends_on: - - oai-flexric + - nearRT-RIC - oai-gnb networks: public_net: @@ -51,7 +51,7 @@ services: command: "stdbuf -o0 /usr/local/flexric/xApp/c/kpm_rc/xapp_kpm_rc" container_name: xapp-kpm-rc depends_on: - - oai-flexric + - nearRT-RIC - oai-gnb networks: public_net: @@ -68,7 +68,7 @@ services: command: "stdbuf -o0 /usr/local/flexric/xApp/c/monitor/xapp_gtp_mac_rlc_pdcp_moni" container_name: xapp-gtp-mac-rlc-pdcp-moni depends_on: - - oai-flexric + - nearRT-RIC - oai-gnb networks: public_net: @@ -88,7 +88,7 @@ services: USE_ADDITIONAL_OPTIONS: -E --rfsim --log_config.global_log_options level,nocolor,time ASAN_OPTIONS: detect_leaks=0 depends_on: - - oai-flexric + - nearRT-RIC networks: public_net: ipv4_address: 192.168.70.151 From 090cf00c76b3d9cdf16ca2987044593f425c95ba Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 12 Nov 2025 16:14:11 +0100 Subject: [PATCH 220/275] Simplify logging UTC option to only show time We probably almost never care about the date, but it takes a lot of space. Remove it; if somebody needs the full time stamp including the date, we could still add it with a new option. --- common/utils/LOG/log.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/utils/LOG/log.c b/common/utils/LOG/log.c index 91c97ac1ac..8e332b7797 100644 --- a/common/utils/LOG/log.c +++ b/common/utils/LOG/log.c @@ -575,8 +575,7 @@ static inline int log_header(log_component_t *c, struct tm utc_time; if (gmtime_r(&t.tv_sec, &utc_time) == NULL) abort(); - snprintf(timeString, sizeof(timeString), "%04d-%02d-%02d %02d:%02d:%02d.%06lu UTC ", - utc_time.tm_year + 1900, utc_time.tm_mon + 1, utc_time.tm_mday, + snprintf(timeString, sizeof(timeString), "[%02d:%02d:%02d.%06lu] ", utc_time.tm_hour, utc_time.tm_min, utc_time.tm_sec, t.tv_nsec / 1000); } else { snprintf(timeString, sizeof(timeString), "%lu.%06lu ", From 7f57019128b5d4500fd6bec80ae4e9d93a478ef3 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 14 Nov 2025 16:01:07 +0100 Subject: [PATCH 221/275] log: Only print one space if no level selected Prior to this change, when no "level" option is given to the logging module, we see [PHY] RU 0 RF started cpu_meas_enabled 0 [HW] No connected device, generating void samples... with three spaces, which is too much. This change reduces the amount of spaces to 1 without log level. Co-authored-by: Robert Schmidt --- common/utils/LOG/log.c | 29 +++++++++++++++++------------ common/utils/LOG/log.h | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/common/utils/LOG/log.c b/common/utils/LOG/log.c index 8e332b7797..bb7d0482cf 100644 --- a/common/utils/LOG/log.c +++ b/common/utils/LOG/log.c @@ -509,12 +509,15 @@ int logInit (void) for (int i = 0; i < MAX_LOG_PREDEF_COMPONENTS; i++) register_log_component(comp_name[i], comp_extension[i], i); - for (int i=0 ; log_level_names[i].name != NULL ; i++) - g_log->level2string[i] = toupper(log_level_names[i].name[0]); // uppercased first letter of level name - g_log->filelog_name = "/tmp/openair.log"; log_getconfig(g_log); + for (int i = 0; log_level_names[i].name != NULL; i++) + if (g_log->flag & FLAG_LEVEL) + snprintf(g_log->level2string[i], sizeof g_log->level2string[i], " %c ", toupper(log_level_names[i].name[0])); + else + snprintf(g_log->level2string[i], sizeof g_log->level2string[i], " "); + // set all unused component items to 0, they are for non predefined components for (int i=MAX_LOG_PREDEF_COMPONENTS; i < MAX_LOG_COMPONENTS; i++) { memset(&(g_log->log_component[i]),0,sizeof(log_component_t)); @@ -592,15 +595,17 @@ static inline int log_header(log_component_t *c, } else { threadIdString[0] = 0; } - return snprintf(log_buffer, buffsize, "%s%s%s[%s] %c %s%s", - flag & FLAG_NOCOLOR ? "" : log_level_highlight_start[level], - timeString, - threadIdString, - c->name, - flag & FLAG_LEVEL ? g_log->level2string[level] : ' ', - l, - threadname - ); + + return snprintf(log_buffer, + buffsize, + "%s%s%s[%s]%s%s%s", + flag & FLAG_NOCOLOR ? "" : log_level_highlight_start[level], + timeString, + threadIdString, + c->name, + g_log->level2string[level], // will print space if no level selected + l, + threadname); } void logRecord_mt(const char *file, diff --git a/common/utils/LOG/log.h b/common/utils/LOG/log.h index c873b0d418..37f903c597 100644 --- a/common/utils/LOG/log.h +++ b/common/utils/LOG/log.h @@ -217,7 +217,7 @@ typedef struct { typedef struct { log_component_t log_component[MAX_LOG_COMPONENTS]; log_component_back_t log_rarely_used[MAX_LOG_COMPONENTS]; - char level2string[NUM_LOG_LEVEL]; + char level2string[NUM_LOG_LEVEL][4]; int flag; char *filelog_name; debug_flags_t debug_mask; From ea1aa382c3932606b388a599bfc7e374d1581e6d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 14 Nov 2025 17:05:49 +0100 Subject: [PATCH 222/275] Update log header format to improve visual alignment The new format left-aligns the component name and enforces a minimum width of 7 (log component) characters, padding shorter names with spaces after the closing "]". Example output: [14:05:43.395376] [E1AP] I releasing UE 1 [14:05:43.395400] [GTPU] I [91] UE ID 1: Delete all tunnels (1 tunnels) [14:05:43.395414] [NR_RRC] A [DL] (cellID bc614e, UE ID 1 RNTI 8e27) Send RRC Release [14:05:43.395431] [RRC] I UE 1: received bearer release complete [14:05:43.424551] [RLC] W Remove UE 36391 [14:05:43.424597] [NR_RRC] I removed UE CU UE ID 1/RNTI 8e27 [14:05:43.424773] [NR_RRC] A [--] (cellID bc614e, UE ID 1 RNTI 8e27) Remove UE context [14:05:43.425205] [NR_MAC] I Remove NR rnti 0x8e27 [14:05:43.582158] [HW] W Lost socket ^C ** Caught SIGTERM, shutting down Returned from ITTI signal handler [14:05:43.948721] [GNB_APP] I stopping nr-softmodem [14:05:43.948746] [PHY] I Killing gNB 0 processing threads [14:05:44.574818] [PHY] I Stopping RU 0 processing threads [14:05:44.575000] [PHY] I RU 0 RF device stopped [14:05:44.575006] [GNB_APP] I turned off RU rfdevice --- common/utils/LOG/log.c | 16 +++++++++++----- common/utils/LOG/log.h | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/common/utils/LOG/log.c b/common/utils/LOG/log.c index bb7d0482cf..354fae2fb1 100644 --- a/common/utils/LOG/log.c +++ b/common/utils/LOG/log.c @@ -461,9 +461,15 @@ int register_log_component(const char *name, const char *fext, int compidx) } if (computed_compidx >= 0 && computed_compidx log_component[computed_compidx].name = strdup(name); - g_log->log_component[computed_compidx].stream = stdout; - g_log->log_component[computed_compidx].filelog = 0; + log_component_t *c = &g_log->log_component[computed_compidx]; + c->name = strdup(name); + int n = snprintf(c->headerName, sizeof(c->headerName), "[%s", c->name); + if (n >= sizeof(c->headerName) - 1) // snprintf() truncated + n = sizeof(c->headerName) - 2; + c->headerName[n] = ']'; + c->headerName[n + 1] = 0; + c->stream = stdout; + c->filelog = 0; g_log->log_rarely_used[computed_compidx].filelog_name = calloc(1, strlen(name) + 16); /* /tmp/.%s */ sprintf(g_log->log_rarely_used[computed_compidx].filelog_name, "/tmp/%s.", name); strncat(g_log->log_rarely_used[computed_compidx].filelog_name, fext, 3); @@ -598,11 +604,11 @@ static inline int log_header(log_component_t *c, return snprintf(log_buffer, buffsize, - "%s%s%s[%s]%s%s%s", + "%s%s%s%-8s%s%s%s", flag & FLAG_NOCOLOR ? "" : log_level_highlight_start[level], timeString, threadIdString, - c->name, + c->headerName, g_log->level2string[level], // will print space if no level selected l, threadname); diff --git a/common/utils/LOG/log.h b/common/utils/LOG/log.h index 37f903c597..312f2c9510 100644 --- a/common/utils/LOG/log.h +++ b/common/utils/LOG/log.h @@ -206,6 +206,7 @@ typedef struct { typedef struct { const char *name; + char headerName[19]; int level; int filelog; FILE *stream; From 875ec96fce0b3ac0f15dde5abdab8ad34c4a90cf Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 14 Nov 2025 17:07:13 +0100 Subject: [PATCH 223/275] When logging function names, align to 32 characters --- common/utils/LOG/log.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/utils/LOG/log.c b/common/utils/LOG/log.c index 354fae2fb1..23aa075ca1 100644 --- a/common/utils/LOG/log.c +++ b/common/utils/LOG/log.c @@ -604,12 +604,13 @@ static inline int log_header(log_component_t *c, return snprintf(log_buffer, buffsize, - "%s%s%s%-8s%s%s%s", + "%s%s%s%-8s%s%-*s%s", flag & FLAG_NOCOLOR ? "" : log_level_highlight_start[level], timeString, threadIdString, c->headerName, g_log->level2string[level], // will print space if no level selected + l[0] == 0 ? 0 : 32, l, threadname); } From f4ff6c756c392d202d86b06897d689b73245d9a7 Mon Sep 17 00:00:00 2001 From: francescomani Date: Sat, 8 Nov 2025 14:41:42 +0100 Subject: [PATCH 224/275] add a check for rlc-Config presence (even if it should be mandatory at setup) --- openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 2 +- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 5 ++--- openair2/RRC/NR_UE/rrc_UE.c | 9 +++++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index 064857a8e3..ce59a31226 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -329,7 +329,7 @@ static int handle_ue_context_drbs_setup(NR_UE_info_t *UE, *rlc_BearerConfig->rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength = NR_SN_FieldLengthAM_size12; *rlc_BearerConfig->rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength = NR_SN_FieldLengthAM_size12; } - + AssertFatal(rlc_BearerConfig->rlc_Config, "We expect rlc-Config to be always present when we configure a DRB\n"); nr_rlc_add_drb(UE->rnti, drb->id, rlc_BearerConfig); nr_lc_config_t c = {.lcid = rlc_BearerConfig->logicalChannelIdentity, .nssai = drb->nr.nssai}; diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c index e187541fd5..126f9fb51c 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -672,7 +672,7 @@ void nr_rlc_reconfigure_entity(int ue_id, int lc_id, NR_RLC_Config_t *rlc_Config void nr_rlc_add_srb(int ue_id, int srb_id, const NR_RLC_BearerConfig_t *rlc_BearerConfig) { - struct NR_RLC_Config *r = rlc_BearerConfig->rlc_Config; + NR_RLC_Config_t *r = rlc_BearerConfig->rlc_Config; int t_status_prohibit; int t_poll_retransmit; int poll_pdu; @@ -682,8 +682,7 @@ void nr_rlc_add_srb(int ue_id, int srb_id, const NR_RLC_BearerConfig_t *rlc_Bear int sn_field_length; LOG_D(RLC, "Trying to add SRB %d\n", srb_id); - AssertFatal(srb_id > 0 && srb_id < 4, - "Invalid srb id %d\n", srb_id); + AssertFatal(srb_id > 0 && srb_id < 4, "Invalid srb id %d\n", srb_id); if (r && r->present == NR_RLC_Config_PR_am) { struct NR_RLC_Config__am *am; diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 9086ad25eb..4bb0e8bdb3 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -1684,8 +1684,13 @@ static void nr_rrc_manage_rlc_bearers(NR_UE_RRC_INST_t *rrc, const NR_CellGroupC nr_rlc_set_rlf_handler(rrc->ue_id, nr_rrc_signal_maxrtxindication); } else { // DRB NR_DRB_Identity_t drb_id = rlc_bearer->servedRadioBearer->choice.drb_Identity; - nr_rlc_add_drb(rrc->ue_id, drb_id, rlc_bearer); - nr_rlc_set_rlf_handler(rrc->ue_id, nr_rrc_signal_maxrtxindication); + if (!rlc_bearer->rlc_Config) { + LOG_E(RLC, "RLC-Config not present but is mandatory for setup\n"); + rrc->active_RLC_entity[lcid] = false; + } else { + nr_rlc_add_drb(rrc->ue_id, drb_id, rlc_bearer); + nr_rlc_set_rlf_handler(rrc->ue_id, nr_rrc_signal_maxrtxindication); + } } } } From 566da5317fad6d2c2c3d9877f87ae07ffa084cf4 Mon Sep 17 00:00:00 2001 From: francescomani Date: Sat, 8 Nov 2025 14:48:31 +0100 Subject: [PATCH 225/275] add a check for cell ID presence in handling reconfigurationWithSync --- openair2/RRC/NR_UE/rrc_UE.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 4bb0e8bdb3..818bb26be3 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -1718,7 +1718,10 @@ static void nr_rrc_process_reconfigurationWithSync(NR_UE_RRC_INST_t *rrc, rrc->arfcn_ssb = *dcc->frequencyInfoDL->absoluteFrequencySSB; // consider the target SpCell to be one with a physical cell identity indicated by the physCellId - rrc->phyCellID = *reconfigurationWithSync->spCellConfigCommon->physCellId; + if (!reconfigurationWithSync->spCellConfigCommon->physCellId) + LOG_E(NR_RRC, "physCellId absent but should be mandatory present upon cell change and cell addition\n"); + else + rrc->phyCellID = *reconfigurationWithSync->spCellConfigCommon->physCellId; } NR_UE_Timers_Constants_t *tac = &rrc->timers_and_constants; From 88baba8948cc2b9b7f3c8ccc8d9f93c3d7922013 Mon Sep 17 00:00:00 2001 From: francescomani Date: Sat, 8 Nov 2025 14:52:53 +0100 Subject: [PATCH 226/275] do not handle not decoded cellGroupConfig (issue #1031) --- openair2/RRC/NR_UE/rrc_UE.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 818bb26be3..a1b8616c60 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -1161,20 +1161,19 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_ LOG_E(NR_RRC, "\n"); // free the memory SEQUENCE_free(&asn_DEF_NR_CellGroupConfig, (void *)cellGroupConfig, 1); + } else { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) + xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig); + + nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index); + AssertFatal(!IS_SA_MODE(get_softmodem_params()), "secondaryCellGroup only used in NSA for now\n"); + nr_mac_rrc_message_t rrc_msg = {0}; + rrc_msg.payload_type = NR_MAC_RRC_CONFIG_CG; + nr_mac_rrc_config_cg_t *config_cg = &rrc_msg.payload.config_cg; + config_cg->cellGroupConfig = cellGroupConfig; + config_cg->UE_NR_Capability = rrc->UECap.UE_NR_Capability; + nr_rrc_send_msg_to_mac(rrc, &rrc_msg); } - - if (LOG_DEBUGFLAG(DEBUG_ASN1)) - xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig); - - nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index); - - AssertFatal(!IS_SA_MODE(get_softmodem_params()), "secondaryCellGroup only used in NSA for now\n"); - nr_mac_rrc_message_t rrc_msg = {0}; - rrc_msg.payload_type = NR_MAC_RRC_CONFIG_CG; - nr_mac_rrc_config_cg_t *config_cg = &rrc_msg.payload.config_cg; - config_cg->cellGroupConfig = cellGroupConfig; - config_cg->UE_NR_Capability = rrc->UECap.UE_NR_Capability; - nr_rrc_send_msg_to_mac(rrc, &rrc_msg); } if (ie->measConfig) { LOG_I(NR_RRC, "RRCReconfiguration includes Measurement Configuration\n"); From 9c3c65f5c943575ada45e3b4809912678854e00b Mon Sep 17 00:00:00 2001 From: francescomani Date: Sat, 8 Nov 2025 15:00:38 +0100 Subject: [PATCH 227/275] check presence of optional nonCriticalExtension before handling (issue #1026) --- openair2/RRC/NR_UE/rrc_UE.c | 46 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index a1b8616c60..177ef621b3 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -826,28 +826,30 @@ static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCRe NR_RRCReconfiguration_v1540_IEs_t *rec_1540 = rec_1530->nonCriticalExtension; if (rec_1540) { NR_RRCReconfiguration_v1560_IEs_t *rec_1560 = rec_1540->nonCriticalExtension; - if (rec_1560->sk_Counter) { - // TODO perform AS security key update procedure as specified in 5.3.5.7 - LOG_E(NR_RRC, "RRCReconfiguration includes sk-Counter but this is not implemented yet\n"); - } - if (rec_1560->mrdc_SecondaryCellGroupConfig) { - // TODO perform handling of mrdc-SecondaryCellGroupConfig as specified in 5.3.5.3 - LOG_E(NR_RRC, "RRCReconfiguration includes mrdc-SecondaryCellGroupConfig but this is not handled yet\n"); - } - if (rec_1560->radioBearerConfig2) { - NR_RadioBearerConfig_t *RadioBearerConfig = NULL; - asn_dec_rval_t dec_rval = uper_decode(NULL, - &asn_DEF_NR_RadioBearerConfig, - (void **)&RadioBearerConfig, - (uint8_t *)rec_1560->radioBearerConfig2->buf, - rec_1560->radioBearerConfig2->size, - 0, - 0); - if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { - LOG_E(NR_RRC, "radioBearerConfig2 decode error\n"); - SEQUENCE_free(&asn_DEF_NR_RadioBearerConfig, RadioBearerConfig, 1); - } else - nr_rrc_ue_process_RadioBearerConfig(rrc, RadioBearerConfig); + if (rec_1560) { + if (rec_1560->sk_Counter) { + // TODO perform AS security key update procedure as specified in 5.3.5.7 + LOG_E(NR_RRC, "RRCReconfiguration includes sk-Counter but this is not implemented yet\n"); + } + if (rec_1560->mrdc_SecondaryCellGroupConfig) { + // TODO perform handling of mrdc-SecondaryCellGroupConfig as specified in 5.3.5.3 + LOG_E(NR_RRC, "RRCReconfiguration includes mrdc-SecondaryCellGroupConfig but this is not handled yet\n"); + } + if (rec_1560->radioBearerConfig2) { + NR_RadioBearerConfig_t *RadioBearerConfig = NULL; + asn_dec_rval_t dec_rval = uper_decode(NULL, + &asn_DEF_NR_RadioBearerConfig, + (void **)&RadioBearerConfig, + (uint8_t *)rec_1560->radioBearerConfig2->buf, + rec_1560->radioBearerConfig2->size, + 0, + 0); + if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { + LOG_E(NR_RRC, "radioBearerConfig2 decode error\n"); + SEQUENCE_free(&asn_DEF_NR_RadioBearerConfig, RadioBearerConfig, 1); + } else + nr_rrc_ue_process_RadioBearerConfig(rrc, RadioBearerConfig); + } } } } From 789564fa8eddc5da61abc3d3781938b56abf636e Mon Sep 17 00:00:00 2001 From: francescomani Date: Sat, 8 Nov 2025 15:09:58 +0100 Subject: [PATCH 228/275] add UE capability container for each element of request list (issue #1025) --- openair2/RRC/NR_UE/rrc_UE.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 177ef621b3..6b7688c19c 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -2247,13 +2247,7 @@ static void nr_rrc_ue_process_ueCapabilityEnquiry(NR_UE_RRC_INST_t *rrc, NR_UECa AssertFatal (enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); rrc->UECap.sdu_size = (enc_rval.encoded + 7) / 8; LOG_I(PHY, "[RRC]UE NR Capability encoded, %d bytes (%zd bits)\n", rrc->UECap.sdu_size, enc_rval.encoded + 7); - /* RAT Container */ - NR_UE_CapabilityRAT_Container_t *ue_CapabilityRAT_Container = CALLOC(1, sizeof(NR_UE_CapabilityRAT_Container_t)); - ue_CapabilityRAT_Container->rat_Type = NR_RAT_Type_nr; - OCTET_STRING_fromBuf(&ue_CapabilityRAT_Container->ue_CapabilityRAT_Container, (const char *)rrc->UECap.sdu, rrc->UECap.sdu_size); NR_UECapabilityEnquiry_IEs_t *ueCapabilityEnquiry_ie = UECapabilityEnquiry->criticalExtensions.choice.ueCapabilityEnquiry; - // ue_CapabilityRAT_Container.ueCapabilityRAT_Container.buf = UE_rrc_inst[ue_mod_idP].UECapability; - // ue_CapabilityRAT_Container.ueCapabilityRAT_Container.size = UE_rrc_inst[ue_mod_idP].UECapability_size; AssertFatal(UECapabilityEnquiry->criticalExtensions.present == NR_UECapabilityEnquiry__criticalExtensions_PR_ueCapabilityEnquiry, "UECapabilityEnquiry->criticalExtensions.present (%d) != UECapabilityEnquiry__criticalExtensions_PR_c1 (%d)\n", UECapabilityEnquiry->criticalExtensions.present,NR_UECapabilityEnquiry__criticalExtensions_PR_ueCapabilityEnquiry); @@ -2266,6 +2260,10 @@ static void nr_rrc_ue_process_ueCapabilityEnquiry(NR_UE_RRC_INST_t *rrc, NR_UECa for (int i = 0; i < ueCapabilityEnquiry_ie->ue_CapabilityRAT_RequestList.list.count; i++) { if (ueCapabilityEnquiry_ie->ue_CapabilityRAT_RequestList.list.array[i]->rat_Type == NR_RAT_Type_nr) { + /* RAT Container */ + NR_UE_CapabilityRAT_Container_t *ue_CapabilityRAT_Container = CALLOC(1, sizeof(NR_UE_CapabilityRAT_Container_t)); + ue_CapabilityRAT_Container->rat_Type = NR_RAT_Type_nr; + OCTET_STRING_fromBuf(&ue_CapabilityRAT_Container->ue_CapabilityRAT_Container, (const char *)rrc->UECap.sdu, rrc->UECap.sdu_size); asn1cSeqAdd(&UEcapList->list, ue_CapabilityRAT_Container); uint8_t buffer[500]; asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, NULL, (void *)&ul_dcch_msg, buffer, 500); @@ -2279,8 +2277,7 @@ static void nr_rrc_ue_process_ueCapabilityEnquiry(NR_UE_RRC_INST_t *rrc, NR_UECa nr_pdcp_data_req_srb(rrc->ue_id, srb_id, 0, (enc_rval.encoded + 7) / 8, buffer, deliver_pdu_srb_rlc, NULL); } } - /* Free struct members after it's done - including locally allocated ue_CapabilityRAT_Container */ + /* Free struct members after it's done including locally allocated ue_CapabilityRAT_Container */ ASN_STRUCT_RESET(asn_DEF_NR_UL_DCCH_Message, &ul_dcch_msg); } From 8620f33a58a9883b5159c81d7a0141988328213f Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 14 Nov 2025 12:33:14 +0100 Subject: [PATCH 229/275] Provide public helper functions to create NR_RLC_Config from nr_rlc_configuration_t --- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c | 43 +++++++++++--------- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h | 3 ++ 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index 99bf846d4d..dff2f2b663 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -3538,6 +3538,24 @@ static NR_SpCellConfig_t *get_initial_SpCellConfig(int uid, return SpCellConfig; } +struct NR_RLC_Config *nr_srb_config(const nr_rlc_configuration_t *default_rlc_config) +{ + NR_RLC_Config_t *rlc_Config = calloc(1, sizeof(NR_RLC_Config_t)); + rlc_Config->present = NR_RLC_Config_PR_am; + rlc_Config->choice.am = calloc(1, sizeof(*rlc_Config->choice.am)); + rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); + *(rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength) = encode_sn_field_length_am(default_rlc_config->srb.sn_field_length); + rlc_Config->choice.am->dl_AM_RLC.t_Reassembly = encode_t_reassembly(default_rlc_config->srb.t_reassembly); + rlc_Config->choice.am->dl_AM_RLC.t_StatusProhibit = encode_t_status_prohibit(default_rlc_config->srb.t_status_prohibit); + rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); + *(rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength) = encode_sn_field_length_am(default_rlc_config->srb.sn_field_length); + rlc_Config->choice.am->ul_AM_RLC.t_PollRetransmit = encode_t_poll_retransmit(default_rlc_config->srb.t_poll_retransmit); + rlc_Config->choice.am->ul_AM_RLC.pollPDU = encode_poll_pdu(default_rlc_config->srb.poll_pdu); + rlc_Config->choice.am->ul_AM_RLC.pollByte = encode_poll_byte(default_rlc_config->srb.poll_byte); + rlc_Config->choice.am->ul_AM_RLC.maxRetxThreshold = encode_max_retx_threshold(default_rlc_config->srb.max_retx_threshold); + return rlc_Config; +} + NR_RLC_BearerConfig_t *get_SRB_RLC_BearerConfig(long channelId, long priority, long bucketSizeDuration, @@ -3551,20 +3569,7 @@ NR_RLC_BearerConfig_t *get_SRB_RLC_BearerConfig(long channelId, rlc_BearerConfig->servedRadioBearer->choice.srb_Identity = channelId; rlc_BearerConfig->reestablishRLC = NULL; - NR_RLC_Config_t *rlc_Config = calloc(1, sizeof(NR_RLC_Config_t)); - rlc_Config->present = NR_RLC_Config_PR_am; - rlc_Config->choice.am = calloc(1, sizeof(*rlc_Config->choice.am)); - rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); - *(rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength) = encode_sn_field_length_am(default_rlc_config->srb.sn_field_length); - rlc_Config->choice.am->dl_AM_RLC.t_Reassembly = encode_t_reassembly(default_rlc_config->srb.t_reassembly); - rlc_Config->choice.am->dl_AM_RLC.t_StatusProhibit = encode_t_status_prohibit(default_rlc_config->srb.t_status_prohibit); - rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t)); - *(rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength) = encode_sn_field_length_am(default_rlc_config->srb.sn_field_length); - rlc_Config->choice.am->ul_AM_RLC.t_PollRetransmit = encode_t_poll_retransmit(default_rlc_config->srb.t_poll_retransmit); - rlc_Config->choice.am->ul_AM_RLC.pollPDU = encode_poll_pdu(default_rlc_config->srb.poll_pdu); - rlc_Config->choice.am->ul_AM_RLC.pollByte = encode_poll_byte(default_rlc_config->srb.poll_byte); - rlc_Config->choice.am->ul_AM_RLC.maxRetxThreshold = encode_max_retx_threshold(default_rlc_config->srb.max_retx_threshold); - rlc_BearerConfig->rlc_Config = rlc_Config; + rlc_BearerConfig->rlc_Config = nr_srb_config(default_rlc_config); NR_LogicalChannelConfig_t *logicalChannelConfig = calloc(1, sizeof(NR_LogicalChannelConfig_t)); logicalChannelConfig->ul_SpecificParameters = calloc(1, sizeof(*logicalChannelConfig->ul_SpecificParameters)); @@ -3584,10 +3589,9 @@ NR_RLC_BearerConfig_t *get_SRB_RLC_BearerConfig(long channelId, return rlc_BearerConfig; } -static void nr_drb_config(struct NR_RLC_Config *rlc_Config, - NR_RLC_Config_PR rlc_config_pr, - const nr_rlc_configuration_t *default_rlc_config) +struct NR_RLC_Config *nr_drb_config(NR_RLC_Config_PR rlc_config_pr, const nr_rlc_configuration_t *default_rlc_config) { + NR_RLC_Config_t *rlc_Config = calloc(1, sizeof(NR_RLC_Config_t)); switch (rlc_config_pr) { case NR_RLC_Config_PR_um_Bi_Directional: // RLC UM Bi-directional Bearer configuration @@ -3620,6 +3624,7 @@ static void nr_drb_config(struct NR_RLC_Config *rlc_Config, break; } rlc_Config->present = rlc_config_pr; + return rlc_Config; } NR_RLC_BearerConfig_t *get_DRB_RLC_BearerConfig(long lcChannelId, @@ -3635,9 +3640,7 @@ NR_RLC_BearerConfig_t *get_DRB_RLC_BearerConfig(long lcChannelId, rlc_BearerConfig->servedRadioBearer->choice.drb_Identity = drbId; rlc_BearerConfig->reestablishRLC = NULL; - NR_RLC_Config_t *rlc_Config = calloc(1, sizeof(NR_RLC_Config_t)); - nr_drb_config(rlc_Config, rlc_conf, default_rlc_config); - rlc_BearerConfig->rlc_Config = rlc_Config; + rlc_BearerConfig->rlc_Config = nr_drb_config(rlc_conf, default_rlc_config); NR_LogicalChannelConfig_t *logicalChannelConfig = calloc(1, sizeof(NR_LogicalChannelConfig_t)); logicalChannelConfig->ul_SpecificParameters = calloc(1, sizeof(*logicalChannelConfig->ul_SpecificParameters)); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h index 89ce03e7f5..466f18c0de 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h @@ -112,6 +112,9 @@ NR_CellGroupConfig_t *get_default_secondaryCellGroup(const NR_ServingCellConfigC NR_ReconfigurationWithSync_t *get_reconfiguration_with_sync(rnti_t rnti, uid_t uid, const NR_ServingCellConfigCommon_t *scc, int frame); +struct NR_RLC_Config *nr_srb_config(const nr_rlc_configuration_t *default_rlc_config); +struct NR_RLC_Config *nr_drb_config(NR_RLC_Config_PR rlc_config_pr, const nr_rlc_configuration_t *default_rlc_config); + NR_RLC_BearerConfig_t *get_SRB_RLC_BearerConfig(long channelId, long priority, long bucketSizeDuration, From eb7ca9663cd87a7e68bfcdcf8a032fae244ec9d9 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 14 Nov 2025 12:45:37 +0100 Subject: [PATCH 230/275] gNB: modify RLC configuration applied for SRB1 during RRCReestablishment TS 38.331 clause 5.3.7.4 specifies to apply the configuration defined in 9.2.1 for SRB1 at UE side. This implies that the gNB also has to apply sn_field_length = 12, but there are no implications on the other RLC parameters. As wrong timer values break SRB1 e.g. for long RTT NTN scenarios, this commit switches to using the RLC timer values provided in the configuration file. Currently we still set poll_pdu, poll_byte and max_retx_threshold to the values defined in 9.2.1, but this can be changed. --- openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index 064857a8e3..d353ab9e3f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -980,8 +980,15 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc) pthread_mutex_unlock(&mac->sched_lock); nr_rlc_remove_ue(dl_rrc->gNB_DU_ue_id); nr_rlc_update_id(*dl_rrc->old_gNB_DU_ue_id, dl_rrc->gNB_DU_ue_id); - /* 38.331 clause 5.3.7.4: apply the specified configuration defined in 9.2.1 for SRB1 */ - nr_rlc_reconfigure_entity(dl_rrc->gNB_DU_ue_id, 1, NULL); + /* 38.331 clause 5.3.7.4: apply gNB RLC configuration for SRB1 to match the UE RLC configuration defined in 9.2.1 */ + nr_rlc_configuration_t rlc_configuration = mac->rlc_config; // use configuration file values for timers t_poll_retransmit, t_reassembly and t_status_prohibit + rlc_configuration.srb.poll_pdu = -1; + rlc_configuration.srb.poll_byte = -1; + rlc_configuration.srb.max_retx_threshold = 8; + rlc_configuration.srb.sn_field_length = 12; + NR_RLC_Config_t *rlc_Config = nr_srb_config(&rlc_configuration); + nr_rlc_reconfigure_entity(dl_rrc->gNB_DU_ue_id, 1, rlc_Config); + ASN_STRUCT_FREE(asn_DEF_NR_RLC_Config, rlc_Config); instance_t f1inst = get_f1_gtp_instance(); if (f1inst >= 0) // we actually use F1-U gtpv1u_update_ue_id(f1inst, *dl_rrc->old_gNB_DU_ue_id, dl_rrc->gNB_DU_ue_id); From e17c165c9d17dd4c9a2d82b1e0a998b8df51566d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Nov 2025 22:44:48 +0100 Subject: [PATCH 231/275] Move DLSCH/PDSCH variables to gNB The next commit will remove msgDataTx. We can therefore not use msgDataTx to retain the PDSCH/DLSCH variables, and move them to the gNB. Note that the only reason for NR_gNB_DLSCH_t after this commit is to hold various large buffers (c, b, f). Future work could be done to remove this, in which case the array inside PHY_VARS_gNB could be deleted. On this occasion, clean up the use of init_DLSCH_struct() and reset_DLSCH_struct() (renamed destroy_DLSCH_struct()) and centralize their call in phy_init_nr_gNB()/phy_free_nr_gNB(), and correct simulators accordingly. --- executables/nr-gnb.c | 1 - openair1/PHY/INIT/nr_init.c | 21 +++++++++++++-------- openair1/PHY/INIT/nr_phy_init.h | 3 --- openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c | 4 ++-- openair1/PHY/defs_gNB.h | 3 ++- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 2 +- openair1/SIMULATION/NR_PHY/dlschsim.c | 8 +++----- openair1/SIMULATION/NR_PHY/dlsim.c | 4 ++-- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index c4ad405655..7c52d67c97 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -361,7 +361,6 @@ void init_gNB_Tpool(int inst) notifiedFIFO_elt_t *msgL1Tx = newNotifiedFIFO_elt(sizeof(processingData_L1tx_t), 0, &gNB->L1_tx_out, NULL); processingData_L1tx_t *msgDataTx = (processingData_L1tx_t *)NotifiedFifoData(msgL1Tx); memset(msgDataTx, 0, sizeof(processingData_L1tx_t)); - init_DLSCH_struct(gNB, msgDataTx); // this will be removed when the msgDataTx is not necessary anymore gNB->msgDataTx = msgDataTx; diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index e87a1d13db..f744b441ab 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -44,6 +44,9 @@ #include #include "nfapi/open-nFAPI/fapi/inc/nr_fapi_p5_utils.h" +static void init_DLSCH_struct(PHY_VARS_gNB *gNB); +static void destroy_DLSCH_struct(const PHY_VARS_gNB *gNB); + int l1_north_init_gNB() { AssertFatal(RC.nb_nr_L1_inst > 0, "Failed to init PHY callbacks: nb_nr_L1_inst = %d\n", RC.nb_nr_L1_inst); @@ -155,6 +158,8 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) /// Transport init necessary for NR synchro init_nr_transport(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)); @@ -241,6 +246,8 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) reset_nr_transport(gNB); + destroy_DLSCH_struct(gNB); + NR_gNB_COMMON * common_vars = &gNB->common_vars; for (int j = 0; j < common_vars->num_beams_period; j++) { if (common_vars->beam_id) @@ -401,29 +408,27 @@ void nr_phy_config_request(NR_PHY_Config_t *phy_config) init_timeshift_rotation(fp); } -void init_DLSCH_struct(PHY_VARS_gNB *gNB, processingData_L1tx_t *msg) +static void init_DLSCH_struct(PHY_VARS_gNB *gNB) { NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; uint16_t grid_size = cfg->carrier_config.dl_grid_size[fp->numerology_index].value; - msg->num_pdsch_slot = 0; - - msg->dlsch = calloc(gNB->max_nb_pdsch, sizeof(*msg->dlsch)); + gNB->dlsch = calloc(gNB->max_nb_pdsch, sizeof(*gNB->dlsch)); for (int i = 0; i < gNB->max_nb_pdsch; i++) { LOG_D(PHY, "Allocating Transport Channel Buffers for DLSCH %d/%d\n", i, gNB->max_nb_pdsch); - msg->dlsch[i] = new_gNB_dlsch(fp, grid_size); + gNB->dlsch[i] = new_gNB_dlsch(fp, grid_size); } } -void reset_DLSCH_struct(const PHY_VARS_gNB *gNB, processingData_L1tx_t *msg) +static void destroy_DLSCH_struct(const PHY_VARS_gNB *gNB) { const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; const nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; const uint16_t grid_size = cfg->carrier_config.dl_grid_size[fp->numerology_index].value; for (int i = 0; i < gNB->max_nb_pdsch; i++) { - free_gNB_dlsch(&msg->dlsch[i], grid_size, fp); + free_gNB_dlsch(&gNB->dlsch[i], grid_size, fp); } - free(msg->dlsch); + free(gNB->dlsch); } void init_nr_transport(PHY_VARS_gNB *gNB) diff --git a/openair1/PHY/INIT/nr_phy_init.h b/openair1/PHY/INIT/nr_phy_init.h index 1b1a0fa1b0..f556498f0f 100644 --- a/openair1/PHY/INIT/nr_phy_init.h +++ b/openair1/PHY/INIT/nr_phy_init.h @@ -46,9 +46,6 @@ int l1_north_init_gNB(void); void init_nr_transport(PHY_VARS_gNB *gNB); void reset_nr_transport(PHY_VARS_gNB *gNB); -void init_DLSCH_struct(PHY_VARS_gNB *gNB, processingData_L1tx_t *msg); -void reset_DLSCH_struct(const PHY_VARS_gNB *gNB, processingData_L1tx_t *msg); - void RCconfig_nrUE_prs(void *cfg); void init_nr_prs_ue_vars(PHY_VARS_NR_UE *ue); void nr_init_dl_harq_processes(NR_DL_UE_HARQ_t harq_list[2][NR_MAX_DLSCH_HARQ_PROCESSES], int number_of_processes, int num_rb); diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c index 244ab5363b..bde85329e7 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c @@ -36,7 +36,7 @@ void nr_fill_dlsch_dl_tti_req(processingData_L1tx_t *msgTx, nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu) { - NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[msgTx->num_pdsch_slot]; + NR_gNB_DLSCH_t *dlsch = &msgTx->gNB->dlsch[msgTx->num_pdsch_slot]; /// DLSCH struct memcpy((void*)&dlsch->pdsch_pdu, (void*)pdsch_pdu, sizeof(nfapi_nr_dl_tti_pdsch_pdu)); AssertFatal(msgTx->num_pdsch_slot == pdsch_pdu->pdsch_pdu_rel15.pduIndex, @@ -53,7 +53,7 @@ void nr_fill_dlsch_tx_req(processingData_L1tx_t *msgTx, int idx, uint8_t *sdu) /* not sure if FAPI could transmit DL_TTI_req and TX_req in different orders. * for the moment, assume they are in the same order (and check!) */ - NR_gNB_DLSCH_t *dlsch = &msgTx->dlsch[idx]; + NR_gNB_DLSCH_t *dlsch = &msgTx->gNB->dlsch[idx]; nfapi_nr_dl_tti_pdsch_pdu *pdsch = &dlsch->pdsch_pdu; AssertFatal(pdsch->pdsch_pdu_rel15.pduIndex == idx, "PDSCH PDU index %d does not match %d\n", pdsch->pdsch_pdu_rel15.pduIndex, idx); dlsch->pdu = sdu; diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index af4f0d5707..0563d7580e 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -386,6 +386,8 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_COMMON common_vars; prach_list_t prach_list; + // 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; NR_gNB_PUSCH *pusch_vars; NR_gNB_PUCCH_t *pucch; @@ -580,7 +582,6 @@ typedef struct processingData_L1tx { /// corresponds to UL_dci_req->ul_dci_pdu_list nfapi_nr_dl_tti_pdcch_pdu ul_pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; nfapi_nr_dl_tti_csi_rs_pdu csirs_pdu[NFAPI_NR_MAX_NB_CORESETS]; - NR_gNB_DLSCH_t *dlsch; nfapi_nr_dl_tti_ssb_pdu ssb_pdu[64]; int n_ssb_pdu; int n_csirs_pdu; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index f3f80e2ae4..3579da8642 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -312,7 +312,7 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, if (msgTx->num_pdsch_slot > 0) { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,1); LOG_D(PHY, "PDSCH generation started (%d) in frame %d.%d\n", msgTx->num_pdsch_slot,frame,slot); - nr_generate_pdsch(gNB, msgTx->num_pdsch_slot, msgTx->dlsch, frame, slot); + nr_generate_pdsch(gNB, msgTx->num_pdsch_slot, gNB->dlsch, frame, slot); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,0); } msgTx->num_pdsch_slot = 0; diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c index 0dbc230f09..46cda67a6f 100644 --- a/openair1/SIMULATION/NR_PHY/dlschsim.c +++ b/openair1/SIMULATION/NR_PHY/dlschsim.c @@ -439,9 +439,9 @@ int main(int argc, char **argv) nr_init_dl_harq_processes(UE->dl_harq_processes, 8, nb_rb); unsigned char harq_pid = 0; //dlsch->harq_ids[subframe]; - processingData_L1tx_t msgDataTx; - init_DLSCH_struct(gNB, &msgDataTx); - NR_gNB_DLSCH_t *dlsch = &msgDataTx.dlsch[0]; + processingData_L1tx_t msgDataTx = {0}; + msgDataTx.gNB = gNB; + NR_gNB_DLSCH_t *dlsch = &msgDataTx.gNB->dlsch[0]; nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; //time_stats_t *rm_stats, *te_stats, *i_stats; unsigned int TBS = 8424; @@ -621,8 +621,6 @@ int main(int argc, char **argv) free_channel_desc_scm(gNB2UE); - reset_DLSCH_struct(gNB, &msgDataTx); - int nb_slots_to_set = (1 << mu) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME; for (int i = 0; i < nb_slots_to_set; ++i) free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list); diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index a4c4351764..33ff1b7587 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -976,9 +976,9 @@ int main(int argc, char **argv) initNotifiedFIFO(&gNB->L1_tx_out); // we create 2 threads for L1 tx processing processingData_L1tx_t *msgDataTx = malloc(sizeof(processingData_L1tx_t)); - init_DLSCH_struct(gNB, msgDataTx); msgDataTx->slot = slot; msgDataTx->frame = frame; + msgDataTx->gNB = gNB; gNB->msgDataTx = msgDataTx; // Buffers to store internal memory of slot process @@ -1058,7 +1058,7 @@ int main(int argc, char **argv) n_false_positive = 0; if (n_trials== 1) num_rounds = 1; - NR_gNB_DLSCH_t *gNB_dlsch = &msgDataTx->dlsch[0]; + NR_gNB_DLSCH_t *gNB_dlsch = &msgDataTx->gNB->dlsch[0]; nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &gNB_dlsch->pdsch_pdu.pdsch_pdu_rel15; for (trial = 0; trial < n_trials && !stop; trial++) { From 2bea71a80aa6128a0b75e008e279c5881c0bb3d6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 14 Nov 2025 10:31:55 +0100 Subject: [PATCH 232/275] Avoid use of FAPI message buffers in msgDataTx Note: after this commit, monolithic works but nFAPI operation is broken and fixed in the next commit, to keep the changes small(er). All simulators work. This commit removes the use of msgDataTx's intermediate buffers for FAPI messages and the call of the "Schedule response". The latter incurs an additional copy of FAPI messages for the TX chain (for the RX chain, it is and will remain there) which we can avoid. Instead, it uses an NR_Sched_Rsp_t-typed variable sched_response to store the results of the scheduler when called from tx_func(). Thus, the scheduler remains unchanged (it gets a pointer to where to put FAPI messages), but we don't call nr_schedule_response() to then copy FAPI messages into the L1, but have them in a local variable sched_response. sched_response on on the data segment, as it is big and would overflow the stack; at the same time, this is ok because only one tx_func() runs at a time. Since we don't use allocate_sched_response() anymore, we don't need deref_sched_response(). clear_slot_beamid() is moved to tx_func(), as it was called in nr_schedule_response(). Since the "intermediate" NR_gNB_DLSCH_t structure groups both the DL_tti_pdsch and TX_data.req structures, a pointer has been created to point to pdsch_pdu. This affects a number of simulators, as they have to put some messages on the stack: - nr_dlschsim: put the corresponding variable on the stack - nr_pbchsim: a new array for the SSB PDUs has been introduced Further, these changes are now necessary: - nr_dlsim: there were variables "rel15" and "pdsch_pdu_rel15" that point to the same PDSCH PDU. At least "rel15" would not exist, as the corresponding pointer is now populated in phy_procedures_gNB_TX(), and we therefore refer to the single PDSCH PDU variable with "pdsch_pdu_rel15", but at the place where "rel15" used to be initialized. - nr_ulsim: use nr_save_ul_tti_req() to load PDUs into the RX chain instead of nr_schedule_response(), which does not exist anymore. --- executables/nr-gnb.c | 31 +++--- openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 4 +- openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c | 4 +- openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c | 2 +- openair1/PHY/defs_gNB.h | 2 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 104 +++++++++++++------- openair1/SCHED_NR/sched_nr.h | 9 +- openair1/SIMULATION/NR_PHY/dlschsim.c | 38 +++---- openair1/SIMULATION/NR_PHY/dlsim.c | 70 ++++++------- openair1/SIMULATION/NR_PHY/pbchsim.c | 14 +-- openair1/SIMULATION/NR_PHY/ulsim.c | 12 +-- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c | 20 +--- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 8 +- openair2/NR_PHY_INTERFACE/NR_IF_Module.c | 80 ++++++++------- openair2/NR_PHY_INTERFACE/NR_IF_Module.h | 3 +- 15 files changed, 206 insertions(+), 195 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 7c52d67c97..1485b6de1d 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -82,8 +82,6 @@ static void tx_func(processingData_L1tx_t *info) int slot_rx = info->slot_rx; LOG_D(NR_PHY, "%d.%d running tx_func\n", frame_tx, slot_tx); PHY_VARS_gNB *gNB = info->gNB; - module_id_t module_id = gNB->Mod_id; - uint8_t CC_id = gNB->CC_id; NR_IF_Module_t *ifi = gNB->if_inst; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; @@ -94,15 +92,22 @@ static void tx_func(processingData_L1tx_t *info) reset_active_ulsch(gNB, frame_rx); } + clear_slot_beamid(gNB, slot_tx); + + nfapi_nr_slot_indication_scf_t ind = {.sfn = frame_tx, .slot = slot_tx}; start_meas(&gNB->slot_indication_stats); - ifi->NR_slot_indication(module_id, CC_id, frame_tx, slot_tx); + // this variable is very big (multiple MB), so we put it into static storage + // to not overflow the stack while still having it in local (function) scope + // also, tx_func() is only executed by one thread, serially + static NR_Sched_Rsp_t sched_response; + ifi->NR_slot_indication(&ind, &sched_response); stop_meas(&gNB->slot_indication_stats); - gNB->msgDataTx->timestamp_tx = info->timestamp_tx; - info = gNB->msgDataTx; + info->gNB = gNB; // At this point, MAC scheduler just ran, including scheduling // PRACH/PUCCH/PUSCH, so trigger RX chain processing + nr_save_ul_tti_req(gNB, &sched_response.UL_tti_req); LOG_D(NR_PHY, "Trigger RX for %d.%d\n", frame_rx, slot_rx); notifiedFIFO_elt_t *res = newNotifiedFIFO_elt(sizeof(processingData_L1_t), 0, &gNB->resp_L1, NULL); processingData_L1_t *syncMsg = NotifiedFifoData(res); @@ -119,7 +124,13 @@ static void tx_func(processingData_L1tx_t *info) if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx || IS_SOFTMODEM_RFSIM || cfg->analog_beamforming_ve.analog_beam_list) { start_meas(&info->gNB->phy_proc_tx); - phy_procedures_gNB_TX(info, frame_tx, slot_tx, 1); + phy_procedures_gNB_TX(info->gNB, + &sched_response.DL_req, + &sched_response.TX_req, + &sched_response.UL_dci_req, + frame_tx, + slot_tx, + 1); PHY_VARS_gNB *gNB = info->gNB; processingData_RU_t syncMsgRU; @@ -131,14 +142,6 @@ static void tx_func(processingData_L1tx_t *info) ru_tx_func((void *)&syncMsgRU); stop_meas(&info->gNB->phy_proc_tx); } - - if (NFAPI_MODE == NFAPI_MONOLITHIC) { - /* this thread is done with the sched_info, decrease the reference counter. - * This only applies for monolithic; in the PNF, the memory is allocated in - * a ring buffer that should never be overwritten (one frame duration). */ - LOG_D(NR_PHY, "Calling deref_sched_response for id %d (tx_func) in %d.%d\n", info->sched_response_id, frame_tx, slot_tx); - deref_sched_response(info->sched_response_id); - } } void *L1_rx_thread(void *arg) diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index 7afbed41b4..8df7b4814c 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -555,7 +555,7 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC time_stats_t *dlsch_scrambling_stats = &gNB->dlsch_scrambling_stats; time_stats_t *dlsch_modulation_stats = &gNB->dlsch_modulation_stats; - const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu->pdsch_pdu_rel15; const int layerSz = frame_parms->N_RB_DL * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB; const int symbol_sz=frame_parms->ofdm_symbol_size; const int dmrs_Type = rel15->dmrsConfigType; @@ -789,7 +789,7 @@ void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_arr for (int i = 0; i < n_dlsch; i++) { NR_gNB_DLSCH_t *dlsch = &dlsch_array[i]; - const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu->pdsch_pdu_rel15; LOG_D(PHY, "pdsch: BWPStart %d, BWPSize %d, rbStart %d, rbsize %d\n", diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index 67f3dd4363..c1a4dd152d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -138,7 +138,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, NR_gNB_DLSCH_t *dlsch = &dlsch_array[i]; unsigned int crc = 1; - const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu->pdsch_pdu_rel15; uint32_t A = rel15->TBSize[0] << 3; unsigned char *a = dlsch->pdu; if (rel15->rnti != SI_RNTI) { @@ -224,7 +224,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, for (int i = 0; i < n_dlsch; i++) { NR_gNB_DLSCH_t *dlsch = &dlsch_array[i]; - const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; + const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu->pdsch_pdu_rel15; nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[i]; diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c index bde85329e7..17766da629 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c @@ -54,7 +54,7 @@ void nr_fill_dlsch_tx_req(processingData_L1tx_t *msgTx, int idx, uint8_t *sdu) /* not sure if FAPI could transmit DL_TTI_req and TX_req in different orders. * for the moment, assume they are in the same order (and check!) */ NR_gNB_DLSCH_t *dlsch = &msgTx->gNB->dlsch[idx]; - nfapi_nr_dl_tti_pdsch_pdu *pdsch = &dlsch->pdsch_pdu; + const nfapi_nr_dl_tti_pdsch_pdu *pdsch = dlsch->pdsch_pdu; AssertFatal(pdsch->pdsch_pdu_rel15.pduIndex == idx, "PDSCH PDU index %d does not match %d\n", pdsch->pdsch_pdu_rel15.pduIndex, idx); dlsch->pdu = sdu; } diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 0563d7580e..fdaae94b49 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -98,7 +98,7 @@ typedef struct { typedef struct { /// Nfapi DLSCH PDU - nfapi_nr_dl_tti_pdsch_pdu pdsch_pdu; + const nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu; /// pointer to pdu from MAC interface (this is "a" in 36.212) uint8_t *pdu; /// Pointer to the payload diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 3579da8642..eb4f1c8e84 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -243,12 +243,14 @@ static void nr_generate_csi_rs_gNB(PHY_VARS_gNB *gNB, gNB->common_vars.txdataF[beam_nb]); } -void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, +void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, + const nfapi_nr_dl_tti_request_t *DL_req, + const nfapi_nr_tx_data_request_t *TX_req, + const nfapi_nr_ul_dci_request_t *UL_dci_req, int frame, int slot, int do_meas) { - PHY_VARS_gNB *gNB = msgTx->gNB; NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; int slot_prs = 0; @@ -282,44 +284,41 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, } } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_COMMON_TX,1); - for (int i = 0; i < msgTx->n_ssb_pdu; i++) - nr_common_signal_procedures(gNB, frame, slot, &msgTx->ssb_pdu[i]); - msgTx->n_ssb_pdu = 0; + for (int i = 0; i < UL_dci_req->numPdus; ++i) + nr_generate_dci(gNB, &UL_dci_req->ul_dci_pdu_list[i].pdcch_pdu.pdcch_pdu_rel15, txdataF_offset, &gNB->frame_parms, slot); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_COMMON_TX,0); - - int num_pdcch_pdus = msgTx->num_ul_pdcch + msgTx->num_dl_pdcch; - - if (num_pdcch_pdus > 0) { - LOG_D(PHY, "[gNB %d] Frame %d slot %d Calling nr_generate_dci() (number of UL/DL PDCCH PDUs %d/%d)\n", - gNB->Mod_id, frame, slot, msgTx->num_ul_pdcch, msgTx->num_dl_pdcch); - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_gNB_PDCCH_TX,1); - - start_meas(&gNB->dci_generation_stats); - for (int i = 0; i < msgTx->num_dl_pdcch; ++i) - nr_generate_dci(gNB, &msgTx->pdcch_pdu[i].pdcch_pdu_rel15, txdataF_offset, &gNB->frame_parms, slot); - for (int i = 0; i < msgTx->num_ul_pdcch; ++i) - nr_generate_dci(gNB, &msgTx->ul_pdcch_pdu[i].pdcch_pdu_rel15, txdataF_offset, &gNB->frame_parms, slot); - stop_meas(&gNB->dci_generation_stats); - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_gNB_PDCCH_TX,0); + int num_pdsch = 0; + for (int i = 0; i < DL_req->dl_tti_request_body.nPDUs; ++i) { + const nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu = &DL_req->dl_tti_request_body.dl_tti_pdu_list[i]; + switch (dl_tti_pdu->PDUType) { + case NFAPI_NR_DL_TTI_SSB_PDU_TYPE: + nr_common_signal_procedures(gNB, frame, slot, &dl_tti_pdu->ssb_pdu); + break; + case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE: + nr_generate_dci(gNB, &dl_tti_pdu->pdcch_pdu.pdcch_pdu_rel15, txdataF_offset, &gNB->frame_parms, slot); + break; + case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE: + nr_generate_csi_rs_gNB(gNB, slot, cfg, &dl_tti_pdu->csi_rs_pdu); + break; + case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: { + int tx_data_idx = dl_tti_pdu->pdsch_pdu.pdsch_pdu_rel15.pduIndex; + DevAssert(tx_data_idx < TX_req->Number_of_PDUs); + // reuse dlsch variables, as there are multiple very large memory + // buffers + gNB->dlsch[num_pdsch].pdsch_pdu = &dl_tti_pdu->pdsch_pdu; + gNB->dlsch[num_pdsch].pdu = (uint8_t *)TX_req->pdu_list[tx_data_idx].TLVs[0].value.direct; + DevAssert(num_pdsch < gNB->max_nb_pdsch); + num_pdsch++; + } break; + } } - msgTx->num_dl_pdcch = 0; - msgTx->num_ul_pdcch = 0; - if (msgTx->num_pdsch_slot > 0) { + if (num_pdsch > 0) { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,1); - LOG_D(PHY, "PDSCH generation started (%d) in frame %d.%d\n", msgTx->num_pdsch_slot,frame,slot); - nr_generate_pdsch(gNB, msgTx->num_pdsch_slot, gNB->dlsch, frame, slot); + LOG_D(PHY, "PDSCH generation started (%d) in frame %d.%d\n", num_pdsch, frame, slot); + nr_generate_pdsch(gNB, num_pdsch, gNB->dlsch, frame, slot); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,0); } - msgTx->num_pdsch_slot = 0; - - for (int i = 0; i < msgTx->n_csirs_pdu; i++) - nr_generate_csi_rs_gNB(gNB, slot, cfg, &msgTx->csirs_pdu[i]); - msgTx->n_csirs_pdu = 0; //apply the OFDM symbol rotation here start_meas(&gNB->phase_comp_stats); @@ -1293,3 +1292,40 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_UESPEC_RX,0); return pusch_DTX; } + +void nr_save_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req) +{ + DevAssert(gNB != NULL); + DevAssert(UL_tti_req != NULL); + + int frame = UL_tti_req->SFN; + int slot = UL_tti_req->Slot; + + for (int i = 0; i < UL_tti_req->n_pdus; i++) { + int type = UL_tti_req->pdus_list[i].pdu_type; + LOG_D(NR_PHY, + "frame %d, slot %d got %s for %d.%d\n", + frame, + slot, + txt_nfapi_nr_ul_config_pdu_type[type], + UL_tti_req->SFN, + UL_tti_req->Slot); + switch (type) { + case NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE: + nr_fill_ulsch(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].pusch_pdu); + break; + case NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE: + nr_fill_pucch(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].pucch_pdu); + 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"); + } 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); + break; + } + } +} diff --git a/openair1/SCHED_NR/sched_nr.h b/openair1/SCHED_NR/sched_nr.h index aade6e766c..da71809589 100644 --- a/openair1/SCHED_NR/sched_nr.h +++ b/openair1/SCHED_NR/sched_nr.h @@ -33,7 +33,14 @@ #include "phy_frame_config_nr.h" void nr_set_ssb_first_subcarrier(nfapi_nr_config_request_scf_t *cfg, NR_DL_FRAME_PARMS *fp); -void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, int frame_tx, int slot_tx, int do_meas); +void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, + const nfapi_nr_dl_tti_request_t *DL_req, + const nfapi_nr_tx_data_request_t *TX_req, + const nfapi_nr_ul_dci_request_t *UL_dci_req, + int frame, + int slot, + int do_meas); +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 nr_common_signal_procedures (PHY_VARS_gNB *gNB,int frame,int slot, const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu); diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c index 46cda67a6f..ea4a068b6e 100644 --- a/openair1/SIMULATION/NR_PHY/dlschsim.c +++ b/openair1/SIMULATION/NR_PHY/dlschsim.c @@ -439,10 +439,7 @@ int main(int argc, char **argv) nr_init_dl_harq_processes(UE->dl_harq_processes, 8, nb_rb); unsigned char harq_pid = 0; //dlsch->harq_ids[subframe]; - processingData_L1tx_t msgDataTx = {0}; - msgDataTx.gNB = gNB; - NR_gNB_DLSCH_t *dlsch = &msgDataTx.gNB->dlsch[0]; - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu.pdsch_pdu_rel15; + NR_gNB_DLSCH_t *dlsch = &gNB->dlsch[0]; //time_stats_t *rm_stats, *te_stats, *i_stats; unsigned int TBS = 8424; uint8_t nb_re_dmrs = 6; // No data in dmrs symbol @@ -459,19 +456,24 @@ int main(int argc, char **argv) TBS = nr_compute_tbs(mod_order,rate, nb_rb, nb_symb_sch, nb_re_dmrs*length_dmrs, 0, 0, Nl); printf("available bits %u TBS %u mod_order %d\n", available_bits, TBS, mod_order); //dlsch->harq_ids[subframe]= 0; - rel15->rbSize = nb_rb; - rel15->NrOfSymbols = nb_symb_sch; - rel15->qamModOrder[0] = mod_order; - rel15->nrOfLayers = Nl; - rel15->TBSize[0] = TBS>>3; - rel15->targetCodeRate[0] = rate; - rel15->NrOfCodewords = 1; - rel15->dmrsConfigType = NFAPI_NR_DMRS_TYPE1; - rel15->dlDmrsSymbPos = 4; - rel15->mcsIndex[0] = Imcs; - rel15->numDmrsCdmGrpsNoData = 1; - rel15->maintenance_parms_v3.tbSizeLbrmBytes = Tbslbrm; - rel15->maintenance_parms_v3.ldpcBaseGraph = get_BG(TBS, rate); + nfapi_nr_dl_tti_pdsch_pdu pdsch_pdu = { + .pdsch_pdu_rel15 = { + .rbSize = nb_rb, + .NrOfSymbols = nb_symb_sch, + .qamModOrder[0] = mod_order, + .nrOfLayers = Nl, + .TBSize[0] = TBS >> 3, + .targetCodeRate[0] = rate, + .NrOfCodewords = 1, + .dmrsConfigType = NFAPI_NR_DMRS_TYPE1, + .dlDmrsSymbPos = 4, + .mcsIndex[0] = Imcs, + .numDmrsCdmGrpsNoData = 1, + .maintenance_parms_v3.tbSizeLbrmBytes = Tbslbrm, + .maintenance_parms_v3.ldpcBaseGraph = get_BG(TBS, rate), + }, + }; + dlsch->pdsch_pdu = &pdsch_pdu; double modulated_input[16 * 68 * 384]; // [hna] 16 segments, 68*Zc short channel_output_fixed[16 * 68 * 384]; //unsigned char *estimated_output; @@ -512,7 +514,7 @@ int main(int argc, char **argv) //printf("crc32: [0]->0x%08x\n",crc24c(test_input, 32)); // generate signal - unsigned char output[rel15->rbSize * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB * NR_MAX_NB_LAYERS] __attribute__((aligned(64))); + unsigned char output[nb_rb * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB * NR_MAX_NB_LAYERS] __attribute__((aligned(64))); bzero(output, sizeof(output)); if (input_fd == NULL) { nr_dlsch_encoding(gNB, 1, dlsch, frame, slot, frame_parms, output, NULL, NULL, NULL, NULL, NULL, NULL, NULL); diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 33ff1b7587..82984657d3 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -62,7 +62,6 @@ #include "PHY/defs_nr_common.h" #include "PHY/impl_defs_nr.h" #include "PHY/phy_vars_nr_ue.h" -#include "SCHED_NR/fapi_nr_l1.h" #include "SCHED_NR/sched_nr.h" #include "SCHED_NR_UE/defs.h" #include "SCHED_NR_UE/fapi_nr_ue_l1.h" @@ -286,7 +285,6 @@ nrUE_params_t *get_nrUE_params(void) { return &nrUE_params; } - void validate_input_pmi(nfapi_nr_config_request_scf_t *gNB_config, nr_pdsch_AntennaPorts_t pdsch_AntennaPorts, int nrOfLayers, @@ -974,12 +972,6 @@ int main(int argc, char **argv) int ret = 1; initNamedTpool(gNBthreads, &gNB->threadPool, true, "gNB-tpool"); initNotifiedFIFO(&gNB->L1_tx_out); - // we create 2 threads for L1 tx processing - processingData_L1tx_t *msgDataTx = malloc(sizeof(processingData_L1tx_t)); - msgDataTx->slot = slot; - msgDataTx->frame = frame; - msgDataTx->gNB = gNB; - gNB->msgDataTx = msgDataTx; // Buffers to store internal memory of slot process int rx_size = (((14 * UE->frame_parms.N_RB_DL * 12 * sizeof(int32_t)) + 15) >> 4) << 4; @@ -1058,8 +1050,13 @@ int main(int argc, char **argv) n_false_positive = 0; if (n_trials== 1) num_rounds = 1; - NR_gNB_DLSCH_t *gNB_dlsch = &msgDataTx->gNB->dlsch[0]; - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &gNB_dlsch->pdsch_pdu.pdsch_pdu_rel15; + NR_gNB_DLSCH_t *gNB_dlsch = &gNB->dlsch[0]; + memset(Sched_INFO, 0, sizeof(*Sched_INFO)); + Sched_INFO->sched_response_id = -1; + nfapi_nr_dl_tti_request_body_t *dl_req = &Sched_INFO->DL_req.dl_tti_request_body; + // scheduler will place PDSCH second (after PDCCH), verification below + nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[1]; + nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu_rel15 = &dl_tti_pdsch_pdu->pdsch_pdu.pdsch_pdu_rel15; for (trial = 0; trial < n_trials && !stop; trial++) { @@ -1081,11 +1078,9 @@ int main(int argc, char **argv) UE_harq_process->DLround = round; UE_harq_process->first_rx = 1; - memset(Sched_INFO, 0, sizeof(*Sched_INFO)); - Sched_INFO->sched_response_id = -1; - while (round < num_rounds && !UE_harq_process->decodeResult && !stop) { - clear_nr_nfapi_information(RC.nrmac[0], 0, frame, slot, &Sched_INFO->DL_req, &Sched_INFO->TX_req, &Sched_INFO->UL_dci_req); + reset_sched_response(Sched_INFO, frame, slot, 0, 0); + clear_nr_nfapi_information(RC.nrmac[0], 0, frame, slot); UE_info->UE_sched_ctrl.harq_processes[harq_pid].ndi = !(trial&1); UE_info->UE_sched_ctrl.harq_processes[harq_pid].round = round; @@ -1093,18 +1088,13 @@ int main(int argc, char **argv) NR_SCHED_LOCK(&gNB_mac->sched_lock); nr_schedule_ue_spec(0, frame, slot, &Sched_INFO->DL_req, &Sched_INFO->TX_req); NR_SCHED_UNLOCK(&gNB_mac->sched_lock); - Sched_INFO->module_id = 0; - Sched_INFO->CC_id = 0; - Sched_INFO->frame = frame; - Sched_INFO->slot = slot; - Sched_INFO->UL_dci_req.numPdus = 0; - nr_schedule_response(Sched_INFO); + + /* check that second message is indeed PDSCH */ + DevAssert(dl_tti_pdsch_pdu->PDUType == NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE); /* PTRS values for DLSIM calculations */ - nfapi_nr_dl_tti_request_body_t *dl_req = &Sched_INFO->DL_req.dl_tti_request_body; - nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[1]; - nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu_rel15 = &dl_tti_pdsch_pdu->pdsch_pdu.pdsch_pdu_rel15; pdu_bit_map = pdsch_pdu_rel15->pduBitmap; + // This is already in DL procedures?? if(pdu_bit_map & 0x1) { set_ptrs_symb_idx(&dlPtrsSymPos, pdsch_pdu_rel15->NrOfSymbols, @@ -1112,16 +1102,12 @@ int main(int argc, char **argv) 1<PTRSTimeDensity, pdsch_pdu_rel15->dlDmrsSymbPos); ptrsSymbPerSlot = get_ptrs_symbols_in_slot(dlPtrsSymPos, pdsch_pdu_rel15->StartSymbolIndex, pdsch_pdu_rel15->NrOfSymbols); - ptrsRePerSymb = ((rel15->rbSize + rel15->PTRSFreqDensity - 1) / rel15->PTRSFreqDensity); + ptrsRePerSymb = ((pdsch_pdu_rel15->rbSize + pdsch_pdu_rel15->PTRSFreqDensity - 1) / pdsch_pdu_rel15->PTRSFreqDensity); LOG_D(PHY,"[DLSIM] PTRS Symbols in a slot: %2u, RE per Symbol: %3u, RE in a slot %4d\n", ptrsSymbPerSlot, ptrsRePerSymb, ptrsSymbPerSlot * ptrsRePerSymb); } - msgDataTx->ssb_pdu[0].ssb_pdu_rel15.bchPayload = 0x001234; - msgDataTx->ssb_pdu[0].ssb_pdu_rel15.SsbBlockIndex = 0; - msgDataTx->gNB = gNB; - start_meas(&gNB->phy_proc_tx); - phy_procedures_gNB_TX(msgDataTx,frame,slot,1); + phy_procedures_gNB_TX(gNB, &Sched_INFO->DL_req, &Sched_INFO->TX_req, &Sched_INFO->UL_dci_req, frame, slot, 1); stop_meas(&gNB->phy_proc_tx); int txdataF_offset = slot * frame_parms->samples_per_slot_wCP; @@ -1196,9 +1182,9 @@ int main(int argc, char **argv) double ts = 1.0/(frame_parms->subcarrier_spacing * frame_parms->ofdm_symbol_size); //Compute AWGN variance - sigma2_dB = 10 * log10((double)txlev_sum * ((double)UE->frame_parms.ofdm_symbol_size/(12*rel15->rbSize))) - SNR; + sigma2_dB = 10 * log10((double)txlev_sum * ((double)UE->frame_parms.ofdm_symbol_size/(12*pdsch_pdu_rel15->rbSize))) - SNR; sigma2 = pow(10, sigma2_dB/10); - if (n_trials==1) printf("sigma2 %f (%f dB), txlev_sum %f (factor %f)\n",sigma2,sigma2_dB,10*log10((double)txlev_sum),(double)(double)UE->frame_parms.ofdm_symbol_size/(12*rel15->rbSize)); + if (n_trials==1) printf("sigma2 %f (%f dB), txlev_sum %f (factor %f)\n",sigma2,sigma2_dB,10*log10((double)txlev_sum),(double)(double)UE->frame_parms.ofdm_symbol_size/(12*pdsch_pdu_rel15->rbSize)); for (aa = 0; aa < n_rx; aa++) { bzero(r_re[aa], slot_length * sizeof(double)); @@ -1240,17 +1226,17 @@ int main(int argc, char **argv) int16_t *UE_llr = (int16_t*)UE->phy_sim_pdsch_llr; - TBS = dlsch0->dlsch_config.TBS;//rel15->TBSize[0]; + TBS = dlsch0->dlsch_config.TBS; uint16_t length_dmrs = get_num_dmrs(dlsch0->dlsch_config.dlDmrsSymbPos); uint16_t nb_rb = dlsch0->dlsch_config.number_rbs; uint8_t nb_re_dmrs = dlsch0->dlsch_config.dmrsConfigType == NFAPI_NR_DMRS_TYPE1 ? 6*dlsch0->dlsch_config.n_dmrs_cdm_groups : 4*dlsch0->dlsch_config.n_dmrs_cdm_groups; uint8_t mod_order = dlsch0->dlsch_config.qamModOrder; uint8_t nb_symb_sch = dlsch0->dlsch_config.number_symbols; uint32_t unav_res = ptrsSymbPerSlot * ptrsRePerSymb; - available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, unav_res, mod_order, rel15->nrOfLayers); + available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, unav_res, mod_order, pdsch_pdu_rel15->nrOfLayers); if (pdu_bit_map & 0x1) { if (trial == 0 && round == 0) { - printf("[DLSIM][PTRS] Available bits are: %5u, removed PTRS bits are: %5u \n", available_bits, (ptrsSymbPerSlot * ptrsRePerSymb * rel15->nrOfLayers * mod_order)); + printf("[DLSIM][PTRS] Available bits are: %5u, removed PTRS bits are: %5u \n", available_bits, (ptrsSymbPerSlot * ptrsRePerSymb * pdsch_pdu_rel15->nrOfLayers * mod_order)); } } @@ -1269,8 +1255,8 @@ int main(int argc, char **argv) } // round if (test_input_bit == NULL) { - test_input_bit = (unsigned char *)malloc16(8 * rel15->TBSize[0]); - estimated_output_bit = (unsigned char *)malloc16(8 * rel15->TBSize[0]); + test_input_bit = (unsigned char *)malloc16(8 * pdsch_pdu_rel15->TBSize[0]); + estimated_output_bit = (unsigned char *)malloc16(8 * pdsch_pdu_rel15->TBSize[0]); } for (i = 0; i < TBS; i++) { @@ -1319,7 +1305,7 @@ int main(int argc, char **argv) printf("), Channel BER (%e", berStats[0]); for (int r = 1; r < num_rounds; r++) printf(",%e", berStats[r]); - printf(") Avg round %.2f, Eff Rate %.4f bits/slot, Eff Throughput %.2f, TBS %u bits/slot\n", roundStats, effRate, effRate / (8 * rel15->TBSize[0]) * 100, 8 * rel15->TBSize[0]); + printf(") Avg round %.2f, Eff Rate %.4f bits/slot, Eff Throughput %.2f, TBS %u bits/slot\n", roundStats, effRate, effRate / (8 * pdsch_pdu_rel15->TBSize[0]) * 100, 8 * pdsch_pdu_rel15->TBSize[0]); printf("*****************************************\n"); printf("\n"); // writing to csv file @@ -1327,7 +1313,7 @@ int main(int argc, char **argv) fprintf(csv_file,"%f,%d/%d,",SNR,n_false_positive,n_trials); for (int r = 0; r < num_rounds; r++) fprintf(csv_file,"%d/%d,%u/%u,%f,%e,",n_errors[r], round_trials[r], errors_scrambling[r], available_bits * round_trials[r],blerStats[r],berStats[r]); - fprintf(csv_file,"%.2f,%.4f,%.2f,%u\n", roundStats, effRate, effRate / (8 * rel15->TBSize[0]) * 100, 8 * rel15->TBSize[0]); + fprintf(csv_file,"%.2f,%.4f,%.2f,%u\n", roundStats, effRate, effRate / (8 * pdsch_pdu_rel15->TBSize[0]) * 100, 8 * pdsch_pdu_rel15->TBSize[0]); } if (print_perf==1) { printf("\ngNB TX function statistics (per %d us slot, NPRB %d, mcs %d, C %d, block %d)\n", @@ -1335,7 +1321,7 @@ int main(int argc, char **argv) g_rbSize, g_mcsIndex, UE->dl_harq_processes[0][slot].C, - 8 * rel15->TBSize[0]); + 8 * pdsch_pdu_rel15->TBSize[0]); printDistribution(&gNB->phy_proc_tx,table_tx,"PHY proc tx"); printStatIndent2(&gNB->dci_generation_stats, "DCI encoding time"); printStatIndent2(&gNB->dlsch_encoding_stats,"DLSCH encoding time"); @@ -1377,8 +1363,8 @@ int main(int argc, char **argv) const uint32_t numReSym = (g_rbSize * 12 + 15) & (~15); for (int l = 0; l < g_nrOfLayers; l++) { for (int r = 0; r < n_rx; r++) { - const int s = rel15->StartSymbolIndex; - const int n = rel15->NrOfSymbols; + const int s = pdsch_pdu_rel15->StartSymbolIndex; + const int n = pdsch_pdu_rel15->NrOfSymbols; for (int i = s; i < s + n; i++) { const uint32_t dmrsBitMap = phy_data.dlsch[0].dlsch_config.dlDmrsSymbPos; const uint32_t dmrsCfg = phy_data.dlsch[0].dlsch_config.dmrsConfigType; @@ -1414,7 +1400,7 @@ int main(int argc, char **argv) break; } - if (effRate >= (eff_tp_check * 8 * rel15->TBSize[0])) { + if (effRate >= (eff_tp_check * 8 * pdsch_pdu_rel15->TBSize[0])) { printf("PDSCH test OK\n"); ret = 0; break; diff --git a/openair1/SIMULATION/NR_PHY/pbchsim.c b/openair1/SIMULATION/NR_PHY/pbchsim.c index b40cfc8176..c86744afaa 100644 --- a/openair1/SIMULATION/NR_PHY/pbchsim.c +++ b/openair1/SIMULATION/NR_PHY/pbchsim.c @@ -519,10 +519,10 @@ int main(int argc, char **argv) exit(-1); } - processingData_L1tx_t msgDataTx; // generate signal const uint32_t rxdataF_sz = UE->frame_parms.samples_per_slot_wCP; __attribute__ ((aligned(32))) c16_t rxdataF[UE->frame_parms.nb_antennas_rx][rxdataF_sz]; + nfapi_nr_dl_tti_ssb_pdu ssb_pdu[64] = {0}; if (input_fd==NULL) { for (i=0; iLmax; i++) { @@ -530,10 +530,10 @@ int main(int argc, char **argv) const int sc_offset = frame_parms->freq_range == FR1 ? ssb_subcarrier_offset<freq_range == FR1 ? gNB->gNB_config.ssb_table.ssb_offset_point_a.value<gNB_config.ssb_table.ssb_offset_point_a.value << (mu - 2); - msgDataTx.ssb_pdu[i].ssb_pdu_rel15.bchPayload = 0x55dd33; - msgDataTx.ssb_pdu[i].ssb_pdu_rel15.SsbBlockIndex = i; - msgDataTx.ssb_pdu[i].ssb_pdu_rel15.SsbSubcarrierOffset = sc_offset; - msgDataTx.ssb_pdu[i].ssb_pdu_rel15.ssbOffsetPointA = prb_offset; + ssb_pdu[i].ssb_pdu_rel15.bchPayload = 0x55dd33; + ssb_pdu[i].ssb_pdu_rel15.SsbBlockIndex = i; + ssb_pdu[i].ssb_pdu_rel15.SsbSubcarrierOffset = sc_offset; + ssb_pdu[i].ssb_pdu_rel15.ssbOffsetPointA = prb_offset; start_symbol = nr_get_ssb_start_symbol(frame_parms,i); int slot = start_symbol/14; @@ -541,7 +541,7 @@ int main(int argc, char **argv) for (aa=0; aaframe_parms.nb_antennas_tx; aa++) memset(gNB->common_vars.txdataF[0][aa], 0, frame_parms->samples_per_slot_wCP * sizeof(int32_t)); - nr_common_signal_procedures (gNB,frame,slot, &msgDataTx.ssb_pdu[i]); + nr_common_signal_procedures (gNB,frame,slot, &ssb_pdu[i]); int samp = frame_parms->get_samples_slot_timestamp(slot, frame_parms, 0); for (aa=0; aaframe_parms.nb_antennas_tx; aa++) { @@ -744,7 +744,7 @@ int main(int argc, char **argv) gNB->gNB_config.ssb_table.ssb_subcarrier_offset.value, frame_parms->Lmax); int payload_ret = (result.xtra_byte == xtra_byte); - nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu = &msgDataTx.ssb_pdu[ssb_index].ssb_pdu_rel15; + nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu = &ssb_pdu[ssb_index].ssb_pdu_rel15; for (int i = 0; i < 3; i++) payload_ret += (result.decoded_output[i] == ((pdu->bchPayload >> (8 * i)) & 0xff)); // printf("ret %d\n", payload_ret); diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index b7f93cdf3a..85c165b00d 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -60,7 +60,6 @@ #include "PHY/defs_nr_common.h" #include "PHY/impl_defs_nr.h" #include "PHY/phy_vars_nr_ue.h" -#include "SCHED_NR/fapi_nr_l1.h" #include "SCHED_NR/sched_nr.h" #include "SCHED_NR_UE/defs.h" #include "SCHED_NR_UE/fapi_nr_ue_l1.h" @@ -727,11 +726,6 @@ int main(int argc, char *argv[]) else initNamedTpool(gNBthreads, &gNB->threadPool, true, "gNB-tpool"); - processingData_L1tx_t *msgDataTx = malloc(sizeof(processingData_L1tx_t)); - msgDataTx->slot = -1; - gNB->msgDataTx = msgDataTx; - //gNB_config = &gNB->gNB_config; - NR_UL_IND_t UL_INFO = {0}; UL_INFO.crc_ind.crc_list = UL_INFO.crc_pdu_list; UL_INFO.rx_ind.pdu_list = UL_INFO.rx_pdu_list; @@ -1284,6 +1278,9 @@ int main(int argc, char *argv[]) srs_pdu->beamforming.prg_size = 1; } + /* load FAPI into RX of L1 */ + nr_save_ul_tti_req(gNB, &Sched_INFO->UL_tti_req); + /// UE UL PDUs UE->ul_harq_processes[harq_pid].round = round; @@ -1291,9 +1288,6 @@ int main(int argc, char *argv[]) UE_proc.frame_tx = frame; UE_proc.gNB_id = 0; - // prepare ULSCH/PUSCH reception - nr_schedule_response(Sched_INFO); - // --------- setting parameters for UE -------- nr_scheduled_response_t scheduled_response = {.ul_config = &ul_config, .phy_data = (void *)&phy_data}; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index a102411721..6a9cce656a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -59,13 +59,7 @@ uint8_t nr_get_rv(int rel_round) return nr_rv_round_map[rel_round]; } -void clear_nr_nfapi_information(gNB_MAC_INST *gNB, - int CC_idP, - frame_t frameP, - slot_t slotP, - nfapi_nr_dl_tti_request_t *DL_req, - nfapi_nr_tx_data_request_t *TX_req, - nfapi_nr_ul_dci_request_t *UL_dci_req) +void clear_nr_nfapi_information(gNB_MAC_INST *gNB, int CC_idP, frame_t frameP, slot_t slotP) { /* called below and in simulators, so we assume a lock but don't require it */ const int num_slots = gNB->frame_structure.numb_slots_frame; @@ -75,16 +69,8 @@ void clear_nr_nfapi_information(gNB_MAC_INST *gNB, gNB->pdu_index[CC_idP] = 0; - DL_req[CC_idP].SFN = frameP; - DL_req[CC_idP].Slot = slotP; - DL_req[CC_idP].dl_tti_request_body.nPDUs = 0; - DL_req[CC_idP].dl_tti_request_body.nGroup = 0; memset(pdcch, 0, sizeof(*pdcch) * MAX_NUM_CORESET); - UL_dci_req[CC_idP].SFN = frameP; - UL_dci_req[CC_idP].Slot = slotP; - UL_dci_req[CC_idP].numPdus = 0; - /* advance last round's future UL_tti_req to be ahead of current frame/slot */ const int size = gNB->UL_tti_req_ahead_size; const int prev_slot = frameP * num_slots + slotP + size - 1; @@ -100,8 +86,6 @@ void clear_nr_nfapi_information(gNB_MAC_INST *gNB, future_ul_tti_req->n_ulsch = 0; future_ul_tti_req->n_ulcch = 0; future_ul_tti_req->n_group = 0; - - TX_req[CC_idP].Number_of_PDUs = 0; } static void clear_beam_information(NR_beam_info_t *beam_info, int frame, int slot, int slots_per_frame) @@ -191,7 +175,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo uint16_t *vrb_map_UL = cc[CC_id].vrb_map_UL[i]; memcpy(&vrb_map_UL[prev_slot % size * MAX_BWP_SIZE], &gNB->ulprbbl, sizeof(uint16_t) * MAX_BWP_SIZE); } - clear_nr_nfapi_information(gNB, CC_id, frame, slot, &sched_info->DL_req, &sched_info->TX_req, &sched_info->UL_dci_req); + clear_nr_nfapi_information(gNB, CC_id, frame, slot); } bool wait_prach_completed = gNB->num_scheduled_prach_rx >= NUM_PRACH_RX_FOR_NOISE_ESTIMATE; diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index cfe02755b2..fb5e64efa8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -68,13 +68,7 @@ int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs); int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots); int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info); -void clear_nr_nfapi_information(gNB_MAC_INST *gNB, - int CC_idP, - frame_t frameP, - slot_t slotP, - nfapi_nr_dl_tti_request_t *DL_req, - nfapi_nr_tx_data_request_t *TX_req, - nfapi_nr_ul_dci_request_t *UL_dci_req); +void clear_nr_nfapi_information(gNB_MAC_INST *gNB, int CC_idP, frame_t frameP, slot_t slotP); void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot); diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index 2352870ace..faf07154ee 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -380,48 +380,22 @@ static void match_crc_rx_pdu(nfapi_nr_rx_data_indication_t *rx_ind, nfapi_nr_crc } extern void handle_nr_slot_ind(uint16_t sfn, uint16_t slot); -static void pnf_send_slot_ind(module_id_t module_id, int CC_id, int frame, int slot) +static void pnf_send_slot_ind(const nfapi_nr_slot_indication_scf_t *ind, NR_Sched_Rsp_t *rsp) { - (void)module_id; - (void)CC_id; - handle_nr_slot_ind(frame, slot); + module_id_t module_id = 0; + int CC_id = 0; + reset_sched_response(rsp, ind->sfn, ind->slot, module_id, CC_id); + handle_nr_slot_ind(ind->sfn, ind->slot); + + // TODO copy messages from FAPI buffers } -static void run_scheduler_monolithic(module_id_t module_id, int CC_id, int frame, int slot) +static void run_scheduler_monolithic(const nfapi_nr_slot_indication_scf_t *ind, NR_Sched_Rsp_t *rsp) { - NR_IF_Module_t *ifi = nr_if_inst[module_id]; - - LOG_D(NR_MAC, "Calling scheduler for %d.%d\n", frame, slot); - NR_Sched_Rsp_t *sched_info = allocate_sched_response(); - - // clear UL DCI prior to handling ULSCH - sched_info->UL_dci_req.numPdus = 0; - gNB_dlsch_ulsch_scheduler(module_id, frame, slot, sched_info); - ifi->CC_mask = 0; - sched_info->module_id = module_id; - sched_info->CC_id = CC_id; - sched_info->frame = frame; - sched_info->slot = slot; - /* - sched_info->DL_req = &mac->DL_req[CC_id]; - sched_info->UL_dci_req = &mac->UL_dci_req[CC_id]; - - sched_info->UL_tti_req = mac->UL_tti_req[CC_id]; - - sched_info->TX_req = &mac->TX_req[CC_id]; - */ -#ifdef DUMP_FAPI - dump_dl(sched_info); -#endif - - AssertFatal(ifi->NR_Schedule_response != NULL, "nr_schedule_response is null (mod %d, cc %d)\n", module_id, CC_id); - ifi->NR_Schedule_response(sched_info); - - LOG_D(NR_PHY, - "NR_Schedule_response: SFN SLOT:%d %d dl_pdus:%d\n", - sched_info->frame, - sched_info->slot, - sched_info->DL_req.dl_tti_request_body.nPDUs); + module_id_t module_id = 0; + int CC_id = 0; + reset_sched_response(rsp, ind->sfn, ind->slot, module_id, CC_id); + gNB_dlsch_ulsch_scheduler(rsp->module_id, ind->sfn, ind->slot, rsp); } void NR_UL_indication(NR_UL_IND_t *UL_info) { @@ -515,3 +489,33 @@ NR_IF_Module_t *NR_IF_Module_init(int Mod_id) { return nr_if_inst[Mod_id]; } + +void reset_sched_response(NR_Sched_Rsp_t *sched_response, int frame, int slot, int module_id, int CC_id) +{ + sched_response->module_id = module_id; + sched_response->CC_id = CC_id; + sched_response->frame = frame; + sched_response->slot = slot; + + + nfapi_nr_dl_tti_request_t *DL_req = &sched_response->DL_req; + DL_req->SFN = frame; + DL_req->Slot = slot; + DL_req->dl_tti_request_body.nPDUs = 0; + DL_req->dl_tti_request_body.nGroup = 0; + + nfapi_nr_ul_dci_request_t *UL_dci_req = &sched_response->UL_dci_req; + UL_dci_req->SFN = frame; + UL_dci_req->Slot = slot; + UL_dci_req->numPdus = 0; + + nfapi_nr_ul_tti_request_t *UL_tti_req = &sched_response->UL_tti_req; + UL_tti_req->SFN = frame; + UL_tti_req->Slot = slot; + UL_tti_req->n_pdus = 0; + + nfapi_nr_tx_data_request_t *TX_req = &sched_response->TX_req; + TX_req->SFN = frame; + TX_req->Slot = slot; + TX_req->Number_of_PDUs = 0; +} diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.h b/openair2/NR_PHY_INTERFACE/NR_IF_Module.h index 52a0f28871..fe5c0738c4 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.h +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.h @@ -111,6 +111,7 @@ typedef struct { /// Pointers to DL SDUs nfapi_nr_tx_data_request_t TX_req; } NR_Sched_Rsp_t; +void reset_sched_response(NR_Sched_Rsp_t *sched_response, int frame, int slot, int module_id, int CC_id); typedef struct { uint8_t Mod_id; @@ -122,7 +123,7 @@ typedef struct NR_IF_Module_s { //define the function pointer void (*NR_UL_indication)(NR_UL_IND_t *UL_INFO); void (*NR_Schedule_response)(NR_Sched_Rsp_t *Sched_INFO); - void (*NR_slot_indication)(module_id_t module_idP, int CC_id, int frame, int slot); + void (*NR_slot_indication)(const nfapi_nr_slot_indication_scf_t *ind, NR_Sched_Rsp_t *rsp); void (*NR_PHY_config_req)(NR_PHY_Config_t *config_INFO); uint32_t CC_mask; uint16_t current_frame; From eefb9da69a48ce8ca85e7647fea39bf8d8dcf5dc Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 12 Nov 2025 19:07:51 +0100 Subject: [PATCH 233/275] Reimplement nFAPI message exchange after msgDataTx removal Reimplement nFAPI message exchange between L1 and L2 following the changes in parent commit removing msgDataTx. Avoid direct calls and use NR_IF_module fptrs to avoid linking problems. allowing to remove some function definitions that are not needed. --- doc/nfapi.md | 4 +- executables/lte-softmodem.c | 2 - nfapi/oai_integration/nfapi_pnf.c | 11 +- nfapi/oai_integration/nfapi_pnf.h | 3 +- nfapi/oai_integration/nfapi_vnf.c | 98 ++++---- nfapi/open-nFAPI/pnf/inc/pnf_p7.h | 10 +- .../pnf/public_inc/nfapi_pnf_interface.h | 12 +- nfapi/open-nFAPI/pnf/src/pnf_p7.c | 214 +++++++++--------- nfapi/open-nFAPI/pnf/src/pnf_p7_interface.c | 21 +- nfapi/open-nFAPI/pnf/tests/pnf_cunit_main.c | 6 - openair2/NR_PHY_INTERFACE/NR_IF_Module.c | 19 +- openair2/NR_PHY_INTERFACE/NR_IF_Module.h | 4 +- 12 files changed, 216 insertions(+), 188 deletions(-) diff --git a/doc/nfapi.md b/doc/nfapi.md index 214aa2799f..8b9197b056 100644 --- a/doc/nfapi.md +++ b/doc/nfapi.md @@ -242,10 +242,10 @@ To see the (any) periodical output at the PNF, define `NFAPI_TRACE_LEVEL=info`. This output shows: ``` -41056.739654 [I] 3556767424: pnf_p7_slot_ind: [P7:1] msgs ontime 489 thr DL 0.06 UL 0.01 msg late 0 (vtime) +41056.739654 [I] 3556767424: nr_pnf_p7_get_msgs: [P7:1] msgs ontime 489 thr DL 0.06 UL 0.01 msg late 0 (vtime) ``` -The first numbers are timestamps. `pnf_p7_slot_ind` is the name of the +The first numbers are timestamps. `nr_pnf_p7_get_msgs` is the name of the functions that prints the output. `[P7:1]` refers to the fact that these are information on P7, of PHY ID 1. Finally, `msgs ontime 489` means that in the last window (since the last print), 489 messages arrived at the PNF in total. diff --git a/executables/lte-softmodem.c b/executables/lte-softmodem.c index 80b532607e..0932702910 100644 --- a/executables/lte-softmodem.c +++ b/executables/lte-softmodem.c @@ -199,8 +199,6 @@ void nr_schedule_ul_dci_req() {}; void nr_schedule_tx_req() {}; void nr_schedule_ul_tti_req() {}; void nr_slot_select() {}; -void NR_UL_indication(NR_UL_IND_t *UL_INFO) {}; -void gNB_dlsch_ulsch_scheduler() {}; /*------------------------------------------------------------------------*/ diff --git a/nfapi/oai_integration/nfapi_pnf.c b/nfapi/oai_integration/nfapi_pnf.c index bad2d036c9..79c0d689c7 100644 --- a/nfapi/oai_integration/nfapi_pnf.c +++ b/nfapi/oai_integration/nfapi_pnf.c @@ -2314,7 +2314,7 @@ static void maybe_slow_down_pnf(int mu) last_execution = current_execution; } -void handle_nr_slot_ind(uint16_t sfn, uint16_t slot) +void handle_nr_slot_ind(uint16_t sfn, uint16_t slot, NR_Sched_Rsp_t *sched_resp) { nfapi_pnf_p7_config_t *config = p7_config_g; pnf_p7_t *_this = (pnf_p7_t *)(config); @@ -2345,7 +2345,14 @@ void handle_nr_slot_ind(uint16_t sfn, uint16_t slot) oai_nfapi_nr_slot_indication(&ind); // copy data from appropriate p7 slot buffers into channel structures for PHY processing - nfapi_pnf_p7_slot_ind(config, config->phy_id, sfn, slot); + nfapi_pnf_p7_get_msgs(config, + config->phy_id, + sfn, + slot, + &sched_resp->DL_req, + &sched_resp->UL_tti_req, + &sched_resp->UL_dci_req, + &sched_resp->TX_req); return; } diff --git a/nfapi/oai_integration/nfapi_pnf.h b/nfapi/oai_integration/nfapi_pnf.h index 0ba0676f10..ad2df72ad3 100644 --- a/nfapi/oai_integration/nfapi_pnf.h +++ b/nfapi/oai_integration/nfapi_pnf.h @@ -28,7 +28,8 @@ void configure_nfapi_pnf(char *vnf_ip_addr, int vnf_p5_port, char *pnf_ip_addr, 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); void oai_subframe_ind(uint16_t sfn, uint16_t sf); -void handle_nr_slot_ind(uint16_t sfn, uint16_t slot); +struct NR_Sched_Rsp; +void handle_nr_slot_ind(uint16_t sfn, uint16_t slot, struct NR_Sched_Rsp *resp); void sfnslot_add_slot(int mu, uint16_t *sfn, uint16_t *slot, int offset); int oai_nfapi_nr_slot_indication(nfapi_nr_slot_indication_scf_t *ind); diff --git a/nfapi/oai_integration/nfapi_vnf.c b/nfapi/oai_integration/nfapi_vnf.c index 5ea99de7ea..6f73121c68 100644 --- a/nfapi/oai_integration/nfapi_vnf.c +++ b/nfapi/oai_integration/nfapi_vnf.c @@ -971,65 +971,63 @@ int phy_cqi_indication(struct nfapi_vnf_p7_config *config, nfapi_cqi_indication_ //NR phy indication -NR_Sched_Rsp_t g_sched_resp; -void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slot, NR_Sched_Rsp_t* sched_info); int oai_nfapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req); int oai_nfapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req); int oai_nfapi_tx_data_req(nfapi_nr_tx_data_request_t* tx_data_req); int oai_nfapi_ul_dci_req(nfapi_nr_ul_dci_request_t* ul_dci_req); -int trigger_scheduler(nfapi_nr_slot_indication_scf_t *slot_ind) -{ - // Call into the scheduler (this is hardcoded and should be init properly!) - // memset(sched_resp, 0, sizeof(*sched_resp)); - gNB_dlsch_ulsch_scheduler(0, slot_ind->sfn, slot_ind->slot, &g_sched_resp); - -#ifdef ENABLE_AERIAL - bool send_slt_resp = false; - if (g_sched_resp.DL_req.dl_tti_request_body.nPDUs> 0) { - oai_fapi_dl_tti_req(&g_sched_resp.DL_req); - send_slt_resp = true; - } - if (g_sched_resp.UL_tti_req.n_pdus > 0) { - oai_fapi_ul_tti_req(&g_sched_resp.UL_tti_req); - send_slt_resp = true; - } - if (g_sched_resp.TX_req.Number_of_PDUs > 0) { - oai_fapi_tx_data_req(&g_sched_resp.TX_req); - send_slt_resp = true; - } - if (g_sched_resp.UL_dci_req.numPdus > 0) { - oai_fapi_ul_dci_req(&g_sched_resp.UL_dci_req); - send_slt_resp = true; - } - if (send_slt_resp) { - oai_fapi_send_end_request(0,slot_ind->sfn, slot_ind->slot); - } -#else - if (g_sched_resp.DL_req.dl_tti_request_body.nPDUs > 0) - oai_nfapi_dl_tti_req(&g_sched_resp.DL_req); - - if (g_sched_resp.UL_tti_req.n_pdus > 0) - oai_nfapi_ul_tti_req(&g_sched_resp.UL_tti_req); - - if (g_sched_resp.TX_req.Number_of_PDUs > 0) - oai_nfapi_tx_data_req(&g_sched_resp.TX_req); - - if (g_sched_resp.UL_dci_req.numPdus > 0) - oai_nfapi_ul_dci_req(&g_sched_resp.UL_dci_req); -#endif - - NR_UL_IND_t ind = {.frame = slot_ind->sfn, .slot = slot_ind->slot, }; - NR_UL_indication(&ind); - - return 1; -} - int phy_nr_slot_indication(nfapi_nr_slot_indication_scf_t *ind) { LOG_D(MAC, "VNF SFN/Slot %d.%d \n", ind->sfn, ind->slot); - trigger_scheduler(ind); + // this variable is very big (multiple MB), so we put it into static storage + // to not overflow the stack while still having it in local (function) scope + // also, phy_nr_slot_indication() is only executed by one thread, serially + static NR_Sched_Rsp_t sched_response; + NR_IF_Module_t *ifi = RC.nrmac[0]->if_inst; + ifi->NR_slot_indication(ind, &sched_response); + +#ifdef ENABLE_AERIAL + bool send_slt_resp = false; + if (sched_response.DL_req.dl_tti_request_body.nPDUs> 0) { + oai_fapi_dl_tti_req(&sched_response.DL_req); + send_slt_resp = true; + } + if (sched_response.UL_tti_req.n_pdus > 0) { + oai_fapi_ul_tti_req(&sched_response.UL_tti_req); + send_slt_resp = true; + } + if (sched_response.TX_req.Number_of_PDUs > 0) { + oai_fapi_tx_data_req(&sched_response.TX_req); + send_slt_resp = true; + } + if (sched_response.UL_dci_req.numPdus > 0) { + oai_fapi_ul_dci_req(&sched_response.UL_dci_req); + send_slt_resp = true; + } + if (send_slt_resp) { + oai_fapi_send_end_request(0, ind->sfn, ind->slot); + } +#else + if (sched_response.DL_req.dl_tti_request_body.nPDUs > 0) + oai_nfapi_dl_tti_req(&sched_response.DL_req); + + if (sched_response.UL_tti_req.n_pdus > 0) + oai_nfapi_ul_tti_req(&sched_response.UL_tti_req); + + if (sched_response.TX_req.Number_of_PDUs > 0) + oai_nfapi_tx_data_req(&sched_response.TX_req); + + if (sched_response.UL_dci_req.numPdus > 0) + oai_nfapi_ul_dci_req(&sched_response.UL_dci_req); +#endif + + /* the below works because the function behind the callback collects + * messages from queue into which messages have been copied. + * TODO we should have different callbacks for received messages and call + * into the scheduler separately for each message instead of one big one. */ + NR_UL_IND_t ul_ind = {.frame = ind->sfn, .slot = ind->slot, }; + ifi->NR_UL_indication(&ul_ind); return 1; } diff --git a/nfapi/open-nFAPI/pnf/inc/pnf_p7.h b/nfapi/open-nFAPI/pnf/inc/pnf_p7.h index 5805e07445..3040d9de09 100644 --- a/nfapi/open-nFAPI/pnf/inc/pnf_p7.h +++ b/nfapi/open-nFAPI/pnf/inc/pnf_p7.h @@ -145,8 +145,14 @@ int pnf_nr_p7_message_pump(pnf_p7_t* pnf_p7); int pnf_p7_pack_and_send_p7_message(pnf_p7_t* pnf_p7, nfapi_p7_message_header_t* msg, uint32_t msg_len); int pnf_p7_send_message(pnf_p7_t* pnf_p7, uint8_t* msg, uint32_t msg_len); - -int pnf_p7_slot_ind(pnf_p7_t* config, uint16_t phy_id, uint16_t sfn, uint16_t slot); +int nr_pnf_p7_get_msgs(pnf_p7_t* pnf_p7, + uint16_t phy_id, + uint16_t sfn, + uint16_t slot, + nfapi_nr_dl_tti_request_t* ret_dl_tti, + nfapi_nr_ul_tti_request_t* ret_ul_tti, + nfapi_nr_ul_dci_request_t* ret_ul_dci, + nfapi_nr_tx_data_request_t* ret_tx_data); int pnf_p7_subframe_ind(pnf_p7_t* config, uint16_t phy_id, uint16_t sfn_sf); int nfapi_pnf_p7_nr_slot_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_slot_indication_scf_t* ind); int nfapi_pnf_p7_nr_rx_data_ind(nfapi_pnf_p7_config_t* config, nfapi_nr_rx_data_indication_t* ind); diff --git a/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h b/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h index 957e032f69..6576f6406e 100644 --- a/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h +++ b/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h @@ -856,7 +856,7 @@ int nfapi_pnf_p7_start(nfapi_pnf_p7_config_t* config); */ int nfapi_pnf_p7_stop(nfapi_pnf_p7_config_t* config); -/*! NR Slot indication +/*! NR get stuff * message copied from nfapi_pnf_p7_subframe_ind * \param config A pointer to a PNF P7 config * \param phy_id The phy_id for the phy instance @@ -868,8 +868,14 @@ int nfapi_pnf_p7_stop(nfapi_pnf_p7_config_t* config); * * If messages are not in the subframe buffer, they dummy subframe messages will be sent */ -int nfapi_pnf_p7_slot_ind(nfapi_pnf_p7_config_t* config, uint16_t phy_id, uint16_t sfn, uint16_t slot); - +int nfapi_pnf_p7_get_msgs(nfapi_pnf_p7_config_t* config, + uint16_t phy_id, + uint16_t sfn, + uint16_t slot, + nfapi_nr_dl_tti_request_t* ret_dl_tti, + nfapi_nr_ul_tti_request_t* ret_ul_tti, + nfapi_nr_ul_dci_request_t* ret_ul_dci, + nfapi_nr_tx_data_request_t* ret_tx_data); /*! Subframe indication * \param config A pointer to a PNF P7 config diff --git a/nfapi/open-nFAPI/pnf/src/pnf_p7.c b/nfapi/open-nFAPI/pnf/src/pnf_p7.c index e2b6d253d2..197bac22b7 100644 --- a/nfapi/open-nFAPI/pnf/src/pnf_p7.c +++ b/nfapi/open-nFAPI/pnf/src/pnf_p7.c @@ -672,145 +672,157 @@ void send_dummy_subframe(pnf_p7_t* pnf_p7, uint16_t sfn_sf) } } +int nr_pnf_p7_get_msgs(pnf_p7_t* pnf_p7, + uint16_t phy_id, + uint16_t sfn, + uint16_t slot, + nfapi_nr_dl_tti_request_t* ret_dl_tti, + nfapi_nr_ul_tti_request_t* ret_ul_tti, + nfapi_nr_ul_dci_request_t* ret_ul_dci, + nfapi_nr_tx_data_request_t* ret_tx_data) +{ + if (pthread_mutex_lock(&(pnf_p7->mutex)) != 0) { + NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to lock mutex\n"); + return -1; + } -int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t slot) -{ - //This function is aligned with rx sfn/slot + // save the curren time, sfn and slot + // TODO useful? + pnf_p7->slot_start_time_hr = pnf_get_current_time_hr(); - // We could either send an event to the p7 thread have have it run the - // subframe or we could handle it here and lock access to the subframe - // buffers. If we do it on the p7 thread then we run the risk of blocking - // on the udp send. - - // todo : consider a more efficent lock mechasium - //uint16_t NUM_SLOTS = 20;//10* 2^mu - - if(pthread_mutex_lock(&(pnf_p7->mutex)) != 0) - { - NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to lock mutex\n"); - return -1; - } + // We align the pnf_p7 sfn/slot with tx sfn/slot, and vnf is synced with pnf_p7 sfn/slot. This is so that the scheduler runs + // slot_ahead from rx thread. + pnf_p7->sfn = sfn; + pnf_p7->slot = slot; - // save the curren time, sfn and slot - pnf_p7->slot_start_time_hr = pnf_get_current_time_hr(); + uint32_t tx_slot_dec = NFAPI_SFNSLOT2DEC(pnf_p7->mu, sfn, slot); + uint8_t buffer_index_tx = tx_slot_dec % NFAPI_SLOTNUM(pnf_p7->mu); - //We align the pnf_p7 sfn/slot with tx sfn/slot, and vnf is synced with pnf_p7 sfn/slot. This is so that the scheduler runs slot_ahead from rx thread. + // If the subframe_buffer has been configured + if (pnf_p7->_public.slot_buffer_size != 0) // for now value is same as sf_buffer_size + { + // apply the shift to the incoming sfn_sf + if (pnf_p7->slot_shift != 0) // see in vnf_build_send_dl_node_sync + { + uint16_t shifted_slot = slot + pnf_p7->slot_shift; - pnf_p7->sfn = sfn; - pnf_p7->slot = slot; + // adjust for wrap-around + if (shifted_slot < 0) + shifted_slot += NFAPI_MAX_SFNSLOTDEC(pnf_p7->mu); + else if (shifted_slot > NFAPI_MAX_SFNSLOTDEC(pnf_p7->mu)) + shifted_slot -= NFAPI_MAX_SFNSLOTDEC(pnf_p7->mu); - uint32_t tx_slot_dec = NFAPI_SFNSLOT2DEC(pnf_p7->mu, sfn,slot); - uint8_t buffer_index_tx = tx_slot_dec % NFAPI_SLOTNUM(pnf_p7->mu); + //NFAPI_TRACE(NFAPI_TRACE_INFO, "Applying shift %d to sfn/slot (%d -> %d)\n", pnf_p7->sfn_slot_shift, + //NFAPI_SFNSF2DEC(sfn_slot), shifted_sfn_slot); + slot = shifted_slot; - // If the subframe_buffer has been configured - if(pnf_p7->_public.slot_buffer_size!= 0) // for now value is same as sf_buffer_size - { + // + // why does the shift not apply to pnf_p7->sfn_sf??? + // - // apply the shift to the incoming sfn_sf - if(pnf_p7->slot_shift != 0) // see in vnf_build_send_dl_node_sync - { - uint16_t shifted_slot = slot + pnf_p7->slot_shift; + pnf_p7->slot_shift = 0; + } - // adjust for wrap-around - if(shifted_slot < 0) - shifted_slot += NFAPI_MAX_SFNSLOTDEC(pnf_p7->mu); - else if(shifted_slot > NFAPI_MAX_SFNSLOTDEC(pnf_p7->mu)) - shifted_slot -= NFAPI_MAX_SFNSLOTDEC(pnf_p7->mu); - - // NFAPI_TRACE(NFAPI_TRACE_INFO, "Applying shift %d to sfn/slot (%d -> %d)\n", pnf_p7->sfn_slot_shift, NFAPI_SFNSF2DEC(sfn_slot), shifted_sfn_slot); - slot = shifted_slot; - - // - // why does the shift not apply to pnf_p7->sfn_sf??? - // - - pnf_p7->slot_shift = 0; - } - - nfapi_pnf_p7_slot_buffer_t* tx_slot_buffer = &(pnf_p7->slot_buffer[buffer_index_tx]); + nfapi_pnf_p7_slot_buffer_t* tx_slot_buffer = &(pnf_p7->slot_buffer[buffer_index_tx]); + ret_dl_tti->SFN = sfn; + ret_dl_tti->Slot = slot; + ret_dl_tti->dl_tti_request_body.nPDUs = 0; + ret_tx_data->SFN = sfn; + ret_tx_data->Slot = slot; + ret_tx_data->Number_of_PDUs = 0; nfapi_nr_dl_tti_request_t* dl_tti_req = &tx_slot_buffer->dl_tti_req; - nfapi_nr_tx_data_request_t* tx_data_req = &tx_slot_buffer->tx_data_req; - if (dl_tti_req->dl_tti_request_body.nPDUs > 0 && dl_tti_req->SFN == sfn && dl_tti_req->Slot == slot) { - DevAssert(pnf_p7->_public.dl_tti_req_fn != NULL); - DevAssert(pnf_p7->_public.tx_data_req_fn != NULL); - if (tx_data_req->Number_of_PDUs > 0 && tx_data_req->SFN == sfn && tx_data_req->Slot == slot) { - (pnf_p7->_public.dl_tti_req_fn)(NULL, &(pnf_p7->_public), dl_tti_req); - (pnf_p7->_public.tx_data_req_fn)(&(pnf_p7->_public), tx_data_req); - } else { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%4d.%2d no corresponding tx_data.request for dl_tti.request, dropping\n", sfn, slot); + nfapi_nr_tx_data_request_t* txd = &tx_slot_buffer->tx_data_req; + if (dl_tti_req->SFN == sfn && dl_tti_req->Slot == slot && txd->SFN == sfn && txd->Slot == slot) { + nfapi_nr_dl_tti_request_body_t* p_tti = &dl_tti_req->dl_tti_request_body; + int ret_idx = 0; + for (int i = 0; i < p_tti->nPDUs; ++i) { + int tx_index = p_tti->dl_tti_pdu_list[i].pdsch_pdu.pdsch_pdu_rel15.pduIndex - 1; + if (tx_index >= txd->Number_of_PDUs) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, + "%4d.%2d no corresponding tx_data.request for dl_tti.request index %d, dropping\n", + sfn, + slot, + tx_index); + continue; + } + ret_dl_tti->dl_tti_request_body.dl_tti_pdu_list[ret_idx] = p_tti->dl_tti_pdu_list[i]; + // TODO avoid huge copy + ret_tx_data->pdu_list[ret_idx] = txd->pdu_list[i]; + ret_idx++; } + ret_dl_tti->dl_tti_request_body.nPDUs = ret_idx; + ret_tx_data->Number_of_PDUs = ret_idx; + tx_slot_buffer->dl_tti_req.SFN = -1; tx_slot_buffer->dl_tti_req.Slot = -1; tx_slot_buffer->tx_data_req.SFN = -1; tx_slot_buffer->tx_data_req.Slot = -1; } - if (tx_data_req->Number_of_PDUs > 0 && tx_data_req->SFN == sfn && tx_data_req->Slot == slot) { - // there is a tx_data.request in this slot, but no corresponding - // dl_tti.request (when they need to come pairwise) - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%4d.%2d no corresponding dl_tti.request for tx_data.request, dropping\n", sfn, slot); - tx_slot_buffer->tx_data_req.SFN = -1; - tx_slot_buffer->tx_data_req.Slot = -1; - } - if(tx_slot_buffer->ul_tti_req.n_pdus > 0 && tx_slot_buffer->ul_tti_req.SFN == sfn && tx_slot_buffer->ul_tti_req.Slot == slot) - { - DevAssert(pnf_p7->_public.ul_tti_req_fn != NULL); - (pnf_p7->_public.ul_tti_req_fn)(NULL, &(pnf_p7->_public), &tx_slot_buffer->ul_tti_req); + ret_ul_tti->SFN = sfn; + ret_ul_tti->Slot = slot; + ret_ul_tti->n_pdus = 0; + if (tx_slot_buffer->ul_tti_req.SFN == sfn && tx_slot_buffer->ul_tti_req.Slot == slot) { + nfapi_nr_ul_tti_request_t* p = &tx_slot_buffer->ul_tti_req; + ret_ul_tti->n_pdus = p->n_pdus; + for (int i = 0; i < p->n_pdus; ++i) + ret_ul_tti->pdus_list[i] = p->pdus_list[i]; tx_slot_buffer->ul_tti_req.SFN = -1; tx_slot_buffer->ul_tti_req.Slot = -1; - } + } - if(tx_slot_buffer->ul_dci_req.numPdus > 0 && tx_slot_buffer->ul_dci_req.SFN == sfn && tx_slot_buffer->ul_dci_req.Slot == slot) - { - DevAssert(pnf_p7->_public.ul_dci_req_fn != NULL); - (pnf_p7->_public.ul_dci_req_fn)(NULL, &(pnf_p7->_public), &tx_slot_buffer->ul_dci_req); + ret_ul_dci->SFN = sfn; + ret_ul_dci->Slot = slot; + ret_ul_dci->numPdus = 0; + if (tx_slot_buffer->ul_dci_req.SFN == sfn && tx_slot_buffer->ul_dci_req.Slot == slot) { + nfapi_nr_ul_dci_request_t* p = &tx_slot_buffer->ul_dci_req; + ret_ul_dci->numPdus = p->numPdus; + for (int i = 0; i < p->numPdus; ++i) + ret_ul_dci->ul_dci_pdu_list[i] = p->ul_dci_pdu_list[i]; tx_slot_buffer->ul_dci_req.SFN = -1; tx_slot_buffer->ul_dci_req.Slot = -1; - } + } - //send the periodic timing info if configured - if(pnf_p7->_public.timing_info_mode_periodic && (pnf_p7->timing_info_period_counter++) == pnf_p7->_public.timing_info_period) - { - pnf_nr_pack_and_send_timing_info(pnf_p7); + // send the periodic timing info if configured + if (pnf_p7->_public.timing_info_mode_periodic && (pnf_p7->timing_info_period_counter++) == pnf_p7->_public.timing_info_period) { + pnf_nr_pack_and_send_timing_info(pnf_p7); - pnf_p7->timing_info_period_counter = 0; - } - else if(pnf_p7->_public.timing_info_mode_aperiodic && pnf_p7->timing_info_aperiodic_send) - { - pnf_nr_pack_and_send_timing_info(pnf_p7); + pnf_p7->timing_info_period_counter = 0; + } else if (pnf_p7->_public.timing_info_mode_aperiodic && pnf_p7->timing_info_aperiodic_send) { + pnf_nr_pack_and_send_timing_info(pnf_p7); - pnf_p7->timing_info_aperiodic_send = 0; - } - else - { - pnf_p7->timing_info_ms_counter++; - } - - } + pnf_p7->timing_info_aperiodic_send = 0; + } else { + pnf_p7->timing_info_ms_counter++; + } + } if (sfn % 128 == 0 && slot == 0) { pnf_p7_nr_stats_t* s = &pnf_p7->nr_stats; int ontime = s->dl_tti.ontime + s->ul_tti.ontime + s->ul_dci.ontime + s->tx_data.ontime; int late = s->dl_tti.late + s->ul_tti.late + s->ul_dci.late + s->tx_data.late; - float dl_thr = (float) s->dl.bytes * 8 / 1000 / 1000 / 1.28f; // every 128 frames = 1.28 s - float ul_thr = (float) s->ul.bytes * 8 / 1000 / 1000 / 1.28f; - NFAPI_TRACE(NFAPI_TRACE_INFO, "[P7:%d] msgs ontime %d thr DL %.2f UL %.2f msg late %d (vtime)\n", pnf_p7->_public.phy_id, ontime, dl_thr, ul_thr, late); + float dl_thr = (float)s->dl.bytes * 8 / 1000 / 1000 / 1.28f; // every 128 frames = 1.28 s + float ul_thr = (float)s->ul.bytes * 8 / 1000 / 1000 / 1.28f; + NFAPI_TRACE(NFAPI_TRACE_INFO, + "[P7:%d] msgs ontime %d thr DL %.2f UL %.2f msg late %d (vtime)\n", + pnf_p7->_public.phy_id, + ontime, + dl_thr, + ul_thr, + late); memset(s, 0, sizeof(*s)); } - if(pthread_mutex_unlock(&(pnf_p7->mutex)) != 0) - { - NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to unlock mutex\n"); - return -1; - } + if (pthread_mutex_unlock(&(pnf_p7->mutex)) != 0) { + NFAPI_TRACE(NFAPI_TRACE_INFO, "failed to unlock mutex\n"); + return -1; + } - return 0; + return 0; } - - int pnf_p7_subframe_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn_sf) { // We could either send an event to the p7 thread have have it run the diff --git a/nfapi/open-nFAPI/pnf/src/pnf_p7_interface.c b/nfapi/open-nFAPI/pnf/src/pnf_p7_interface.c index b021e67fc9..af02ef7a4e 100644 --- a/nfapi/open-nFAPI/pnf/src/pnf_p7_interface.c +++ b/nfapi/open-nFAPI/pnf/src/pnf_p7_interface.c @@ -89,15 +89,22 @@ int nfapi_pnf_p7_stop(nfapi_pnf_p7_config_t* config) return 0; } -int nfapi_pnf_p7_slot_ind(nfapi_pnf_p7_config_t* config, uint16_t phy_id, uint16_t sfn, uint16_t slot) +int nfapi_pnf_p7_get_msgs(nfapi_pnf_p7_config_t* config, + uint16_t phy_id, + uint16_t sfn, + uint16_t slot, + nfapi_nr_dl_tti_request_t* ret_dl_tti, + nfapi_nr_ul_tti_request_t* ret_ul_tti, + nfapi_nr_ul_dci_request_t* ret_ul_dci, + nfapi_nr_tx_data_request_t* ret_tx_data) { - // Verify that config is not null - if(config == 0) - return -1; - - pnf_p7_t* _this = (pnf_p7_t*)(config); + // Verify that config is not null + if (config == 0) + return -1; - return pnf_p7_slot_ind(_this, phy_id, sfn, slot); + pnf_p7_t* _this = (pnf_p7_t*)(config); + + return nr_pnf_p7_get_msgs(_this, phy_id, sfn, slot, ret_dl_tti, ret_ul_tti, ret_ul_dci, ret_tx_data); } int nfapi_pnf_p7_subframe_ind(nfapi_pnf_p7_config_t* config, uint16_t phy_id, uint16_t sfn_sf) diff --git a/nfapi/open-nFAPI/pnf/tests/pnf_cunit_main.c b/nfapi/open-nFAPI/pnf/tests/pnf_cunit_main.c index bd6bedbea5..3460d4d9d7 100644 --- a/nfapi/open-nFAPI/pnf/tests/pnf_cunit_main.c +++ b/nfapi/open-nFAPI/pnf/tests/pnf_cunit_main.c @@ -1210,12 +1210,6 @@ void send_dl_subframe_msgs_interleaved(int p7Sock, int phy_id, struct sockaddr_i //free(req.tx_request_body.tx_pdu_list); } -void send_slot_indication(phy_info_t* phy_info) -{ - // DONE: add sfn and slot as members in the phy_info - nfapi_pnf_p7_slot_ind(phy_info->config, phy_info->phy_id, phy_info->sfn, phy_info->slot); -} - void send_subframe_indication(phy_info_t* phy_info) { nfapi_pnf_p7_subframe_ind(phy_info->config, phy_info->phy_id, phy_info->sfn_sf); diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index faf07154ee..9c53b50b76 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -379,15 +379,13 @@ static void match_crc_rx_pdu(nfapi_nr_rx_data_indication_t *rx_ind, nfapi_nr_crc } } -extern void handle_nr_slot_ind(uint16_t sfn, uint16_t slot); +extern void handle_nr_slot_ind(uint16_t sfn, uint16_t slot, NR_Sched_Rsp_t *sched_response); static void pnf_send_slot_ind(const nfapi_nr_slot_indication_scf_t *ind, NR_Sched_Rsp_t *rsp) { module_id_t module_id = 0; int CC_id = 0; reset_sched_response(rsp, ind->sfn, ind->slot, module_id, CC_id); - handle_nr_slot_ind(ind->sfn, ind->slot); - - // TODO copy messages from FAPI buffers + handle_nr_slot_ind(ind->sfn, ind->slot, rsp); } static void run_scheduler_monolithic(const nfapi_nr_slot_indication_scf_t *ind, NR_Sched_Rsp_t *rsp) @@ -398,7 +396,8 @@ static void run_scheduler_monolithic(const nfapi_nr_slot_indication_scf_t *ind, gNB_dlsch_ulsch_scheduler(rsp->module_id, ind->sfn, ind->slot, rsp); } -void NR_UL_indication(NR_UL_IND_t *UL_info) { +static void NR_UL_indication(NR_UL_IND_t *UL_info) +{ AssertFatal(UL_info!=NULL,"UL_info is null\n"); module_id_t module_id = UL_info->module_id; int CC_id = UL_info->CC_id; @@ -475,12 +474,14 @@ NR_IF_Module_t *NR_IF_Module_init(int Mod_id) { nr_if_inst[Mod_id]->CC_mask=0; nr_if_inst[Mod_id]->NR_UL_indication = NR_UL_indication; - if (NFAPI_MODE == NFAPI_MONOLITHIC) + if (NFAPI_MODE == NFAPI_MONOLITHIC) { nr_if_inst[Mod_id]->NR_slot_indication = run_scheduler_monolithic; - else if (NFAPI_MODE == NFAPI_MODE_PNF) + } else if (NFAPI_MODE == NFAPI_MODE_PNF) { nr_if_inst[Mod_id]->NR_slot_indication = pnf_send_slot_ind; - else // NFAPI_MODE_VNF - NULL; + } else { // NFAPI_MODE_VNF + DevAssert(NFAPI_MODE == NFAPI_MODE_VNF || NFAPI_MODE == NFAPI_MODE_AERIAL); + nr_if_inst[Mod_id]->NR_slot_indication = run_scheduler_monolithic; + } AssertFatal(pthread_mutex_init(&nr_if_inst[Mod_id]->if_mutex,NULL)==0, "allocation of nr_if_inst[%d]->if_mutex fails\n",Mod_id); } diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.h b/openair2/NR_PHY_INTERFACE/NR_IF_Module.h index fe5c0738c4..ca7e705f4a 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.h +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.h @@ -91,7 +91,7 @@ typedef struct { // Downlink slot P7 -typedef struct { +typedef struct NR_Sched_Rsp { /// the ID of this sched_response - used by sched_reponse memory management int sched_response_id; /// Module ID @@ -137,8 +137,6 @@ NR_IF_Module_t *NR_IF_Module_init(int Mod_id); void NR_IF_Module_kill(int Mod_id); -void NR_UL_indication(NR_UL_IND_t *UL_INFO); - /*Interface for Downlink, transmitting the DLSCH SDU, DCI SDU*/ void NR_Schedule_Response(NR_Sched_Rsp_t *Sched_INFO); From 5ac381dd5933c3e2098533203fe627b416b9c9ca Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 13 Nov 2025 19:33:17 +0100 Subject: [PATCH 234/275] Remove dead code --- CMakeLists.txt | 3 - ci-scripts/datalog_rt_stats.100.2x2.yaml | 2 - ci-scripts/datalog_rt_stats.1x1.60.yaml | 2 - ci-scripts/datalog_rt_stats.2x2.yaml | 2 - ci-scripts/datalog_rt_stats.60.2x2.yaml | 2 - ci-scripts/datalog_rt_stats.default.yaml | 2 - doc/Doxyfile | 5 - executables/lte-softmodem.c | 4 - executables/lte-uesoftmodem.c | 4 - executables/nr-gnb.c | 11 - executables/nr-softmodem.c | 2 - nfapi/oai_integration/nfapi_pnf.c | 57 +---- .../nrLDPC_coding_segment_decoder.c | 1 - .../nrLDPC_coding_xdma/nrLDPC_coding_xdma.c | 1 - openair1/PHY/INIT/nr_init.c | 3 - openair1/PHY/NR_TRANSPORT/nr_dlsch.h | 3 - openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c | 60 ----- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 1 - openair1/PHY/defs_gNB.h | 14 -- openair1/SCHED_NR/fapi_nr_l1.c | 213 ------------------ openair1/SCHED_NR/fapi_nr_l1.h | 37 --- openair1/SCHED_NR/nr_prach_procedures.c | 1 - openair1/SCHED_NR/phy_procedures_nr_gNB.c | 1 - openair2/NR_PHY_INTERFACE/NR_IF_Module.c | 3 - openair2/NR_PHY_INTERFACE/NR_IF_Module.h | 4 - openair2/NR_PHY_INTERFACE/nr_sched_response.c | 157 ------------- openair2/NR_PHY_INTERFACE/nr_sched_response.h | 32 --- 27 files changed, 4 insertions(+), 623 deletions(-) delete mode 100644 openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c delete mode 100644 openair1/SCHED_NR/fapi_nr_l1.c delete mode 100644 openair1/SCHED_NR/fapi_nr_l1.h delete mode 100644 openair2/NR_PHY_INTERFACE/nr_sched_response.c delete mode 100644 openair2/NR_PHY_INTERFACE/nr_sched_response.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c5f54f1e67..8f8a6006bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -652,7 +652,6 @@ add_library(SCHED_LIB ${SCHED_SRC}) target_link_libraries(SCHED_LIB PRIVATE asn1_lte_rrc_hdrs) set(SCHED_NR_SRC - ${OPENAIR1_DIR}/SCHED_NR/fapi_nr_l1.c ${OPENAIR1_DIR}/SCHED_NR/phy_procedures_nr_gNB.c ${OPENAIR1_DIR}/SCHED_NR/nr_prach_procedures.c ${OPENAIR1_DIR}/SCHED_NR/phy_frame_config_nr.c @@ -939,7 +938,6 @@ set(PHY_SRC_UE ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_dci.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_dci_tools.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_dlsch.c - ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_dlsch_tools.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_dlsch_coding.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch_decoding.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch.c @@ -1215,7 +1213,6 @@ set (MAC_SRC set (MAC_NR_SRC ${NR_PHY_INTERFACE_DIR}/NR_IF_Module.c - ${NR_PHY_INTERFACE_DIR}/nr_sched_response.c ${NR_GNB_MAC_DIR}/main.c ${NR_GNB_MAC_DIR}/config.c ${NR_GNB_MAC_DIR}/gNB_scheduler.c diff --git a/ci-scripts/datalog_rt_stats.100.2x2.yaml b/ci-scripts/datalog_rt_stats.100.2x2.yaml index 7c5cb48add..360ba57402 100644 --- a/ci-scripts/datalog_rt_stats.100.2x2.yaml +++ b/ci-scripts/datalog_rt_stats.100.2x2.yaml @@ -15,7 +15,6 @@ Ref : DLSCH encoding : 200.0 L1 Rx processing : 530.0 PUSCH inner-receiver : 360.0 - Schedule Response : 1.0 DL & UL scheduling timing : 17.0 UL Indication : 1.0 Slot Indication : 13.0 @@ -31,7 +30,6 @@ DeviationThreshold : DLSCH encoding : 0.25 L1 Rx processing : 0.25 PUSCH inner-receiver : 0.25 - Schedule Response : 1.00 DL & UL scheduling timing : 0.25 UL Indication : 1.00 Slot Indication : 0.50 diff --git a/ci-scripts/datalog_rt_stats.1x1.60.yaml b/ci-scripts/datalog_rt_stats.1x1.60.yaml index 5e0564977b..b418b74940 100644 --- a/ci-scripts/datalog_rt_stats.1x1.60.yaml +++ b/ci-scripts/datalog_rt_stats.1x1.60.yaml @@ -15,7 +15,6 @@ Ref : DLSCH encoding : 110.0 L1 Rx processing : 345.0 PUSCH inner-receiver : 150.0 - Schedule Response : 1.0 DL & UL scheduling timing : 5.0 UL Indication : 1.0 Slot Indication : 6.0 @@ -31,7 +30,6 @@ DeviationThreshold : DLSCH encoding : 0.25 L1 Rx processing : 0.25 PUSCH inner-receiver : 0.25 - Schedule Response : 1.00 DL & UL scheduling timing : 0.50 UL Indication : 1.00 Slot Indication : 0.50 diff --git a/ci-scripts/datalog_rt_stats.2x2.yaml b/ci-scripts/datalog_rt_stats.2x2.yaml index 7d489c171e..25009bad10 100644 --- a/ci-scripts/datalog_rt_stats.2x2.yaml +++ b/ci-scripts/datalog_rt_stats.2x2.yaml @@ -15,7 +15,6 @@ Ref : DLSCH encoding : 230.0 L1 Rx processing : 175.0 PUSCH inner-receiver : 100.0 - Schedule Response : 3.0 DL & UL scheduling timing : 11.0 UL Indication : 3.0 Slot Indication : 14.0 @@ -28,7 +27,6 @@ DeviationThreshold : DLSCH encoding : 0.25 L1 Rx processing : 0.25 PUSCH inner-receiver : 0.25 - Schedule Response : 1.00 DL & UL scheduling timing : 0.50 UL Indication : 1.00 Slot Indication : 0.25 diff --git a/ci-scripts/datalog_rt_stats.60.2x2.yaml b/ci-scripts/datalog_rt_stats.60.2x2.yaml index 7352136764..6df6fded54 100644 --- a/ci-scripts/datalog_rt_stats.60.2x2.yaml +++ b/ci-scripts/datalog_rt_stats.60.2x2.yaml @@ -15,7 +15,6 @@ Ref : DLSCH encoding : 140.0 L1 Rx processing : 345.0 PUSCH inner-receiver : 155.0 - Schedule Response : 1.0 DL & UL scheduling timing : 8.0 UL Indication : 3.0 Slot Indication : 9.0 @@ -31,7 +30,6 @@ DeviationThreshold : DLSCH encoding : 0.25 L1 Rx processing : 0.25 PUSCH inner-receiver : 0.25 - Schedule Response : 1.00 DL & UL scheduling timing : 0.35 UL Indication : 1.00 Slot Indication : 0.35 diff --git a/ci-scripts/datalog_rt_stats.default.yaml b/ci-scripts/datalog_rt_stats.default.yaml index be3f8b277e..65fd678b0f 100644 --- a/ci-scripts/datalog_rt_stats.default.yaml +++ b/ci-scripts/datalog_rt_stats.default.yaml @@ -15,7 +15,6 @@ Ref : DLSCH encoding : 90.0 L1 Rx processing : 290.0 PUSCH inner-receiver : 115.0 - Schedule Response : 1.0 DL & UL scheduling timing : 4.0 UL Indication : 2.0 Slot Indication : 5.0 @@ -31,7 +30,6 @@ DeviationThreshold : DLSCH encoding : 0.25 L1 Rx processing : 0.25 PUSCH inner-receiver : 0.25 - Schedule Response : 1.00 DL & UL scheduling timing : 0.50 UL Indication : 1.00 Slot Indication : 0.50 diff --git a/doc/Doxyfile b/doc/Doxyfile index 5f96bc2e4a..85cb5776c0 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -1916,8 +1916,6 @@ INPUT = \ @CMAKE_CURRENT_SOURCE_DIR@/../openair2/E1AP/e1ap_api.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair2/E1AP/e1ap_setup.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair2/E1AP/e1ap_asnc.h \ -@CMAKE_CURRENT_SOURCE_DIR@/../openair2/NR_PHY_INTERFACE/nr_sched_response.h \ -@CMAKE_CURRENT_SOURCE_DIR@/../openair2/NR_PHY_INTERFACE/nr_sched_response.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair2/NR_PHY_INTERFACE/NR_IF_Module.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair2/NR_PHY_INTERFACE/NR_IF_Module.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair2/SDAP/nr_sdap/nr_sdap.h \ @@ -2012,7 +2010,6 @@ INPUT = \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_TRANSPORT/nr_transport_common_proto.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_TRANSPORT/nr_sss.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_TRANSPORT/nr_uci_tools_common.c \ -@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_TRANSPORT/nr_prach.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_TRANSPORT/nr_scrambling.c \ @@ -2294,10 +2291,8 @@ INPUT = \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/SCHED_NR/phy_frame_config_nr.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/SCHED_NR/phy_frame_config_nr.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/SCHED_NR/nr_prach_procedures.c \ -@CMAKE_CURRENT_SOURCE_DIR@/../openair1/SCHED_NR/fapi_nr_l1.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/SCHED_NR/phy_procedures_nr_gNB.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/SCHED_NR/sched_nr.h \ -@CMAKE_CURRENT_SOURCE_DIR@/../openair1/SCHED_NR/fapi_nr_l1.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/SCHED_NR/nr_ru_procedures.c \ @CMAKE_CURRENT_SOURCE_DIR@/../radio/ETHERNET/if_defs.h \ @CMAKE_CURRENT_SOURCE_DIR@/../radio/ETHERNET/ethernet_lib.h \ diff --git a/executables/lte-softmodem.c b/executables/lte-softmodem.c index 0932702910..6ff535fb5b 100644 --- a/executables/lte-softmodem.c +++ b/executables/lte-softmodem.c @@ -194,10 +194,6 @@ void handle_nr_srs_measurements(const module_id_t module_id, void set_default_frame_parms(LTE_DL_FRAME_PARMS *frame_parms[MAX_NUM_CCs]); /* TODO these declarations are to be removed */ -void nr_schedule_dl_tti_req(void) {}; -void nr_schedule_ul_dci_req() {}; -void nr_schedule_tx_req() {}; -void nr_schedule_ul_tti_req() {}; void nr_slot_select() {}; /*------------------------------------------------------------------------*/ diff --git a/executables/lte-uesoftmodem.c b/executables/lte-uesoftmodem.c index b212ceb3f1..f36bdde2b5 100644 --- a/executables/lte-uesoftmodem.c +++ b/executables/lte-uesoftmodem.c @@ -156,10 +156,6 @@ extern char uecap_xer[1024]; char uecap_xer_in=0; /* TODO these declarations are to be removed */ -void nr_schedule_dl_tti_req(void) {}; -void nr_schedule_ul_dci_req() {}; -void nr_schedule_tx_req() {}; -void nr_schedule_ul_tti_req() {}; void nr_slot_select() {}; /* see file openair2/LAYER2/MAC/main.c for why abstraction_flag is needed diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 1485b6de1d..94fc22cf46 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -55,7 +55,6 @@ #include "PHY/defs_gNB.h" #include "PHY/defs_nr_common.h" #include "PHY/impl_defs_nr.h" -#include "SCHED_NR/fapi_nr_l1.h" #include "SCHED_NR/phy_frame_config_nr.h" #include "SCHED_NR/sched_nr.h" #include "assertions.h" @@ -65,7 +64,6 @@ #include "nfapi/oai_integration/vendor_ext.h" #include "nfapi_nr_interface_scf.h" #include "notified_fifo.h" -#include "openair2/NR_PHY_INTERFACE/nr_sched_response.h" #include "thread-pool.h" #include "time_meas.h" #include "utils.h" @@ -251,7 +249,6 @@ static size_t dump_L1_meas_stats(PHY_VARS_gNB *gNB, RU_t *ru, char *output, size output += print_meas_log(&gNB->ul_indication_stats, "UL Indication", NULL, NULL, output, end - output); output += print_meas_log(&gNB->slot_indication_stats, "Slot Indication", NULL, NULL, output, end - output); output += print_meas_log(&gNB->rx_pusch_stats, "PUSCH inner-receiver", NULL, NULL, output, end - output); - output += print_meas_log(&gNB->schedule_response_stats, "Schedule Response", NULL, NULL, output, end - output); output += print_meas_log(&gNB->rx_prach, "PRACH RX", NULL, NULL, output, end - output); if (ru->feprx) output += print_meas_log(&ru->ofdm_demod_stats, "feprx", NULL, NULL, output, end - output); @@ -313,7 +310,6 @@ void *nrL1_stats_thread(void *param) { reset_meas(&gNB->ul_indication_stats); reset_meas(&gNB->slot_indication_stats); reset_meas(&gNB->rx_pusch_stats); - reset_meas(&gNB->schedule_response_stats); reset_meas(&gNB->dlsch_scrambling_stats); reset_meas(&gNB->dlsch_modulation_stats); reset_meas(&gNB->dlsch_resource_mapping_stats); @@ -361,12 +357,6 @@ void init_gNB_Tpool(int inst) // create the TX thread responsible for TX processing start event (L1_tx_out msg queue), then launch tx_func() threadCreate(&gNB->L1_tx_thread, L1_tx_thread, (void *)gNB, "L1_tx_thread", gNB->L1_tx_thread_core, OAI_PRIORITY_RT_MAX); - notifiedFIFO_elt_t *msgL1Tx = newNotifiedFIFO_elt(sizeof(processingData_L1tx_t), 0, &gNB->L1_tx_out, NULL); - processingData_L1tx_t *msgDataTx = (processingData_L1tx_t *)NotifiedFifoData(msgL1Tx); - memset(msgDataTx, 0, sizeof(processingData_L1tx_t)); - // this will be removed when the msgDataTx is not necessary anymore - gNB->msgDataTx = msgDataTx; - if (!IS_SOFTMODEM_NOSTATS) threadCreate(&proc->L1_stats_thread, nrL1_stats_thread, (void *)gNB, "L1_stats", -1, OAI_PRIORITY_RT_LOW); } @@ -443,7 +433,6 @@ void init_gNB() AssertFatal((gNB->if_inst = NR_IF_Module_init(inst)) != NULL, "Cannot register interface"); LOG_I(NR_PHY, "Registered with MAC interface module (%p)\n", gNB->if_inst); - gNB->if_inst->NR_Schedule_response = nr_schedule_response; gNB->if_inst->NR_PHY_config_req = nr_phy_config_request; gNB->prach_energy_counter = 0; diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 0bd583662b..d7ca4a5e69 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -423,8 +423,6 @@ int start_L1L2(module_id_t gnb_id) /* block threads */ oai_exit = 0; sync_var = -1; - extern void init_sched_response(void); - init_sched_response(); /* update config */ gNB_MAC_INST *mac = RC.nrmac[0]; diff --git a/nfapi/oai_integration/nfapi_pnf.c b/nfapi/oai_integration/nfapi_pnf.c index 79c0d689c7..bb3ac8555b 100644 --- a/nfapi/oai_integration/nfapi_pnf.c +++ b/nfapi/oai_integration/nfapi_pnf.c @@ -55,7 +55,6 @@ #include "PHY/INIT/phy_init.h" #include "PHY/INIT/nr_phy_init.h" #include "PHY/LTE_TRANSPORT/transport_proto.h" -#include "openair1/SCHED_NR/fapi_nr_l1.h" #include "openair1/PHY/NR_TRANSPORT/nr_dlsch.h" #include "openair1/PHY/defs_gNB.h" #include @@ -1108,17 +1107,6 @@ void pnf_nr_phy_deallocate_p7_vendor_ext(void *header) free(header); } -extern void nr_schedule_ul_dci_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_dci_request_t *UL_dci_req); -int pnf_phy_ul_dci_req(gNB_L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, nfapi_nr_ul_dci_request_t *req) -{ - DevAssert(RC.gNB != NULL && RC.gNB[0] != NULL); - PHY_VARS_gNB *gNB = RC.gNB[0]; // phy_inst? - - nr_schedule_ul_dci_req(gNB, req); - - return 0; -} - int pnf_phy_hi_dci0_req(L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, nfapi_hi_dci0_request_t *req) { if (req->hi_dci0_request_body.number_of_dci == 0 && req->hi_dci0_request_body.number_of_hi == 0) @@ -1149,21 +1137,6 @@ int pnf_phy_hi_dci0_req(L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, nfa } -// from dl_tti_req_fn fptr -extern void nr_schedule_dl_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_dl_tti_request_t *DL_req); -int pnf_phy_dl_tti_req(gNB_L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, nfapi_nr_dl_tti_request_t *DL_req) -{ - DevAssert(proc == NULL); - DevAssert(sync_var == 0); - DevAssert(RC.gNB != NULL && RC.gNB[0] != NULL); - PHY_VARS_gNB *gNB = RC.gNB[0]; // phy_inst? - - nr_schedule_dl_tti_req(gNB, DL_req); - - return 0; -} - - int pnf_phy_dl_config_req(L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, nfapi_dl_config_request_t *req) { if (RC.eNB == 0) { @@ -1256,18 +1229,6 @@ int pnf_phy_dl_config_req(L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, n } -extern void nr_schedule_tx_req(PHY_VARS_gNB *gNB, nfapi_nr_tx_data_request_t *TX_req); -int pnf_phy_tx_data_req(nfapi_pnf_p7_config_t *pnf_p7, nfapi_nr_tx_data_request_t *req) -{ - DevAssert(RC.gNB != NULL && RC.gNB[0] != NULL); - PHY_VARS_gNB *gNB = RC.gNB[0]; // phy_inst? - - nr_schedule_tx_req(gNB, req); - - return 0; -} - - int pnf_phy_tx_req(nfapi_pnf_p7_config_t *pnf_p7, nfapi_tx_request_t *req) { uint16_t sfn = NFAPI_SFNSF2SFN(req->sfn_sf); uint16_t sf = NFAPI_SFNSF2SF(req->sfn_sf); @@ -1293,16 +1254,6 @@ int pnf_phy_tx_req(nfapi_pnf_p7_config_t *pnf_p7, nfapi_tx_request_t *req) { return 0; } -extern void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req); -int pnf_phy_ul_tti_req(gNB_L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, nfapi_nr_ul_tti_request_t *req) -{ - DevAssert(RC.gNB != NULL && RC.gNB[0] != NULL); - PHY_VARS_gNB *gNB = RC.gNB[0]; // phy_inst? - - nr_schedule_ul_tti_req(gNB, req); - return 0; -} - int pnf_phy_ul_config_req(L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, nfapi_ul_config_request_t *req) { if (0)LOG_D(PHY,"[PNF] UL_CONFIG_REQ %s() sfn_sf:%d pdu:%d rach_prach_frequency_resources:%d srs_present:%u\n", __FUNCTION__, @@ -1711,10 +1662,10 @@ int nr_start_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nf } // NR - p7_config->dl_tti_req_fn = &pnf_phy_dl_tti_req; - p7_config->ul_tti_req_fn = &pnf_phy_ul_tti_req; - p7_config->ul_dci_req_fn = &pnf_phy_ul_dci_req; - p7_config->tx_data_req_fn = &pnf_phy_tx_data_req; + p7_config->dl_tti_req_fn = NULL; + p7_config->ul_tti_req_fn = NULL; + p7_config->ul_dci_req_fn = NULL; + p7_config->tx_data_req_fn = NULL; // LTE p7_config->dl_config_req = &pnf_phy_dl_config_req; diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c index 3fece46ac8..374ecb4b19 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c @@ -36,7 +36,6 @@ #include "PHY/NR_TRANSPORT/nr_ulsch.h" #include "PHY/NR_TRANSPORT/nr_dlsch.h" #include "SCHED_NR/sched_nr.h" -#include "SCHED_NR/fapi_nr_l1.h" #include "defs.h" #include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/LOG/log.h" diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c index b23db3781d..1d9aa53d25 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c @@ -38,7 +38,6 @@ #include "PHY/NR_TRANSPORT/nr_transport_proto.h" #include "PHY/NR_TRANSPORT/nr_ulsch.h" #include "PHY/defs_gNB.h" -#include "SCHED_NR/fapi_nr_l1.h" #include "SCHED_NR/sched_nr.h" #include "common/utils/LOG/log.h" #include "common/utils/LOG/vcd_signal_dumper.h" diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index f744b441ab..5da6559937 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -39,7 +39,6 @@ #include #include "PHY/NR_TRANSPORT/nr_ulsch.h" #include "PHY/NR_REFSIG/nr_refsig.h" -#include "SCHED_NR/fapi_nr_l1.h" #include "PHY/NR_REFSIG/ul_ref_seq_nr.h" #include #include "nfapi/open-nFAPI/fapi/inc/nr_fapi_p5_utils.h" @@ -61,7 +60,6 @@ int l1_north_init_gNB() LOG_D(NR_PHY, "RC.gNB[%d]: installing callbacks\n", i); RC.gNB[i]->if_inst->NR_PHY_config_req = nr_phy_config_request; - RC.gNB[i]->if_inst->NR_Schedule_response = nr_schedule_response; } return 0; @@ -292,7 +290,6 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) void install_nr_schedule_handlers(NR_IF_Module_t *if_inst) { if_inst->NR_PHY_config_req = nr_phy_config_request; - if_inst->NR_Schedule_response = nr_schedule_response; } void nr_phy_config_request_sim(PHY_VARS_gNB *gNB, diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.h b/openair1/PHY/NR_TRANSPORT/nr_dlsch.h index 443035274b..0a82b2c47f 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.h +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.h @@ -35,9 +35,6 @@ #include "PHY/defs_gNB.h" -void nr_fill_dlsch_dl_tti_req(processingData_L1tx_t *msgTx, nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu); -void nr_fill_dlsch_tx_req(processingData_L1tx_t *msgTx, int idx, uint8_t *sdu); - void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_array, int frame, int slot); int nr_dlsch_encoding(PHY_VARS_gNB *gNB, diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c deleted file mode 100644 index 17766da629..0000000000 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -/*! \file PHY/NR_TRANSPORT/nr_dlsch_tools.c -* \brief Top-level routines for decoding DLSCH transport channel from 38-214, V15.2.0 2018-06 -* \author Guy De Souza -* \date 2018 -* \version 0.1 -* \company Eurecom -* \email: desouza@eurecom.fr -* \note - -* \warning -*/ - -#include "nr_dlsch.h" -#include "../../../nfapi/oai_integration/vendor_ext.h" - -void nr_fill_dlsch_dl_tti_req(processingData_L1tx_t *msgTx, nfapi_nr_dl_tti_pdsch_pdu *pdsch_pdu) -{ - NR_gNB_DLSCH_t *dlsch = &msgTx->gNB->dlsch[msgTx->num_pdsch_slot]; - /// DLSCH struct - memcpy((void*)&dlsch->pdsch_pdu, (void*)pdsch_pdu, sizeof(nfapi_nr_dl_tti_pdsch_pdu)); - AssertFatal(msgTx->num_pdsch_slot == pdsch_pdu->pdsch_pdu_rel15.pduIndex, - "PDSCH PDU index %d does not match msgTx index %d\n", - pdsch_pdu->pdsch_pdu_rel15.pduIndex, - msgTx->num_pdsch_slot); - msgTx->num_pdsch_slot++; - dlsch->pdu = NULL; -} - -void nr_fill_dlsch_tx_req(processingData_L1tx_t *msgTx, int idx, uint8_t *sdu) -{ - AssertFatal(sdu != NULL, "sdu is null\n"); - - /* not sure if FAPI could transmit DL_TTI_req and TX_req in different orders. - * for the moment, assume they are in the same order (and check!) */ - NR_gNB_DLSCH_t *dlsch = &msgTx->gNB->dlsch[idx]; - const nfapi_nr_dl_tti_pdsch_pdu *pdsch = dlsch->pdsch_pdu; - AssertFatal(pdsch->pdsch_pdu_rel15.pduIndex == idx, "PDSCH PDU index %d does not match %d\n", pdsch->pdsch_pdu_rel15.pduIndex, idx); - dlsch->pdu = sdu; -} diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index 239b6f2f29..24d4950dfa 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -35,7 +35,6 @@ #include "PHY/NR_TRANSPORT/nr_ulsch.h" #include "PHY/NR_TRANSPORT/nr_dlsch.h" #include "SCHED_NR/sched_nr.h" -#include "SCHED_NR/fapi_nr_l1.h" #include "defs.h" #include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/LOG/log.h" diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index fdaae94b49..abc700f9d2 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -473,7 +473,6 @@ typedef struct PHY_VARS_gNB_s { time_stats_t rx_pusch_symbol_processing_stats; time_stats_t ul_indication_stats; time_stats_t slot_indication_stats; - time_stats_t schedule_response_stats; time_stats_t ulsch_decoding_stats; time_stats_t ts_deinterleave; time_stats_t ts_rate_unmatch; @@ -501,7 +500,6 @@ typedef struct PHY_VARS_gNB_s { int L1_rx_thread_core; pthread_t L1_tx_thread; int L1_tx_thread_core; - struct processingData_L1tx *msgDataTx; void *scopeData; } PHY_VARS_gNB; @@ -578,18 +576,6 @@ typedef struct processingData_L1tx { int slot_rx; openair0_timestamp timestamp_tx; PHY_VARS_gNB *gNB; - nfapi_nr_dl_tti_pdcch_pdu pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; - /// corresponds to UL_dci_req->ul_dci_pdu_list - nfapi_nr_dl_tti_pdcch_pdu ul_pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; - nfapi_nr_dl_tti_csi_rs_pdu csirs_pdu[NFAPI_NR_MAX_NB_CORESETS]; - nfapi_nr_dl_tti_ssb_pdu ssb_pdu[64]; - int n_ssb_pdu; - int n_csirs_pdu; - uint16_t num_pdsch_slot; - int num_dl_pdcch; - int num_ul_pdcch; - /* a reference to the sched_response, to release it when not needed anymore */ - int sched_response_id; } processingData_L1tx_t; typedef struct processingData_L1rx { diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c deleted file mode 100644 index 0d122cedf5..0000000000 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -/*! \file fapi_nr_l1.c - * \brief functions for FAPI L1 interface - * \author R. Knopp, WEI-TAI CHEN - * \date 2017, 2018 - * \version 0.1 - * \company Eurecom, NTUST - * \email: knopp@eurecom.fr, kroempa@gmail.com - * \note - * \warning - */ -#include "fapi_nr_l1.h" -#include "common/ran_context.h" -#include "PHY/NR_TRANSPORT/nr_transport_proto.h" -#include "PHY/NR_TRANSPORT/nr_dlsch.h" -#include "PHY/NR_TRANSPORT/nr_dci.h" -#include "nfapi/oai_integration/vendor_ext.h" -#include "openair2/NR_PHY_INTERFACE/nr_sched_response.h" -#include "nfapi/oai_integration/nfapi_vnf.h" - -static void handle_nr_nfapi_ssb_pdu(processingData_L1tx_t *msgTx, int frame, int slot, nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu) -{ - - AssertFatal(dl_tti_pdu->ssb_pdu.ssb_pdu_rel15.bchPayloadFlag== 1, "bchPayloadFlat %d != 1\n", - dl_tti_pdu->ssb_pdu.ssb_pdu_rel15.bchPayloadFlag); - - uint8_t i_ssb = dl_tti_pdu->ssb_pdu.ssb_pdu_rel15.SsbBlockIndex; - - LOG_D(NR_PHY,"%d.%d : ssb index %d pbch_pdu: %x\n",frame,slot,i_ssb,dl_tti_pdu->ssb_pdu.ssb_pdu_rel15.bchPayload); - msgTx->ssb_pdu[msgTx->n_ssb_pdu++] = dl_tti_pdu->ssb_pdu; -} - -static void handle_nfapi_nr_csirs_pdu(processingData_L1tx_t *msgTx, int frame, int slot, nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu) -{ - msgTx->csirs_pdu[msgTx->n_csirs_pdu++] = *csirs_pdu; -} - -void nr_schedule_dl_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_dl_tti_request_t *DL_req) -{ - DevAssert(gNB != NULL); - DevAssert(DL_req != NULL); - nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; - - int frame = DL_req->SFN; - int slot = DL_req->Slot; - - int slot_type = nr_slot_select(cfg, frame, slot); - DevAssert(slot_type == NR_DOWNLINK_SLOT || slot_type == NR_MIXED_SLOT); - - processingData_L1tx_t *msgTx = gNB->msgDataTx; - msgTx->slot = slot; - msgTx->frame = frame; - - uint8_t number_dl_pdu = DL_req->dl_tti_request_body.nPDUs; - - for (int i = 0; i < number_dl_pdu; i++) { - nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu = &DL_req->dl_tti_request_body.dl_tti_pdu_list[i]; - LOG_D(NR_PHY, "NFAPI: dl_pdu %d : type %d\n", i, dl_tti_pdu->PDUType); - switch (dl_tti_pdu->PDUType) { - case NFAPI_NR_DL_TTI_SSB_PDU_TYPE: - handle_nr_nfapi_ssb_pdu(msgTx, frame, slot, dl_tti_pdu); - break; - - case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE: - LOG_D(NR_PHY, "frame %d, slot %d, Got NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE for %d.%d\n", frame, slot, DL_req->SFN, DL_req->Slot); - msgTx->pdcch_pdu[msgTx->num_dl_pdcch] = dl_tti_pdu->pdcch_pdu; - msgTx->num_dl_pdcch++; - break; - - case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE: - LOG_D(NR_PHY, "frame %d, slot %d, Got NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE for %d.%d\n", frame, slot, DL_req->SFN, DL_req->Slot); - handle_nfapi_nr_csirs_pdu(msgTx, frame, slot, &dl_tti_pdu->csi_rs_pdu); - break; - - case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: - LOG_D(NR_PHY, "frame %d, slot %d, Got NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE for %d.%d\n", frame, slot, DL_req->SFN, DL_req->Slot); - nr_fill_dlsch_dl_tti_req(msgTx, &dl_tti_pdu->pdsch_pdu); - } - } -} - -void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req) -{ - DevAssert(gNB != NULL); - DevAssert(UL_tti_req != NULL); - - int frame = UL_tti_req->SFN; - int slot = UL_tti_req->Slot; - - for (int i = 0; i < UL_tti_req->n_pdus; i++) { - int type = UL_tti_req->pdus_list[i].pdu_type; - LOG_D(NR_PHY, - "frame %d, slot %d got %s for %d.%d\n", - frame, - slot, - txt_nfapi_nr_ul_config_pdu_type[type], - UL_tti_req->SFN, - UL_tti_req->Slot); - switch (type) { - case NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE: - nr_fill_ulsch(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].pusch_pdu); - break; - case NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE: - nr_fill_pucch(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].pucch_pdu); - 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"); - } 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); - break; - } - } -} - -void nr_schedule_tx_req(PHY_VARS_gNB *gNB, nfapi_nr_tx_data_request_t *TX_req) -{ - DevAssert(gNB != NULL); - DevAssert(TX_req != NULL); - processingData_L1tx_t *msgTx = gNB->msgDataTx; - - for (int idx = 0; idx < TX_req->Number_of_PDUs; ++idx) { - uint8_t *sdu = (uint8_t *)TX_req->pdu_list[idx].TLVs[0].value.direct; - nr_fill_dlsch_tx_req(msgTx, idx, sdu); - } -} - -void nr_schedule_ul_dci_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_dci_request_t *UL_dci_req) -{ - DevAssert(gNB != NULL); - DevAssert(UL_dci_req != NULL); - processingData_L1tx_t *msgTx = gNB->msgDataTx; - - msgTx->num_ul_pdcch = UL_dci_req->numPdus; - for (int i = 0; i < UL_dci_req->numPdus; i++) - msgTx->ul_pdcch_pdu[i] = UL_dci_req->ul_dci_pdu_list[i].pdcch_pdu; -} - -void nr_schedule_response(NR_Sched_Rsp_t *Sched_INFO) -{ - // copy data from L2 interface into L1 structures - module_id_t Mod_id = Sched_INFO->module_id; - frame_t frame = Sched_INFO->frame; - slot_t slot = Sched_INFO->slot; - - AssertFatal(RC.gNB != NULL, "RC.gNB is null\n"); - AssertFatal(RC.gNB[Mod_id] != NULL, "RC.gNB[%d] is null\n", Mod_id); - - PHY_VARS_gNB *gNB = RC.gNB[Mod_id]; - start_meas(&gNB->schedule_response_stats); - - nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; - - int slot_type = nr_slot_select(cfg, frame, slot); - - clear_slot_beamid(gNB, slot); // reset beam_id information for the slot to be processed - DevAssert(NFAPI_MODE == NFAPI_MONOLITHIC); - bool is_dl = slot_type == NR_DOWNLINK_SLOT || slot_type == NR_MIXED_SLOT; - - processingData_L1tx_t *msgTx = gNB->msgDataTx; - /* store the sched_response_id for the TX thread to release it when done */ - msgTx->sched_response_id = Sched_INFO->sched_response_id; - - DevAssert(Sched_INFO->DL_req.SFN == frame); - DevAssert(Sched_INFO->DL_req.Slot == slot); - - if (is_dl) { - nr_schedule_dl_tti_req(gNB, &Sched_INFO->DL_req); - } - - nr_schedule_ul_tti_req(gNB, &Sched_INFO->UL_tti_req); - - if (is_dl) { - nr_schedule_tx_req(gNB, &Sched_INFO->TX_req); - - nr_schedule_ul_dci_req(gNB, &Sched_INFO->UL_dci_req); - /* Both the current thread and the TX thread will access the sched_info - * at the same time, so increase its reference counter, so that it is - * released only when both threads are done with it. - */ - inc_ref_sched_response(Sched_INFO->sched_response_id); - } - - /* this thread is done with the sched_info, decrease the reference counter */ - if ((slot_type == NR_DOWNLINK_SLOT || slot_type == NR_MIXED_SLOT) && NFAPI_MODE == NFAPI_MONOLITHIC) { - LOG_D(NR_PHY, "Calling dref_sched_response for id %d in %d.%d (sched_response)\n", Sched_INFO->sched_response_id, frame, slot); - deref_sched_response(Sched_INFO->sched_response_id); - } - stop_meas(&gNB->schedule_response_stats); -} diff --git a/openair1/SCHED_NR/fapi_nr_l1.h b/openair1/SCHED_NR/fapi_nr_l1.h deleted file mode 100644 index b8f1b6e238..0000000000 --- a/openair1/SCHED_NR/fapi_nr_l1.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -/*! \file fapi_nr_l1.h - * \brief function prototypes for FAPI L1 interface - * \author R. Knopp, WEI-TAI CHEN - * \date 2017, 2018 - * \version 0.1 - * \company Eurecom, NTUST - * \email: knopp@eurecom.fr, kroempa@gmail.com - * \note - * \warning - */ - -#include "PHY/defs_gNB.h" -#include "SCHED_NR/sched_nr.h" -#include "nfapi_nr_interface_scf.h" - -void nr_schedule_response(NR_Sched_Rsp_t *Sched_INFO); diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index e6cd15b4a9..d5d6a8860e 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -33,7 +33,6 @@ #include "PHY/defs_gNB.h" #include "PHY/NR_TRANSPORT/nr_transport_proto.h" #include "nfapi_nr_interface_scf.h" -#include "fapi_nr_l1.h" #include "nfapi_pnf.h" #include "common/utils/LOG/log.h" #include "common/utils/LOG/vcd_signal_dumper.h" diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index eb4f1c8e84..8fab2bde81 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -27,7 +27,6 @@ #include "PHY/NR_TRANSPORT/nr_dci.h" #include "PHY/NR_ESTIMATION/nr_ul_estimation.h" #include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_interface.h" -#include "fapi_nr_l1.h" #include "common/utils/LOG/log.h" #include "common/utils/LOG/vcd_signal_dumper.h" #include "PHY/INIT/nr_phy_init.h" diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c index 9c53b50b76..18944a8099 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c @@ -41,7 +41,6 @@ #include "nfapi/oai_integration/gnb_ind_vars.h" #include "nfapi/oai_integration/vendor_ext.h" #include "nfapi_interface.h" -#include "openair2/NR_PHY_INTERFACE/nr_sched_response.h" #include "openair2/PHY_INTERFACE/queue_t.h" #include "utils.h" #include "nfapi/oai_integration/nfapi_pnf.h" @@ -486,8 +485,6 @@ NR_IF_Module_t *NR_IF_Module_init(int Mod_id) { "allocation of nr_if_inst[%d]->if_mutex fails\n",Mod_id); } - init_sched_response(); - return nr_if_inst[Mod_id]; } diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.h b/openair2/NR_PHY_INTERFACE/NR_IF_Module.h index ca7e705f4a..49219a0af1 100644 --- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.h +++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.h @@ -122,7 +122,6 @@ typedef struct { typedef struct NR_IF_Module_s { //define the function pointer void (*NR_UL_indication)(NR_UL_IND_t *UL_INFO); - void (*NR_Schedule_response)(NR_Sched_Rsp_t *Sched_INFO); void (*NR_slot_indication)(const nfapi_nr_slot_indication_scf_t *ind, NR_Sched_Rsp_t *rsp); void (*NR_PHY_config_req)(NR_PHY_Config_t *config_INFO); uint32_t CC_mask; @@ -137,7 +136,4 @@ NR_IF_Module_t *NR_IF_Module_init(int Mod_id); void NR_IF_Module_kill(int Mod_id); -/*Interface for Downlink, transmitting the DLSCH SDU, DCI SDU*/ -void NR_Schedule_Response(NR_Sched_Rsp_t *Sched_INFO); - #endif /*_NFAPI_INTERFACE_NR_H_*/ diff --git a/openair2/NR_PHY_INTERFACE/nr_sched_response.c b/openair2/NR_PHY_INTERFACE/nr_sched_response.c deleted file mode 100644 index 9c66b21536..0000000000 --- a/openair2/NR_PHY_INTERFACE/nr_sched_response.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -/* sched response memory management */ - -/* A system with reference counting is implemented. - * It is needed because several threads will access the structure, - * which has to be released only when all the threads are done with it. - * So the main thread allocates a sched_response with allocate_sched_response() - * which allocates a sched_response with a reference counter == 1 - * and then for each other thread that will access sched_response, a - * call to inc_ref_sched_response() is done. When a thread has finished - * using the sched_response, it calls deref_sched_response() which will - * in turn call release_sched_response() when the reference counter becomes 0. - * - * The several threads in question are accessing the _same_ sched_response, - * it has not to be confused with the various TX processes that may run in - * parallel and which are accessing _different_ sched_response (the maximum - * number of parallel processes is N_RESP). - */ - -#include "nr_sched_response.h" -#include -#include -#include -#include "NR_PHY_INTERFACE/NR_IF_Module.h" -#include "common/utils/LOG/log.h" -#include "common/utils/assertions.h" - -#define N_RESP 3 -static NR_Sched_Rsp_t resp[N_RESP]; -static int resp_refcount[N_RESP]; -static int resp_freelist_next[N_RESP]; -static int resp_freelist_head; -static pthread_mutex_t resp_mutex = PTHREAD_MUTEX_INITIALIZER; -static bool resp_freelist_inited = false; - -void init_sched_response(void) -{ - /* init only once */ - if (pthread_mutex_lock(&resp_mutex)) - AssertFatal(0, "pthread_mutex_lock failed\n"); - if (resp_freelist_inited) { - if (pthread_mutex_unlock(&resp_mutex)) - AssertFatal(0, "pthread_mutex_unlock failed\n"); - return; - } - resp_freelist_inited = true; - if (pthread_mutex_unlock(&resp_mutex)) - AssertFatal(0, "pthread_mutex_unlock failed\n"); - - int i; - for (i = 0; i < N_RESP - 1; i++) - resp_freelist_next[i] = i + 1; - resp_freelist_next[N_RESP - 1] = -1; - resp_freelist_head = 0; -} - -NR_Sched_Rsp_t *allocate_sched_response(void) -{ - NR_Sched_Rsp_t *ret; - int new_head; - - if (pthread_mutex_lock(&resp_mutex)) - AssertFatal(0, "pthread_mutex_lock failed\n"); - - AssertFatal(resp_freelist_inited, "sched_response used before init\n"); - - if (resp_freelist_head == -1) { - LOG_E(PHY, "fatal: sched_response cannot be allocated, increase N_RESP\n"); - exit(1); - } - - ret = &resp[resp_freelist_head]; - ret->sched_response_id = resp_freelist_head; - resp_refcount[resp_freelist_head] = 1; - - new_head = resp_freelist_next[resp_freelist_head]; - resp_freelist_next[resp_freelist_head] = -1; - resp_freelist_head = new_head; - - if (pthread_mutex_unlock(&resp_mutex)) - AssertFatal(0, "pthread_mutex_unlock failed\n"); - - return ret; -} - -static void release_sched_response(int sched_response_id) -{ - LOG_D(NR_MAC,"Releasing sched_response %d\n",sched_response_id); - resp_freelist_next[sched_response_id] = resp_freelist_head; - resp_freelist_head = sched_response_id; -} - -void deref_sched_response(int sched_response_id) -{ - /* simulators (ulsim/dlsim) deal with their own sched_response but call - * functions that call this one, let's handle this case with a special - * value -1 where we do nothing (yes it's a hack) - */ - if (sched_response_id == -1) - return; - - if (pthread_mutex_lock(&resp_mutex)) - AssertFatal(0, "pthread_mutex_lock failed\n"); - - AssertFatal(resp_freelist_inited, "sched_response used before init\n"); - AssertFatal(resp_refcount[sched_response_id] > 0, "sched_reponse decreased too much\n"); - - LOG_D(NR_MAC,"resp_refcount[%d] %d\n",sched_response_id,resp_refcount[sched_response_id]); - resp_refcount[sched_response_id]--; - if (resp_refcount[sched_response_id] == 0) - release_sched_response(sched_response_id); - - if (pthread_mutex_unlock(&resp_mutex)) - AssertFatal(0, "pthread_mutex_unlock failed\n"); -} - -void inc_ref_sched_response(int sched_response_id) -{ - /* simulators (ulsim/dlsim) deal with their own sched_response but call - * functions that call this one, let's handle this case with a special - * value -1 where we do nothing (yes it's a hack) - */ - if (sched_response_id == -1) - return; - - LOG_D(NR_MAC,"Incrementing sched_resp resp_refounct[%d] = %d\n", - sched_response_id,resp_refcount[sched_response_id]); - if (pthread_mutex_lock(&resp_mutex)) - AssertFatal(0, "pthread_mutex_lock failed\n"); - - AssertFatal(resp_freelist_inited, "sched_response used before init\n"); - - resp_refcount[sched_response_id]++; - - if (pthread_mutex_unlock(&resp_mutex)) - AssertFatal(0, "pthread_mutex_unlock failed\n"); -} diff --git a/openair2/NR_PHY_INTERFACE/nr_sched_response.h b/openair2/NR_PHY_INTERFACE/nr_sched_response.h deleted file mode 100644 index 33a0af93d0..0000000000 --- a/openair2/NR_PHY_INTERFACE/nr_sched_response.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#ifndef OPENAIR2_NR_PHY_INTERFACE_NR_SCHED_RESPONSE_H -#define OPENAIR2_NR_PHY_INTERFACE_NR_SCHED_RESPONSE_H - -#include "NR_IF_Module.h" - -void init_sched_response(void); -NR_Sched_Rsp_t *allocate_sched_response(void); -void deref_sched_response(int sched_response_id); -void inc_ref_sched_response(int sched_response_id); - -#endif /* OPENAIR2_NR_PHY_INTERFACE_NR_SCHED_RESPONSE_H */ From c8fba9964f24b6aac635aaff86e9c279afa9cdef Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Nov 2025 19:23:30 +0100 Subject: [PATCH 235/275] Remove dead code --- nfapi/oai_integration/nfapi_vnf.c | 1 - openair1/PHY/INIT/nr_init.c | 6 ------ openair1/SCHED/nfapi_lte_dummy.c | 2 -- 3 files changed, 9 deletions(-) diff --git a/nfapi/oai_integration/nfapi_vnf.c b/nfapi/oai_integration/nfapi_vnf.c index 6f73121c68..d5f644055a 100644 --- a/nfapi/oai_integration/nfapi_vnf.c +++ b/nfapi/oai_integration/nfapi_vnf.c @@ -124,7 +124,6 @@ int vnf_nr_unpack_vendor_extension_tlv(nfapi_tl_t *tl, { return -1; } -void install_nr_schedule_handlers(NR_IF_Module_t *if_inst); void install_schedule_handlers(IF_Module_t *if_inst); extern int sf_ahead; diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 5da6559937..eb584e4f8a 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -286,12 +286,6 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) } -//Adding nr_schedule_handler -void install_nr_schedule_handlers(NR_IF_Module_t *if_inst) -{ - if_inst->NR_PHY_config_req = nr_phy_config_request; -} - void nr_phy_config_request_sim(PHY_VARS_gNB *gNB, int N_RB_DL, int N_RB_UL, diff --git a/openair1/SCHED/nfapi_lte_dummy.c b/openair1/SCHED/nfapi_lte_dummy.c index 3e56b51fc1..fa59cbdb71 100644 --- a/openair1/SCHED/nfapi_lte_dummy.c +++ b/openair1/SCHED/nfapi_lte_dummy.c @@ -29,8 +29,6 @@ void nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_p void nr_phy_config_request(NR_PHY_Config_t *gNB){} -void install_nr_schedule_handlers(NR_IF_Module_t *if_inst){} - void nr_dump_frame_parms(NR_DL_FRAME_PARMS *fp){} bool vnf_nr_send_p5_msg(vnf_t *vnf, uint16_t p5_idx, nfapi_nr_p4_p5_message_header_t *msg, uint32_t msg_len) From aa3f01b6779f97753a82099d3b137934fbefa23d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Nov 2025 19:42:26 +0100 Subject: [PATCH 236/275] Delete dead code --- nfapi/oai_integration/nfapi_vnf.c | 91 +------------------------------ 1 file changed, 2 insertions(+), 89 deletions(-) diff --git a/nfapi/oai_integration/nfapi_vnf.c b/nfapi/oai_integration/nfapi_vnf.c index d5f644055a..b251d9d762 100644 --- a/nfapi/oai_integration/nfapi_vnf.c +++ b/nfapi/oai_integration/nfapi_vnf.c @@ -325,71 +325,6 @@ int pnf_config_resp_cb(nfapi_vnf_config_t *config, int p5_idx, nfapi_pnf_config_ return 0; } -int wake_gNB_rxtx(PHY_VARS_gNB *gNB, uint16_t sfn, uint16_t slot) { - struct timespec curr_t; - clock_gettime(CLOCK_MONOTONIC,&curr_t); - //NFAPI_TRACE(NFAPI_TRACE_INFO, "\n wake_gNB_rxtx before assignment sfn:%d slot:%d TIME %d.%d",sfn,slot,curr_t.tv_sec,curr_t.tv_nsec); - gNB_L1_proc_t *proc=&gNB->proc; - gNB_L1_rxtx_proc_t *L1_proc= (slot&1)? &proc->L1_proc : &proc->L1_proc_tx; - - NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s(eNB:%p, sfn:%d, sf:%d)\n", __FUNCTION__, eNB, sfn, sf); - //int i; - struct timespec wait; - clock_gettime(CLOCK_REALTIME, &wait); - wait.tv_sec = 0; - wait.tv_nsec +=5000L; - //wait.tv_nsec = 0; - // wake up TX for subframe n+sf_ahead - // lock the TX mutex and make sure the thread is ready - AssertFatal(gNB->if_inst->sl_ahead==6,"gNB->if_inst->sl_ahead %d : This is hard-coded to 6 in nfapi P7!!!\n",gNB->if_inst->sl_ahead); - if (pthread_mutex_timedlock(&L1_proc->mutex,&wait) != 0) { - LOG_E( PHY, "[gNB] ERROR pthread_mutex_lock for gNB RXTX thread %d (IC %d)\n", L1_proc->slot_rx&1,L1_proc->instance_cnt ); - exit_fun( "error locking mutex_rxtx" ); - return(-1); - } - - { - static uint16_t old_slot = 0; - static uint16_t old_sfn = 0; - proc->slot_rx = old_slot; - proc->frame_rx = old_sfn; - // Try to be 1 frame back - old_slot = slot; - old_sfn = sfn; - //NFAPI_TRACE(NFAPI_TRACE_INFO, "\n wake_gNB_rxtx after assignment sfn:%d slot:%d",proc->frame_rx,proc->slot_rx); - if (old_slot == 0 && old_sfn % 100 == 0) LOG_W( PHY,"[gNB] sfn/slot:%d%d old_sfn/slot:%d%d proc[rx:%d%d]\n", sfn, slot, old_sfn, old_slot, proc->frame_rx, proc->slot_rx); - } - - ++L1_proc->instance_cnt; - //LOG_D( PHY,"[VNF-subframe_ind] sfn/sf:%d:%d proc[frame_rx:%d subframe_rx:%d] L1_proc->instance_cnt_rxtx:%d \n", sfn, sf, proc->frame_rx, proc->subframe_rx, L1_proc->instance_cnt_rxtx); - // We have just received and processed the common part of a subframe, say n. - // TS_rx is the last received timestamp (start of 1st slot), TS_tx is the desired - // transmitted timestamp of the next TX slot (first). - // The last (TS_rx mod samples_per_frame) was n*samples_per_tti, - // we want to generate subframe (n+N), so TS_tx = TX_rx+N*samples_per_tti, - // and proc->subframe_tx = proc->subframe_rx+sf_ahead - L1_proc->timestamp_tx = proc->timestamp_rx + (gNB->if_inst->sl_ahead *fp->samples_per_subframe); - L1_proc->frame_rx = proc->frame_rx; - L1_proc->slot_rx = proc->slot_rx; - L1_proc->frame_tx = (L1_proc->slot_rx > (19-gNB->if_inst->sl_ahead)) ? (L1_proc->frame_rx+1)&1023 : L1_proc->frame_rx; - L1_proc->slot_tx = (L1_proc->slot_rx + gNB->if_inst->sl_ahead)%20; - - //LOG_D(PHY, "sfn/sf:%d%d proc[rx:%d%d] rx:%d%d] About to wake rxtx thread\n\n", sfn, slot, proc->frame_rx, proc->slot_rx, L1_proc->frame_rx, L1_proc->slot_rx); - //NFAPI_TRACE(NFAPI_TRACE_INFO, "\nEntering wake_gNB_rxtx sfn %d slot %d\n",L1_proc->frame_rx,L1_proc->slot_rx); - // the thread can now be woken up - if (pthread_cond_signal(&L1_proc->cond) != 0) { - LOG_E( PHY, "[gNB] ERROR pthread_cond_signal for gNB RXn-TXnp4 thread\n"); - exit_fun( "ERROR pthread_clond_signal" ); - return(-1); - } - - //LOG_D(PHY,"%s() About to attempt pthread_mutex_unlock\n", __FUNCTION__); - pthread_mutex_unlock( &L1_proc->mutex ); - //LOG_D(PHY,"%s() UNLOCKED pthread_mutex_unlock\n", __FUNCTION__); - return(0); -} - int wake_eNB_rxtx(PHY_VARS_eNB *eNB, uint16_t sfn, uint16_t sf) { L1_proc_t *proc=&eNB->proc; L1_rxtx_proc_t *L1_proc= (sf&1)? &proc->L1_proc : &proc->L1_proc_tx; @@ -497,28 +432,6 @@ int phy_sync_indication(struct nfapi_vnf_p7_config *config, uint8_t sync) { } -int phy_slot_indication(struct nfapi_vnf_p7_config *config, uint16_t phy_id, uint16_t sfn, uint16_t slot) { - static uint8_t first_time = 1; - - if (first_time) { - NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] slot indication %d.%d\n", sfn, slot); - first_time = 0; - } - - if (RC.gNB && RC.gNB[0]->configured) { - // uint16_t sfn = NFAPI_SFNSF2SFN(sfn_sf); - // uint16_t sf = NFAPI_SFNSF2SF(sfn_sf); - LOG_D(PHY,"[VNF] slot indication sfn:%d slot:%d\n", sfn, slot); - wake_gNB_rxtx(RC.gNB[0], sfn, slot); // DONE: find NR equivalent - } else { - NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] %s() RC.gNB:%p\n", __FUNCTION__, RC.gNB); - - if (RC.gNB) NFAPI_TRACE(NFAPI_TRACE_INFO, "RC.gNB[0]->configured:%d\n", RC.gNB[0]->configured); - } - - return 0; -} - int phy_subframe_indication(struct nfapi_vnf_p7_config *config, uint16_t phy_id, uint16_t sfn_sf) { static uint8_t first_time = 1; @@ -1302,7 +1215,7 @@ void *configure_nr_p7_vnf(void *ptr) p7_vnf->config->port = p7_vnf->local_port; #ifndef ENABLE_AERIAL p7_vnf->config->sync_indication = &phy_sync_indication; - p7_vnf->config->slot_indication = &phy_slot_indication; + p7_vnf->config->slot_indication = NULL; p7_vnf->config->harq_indication = &phy_harq_indication; #endif p7_vnf->config->nr_crc_indication = &phy_nr_crc_indication; @@ -1364,7 +1277,7 @@ void *vnf_p7_thread_start(void *ptr) { p7_vnf->config->port = p7_vnf->local_port; p7_vnf->config->sync_indication = &phy_sync_indication; p7_vnf->config->subframe_indication = &phy_subframe_indication; - p7_vnf->config->slot_indication = &phy_slot_indication; + p7_vnf->config->slot_indication = NULL; p7_vnf->config->harq_indication = &phy_harq_indication; p7_vnf->config->crc_indication = &phy_crc_indication; From 15441c741c48305c8f5fbf0e00f881c56791604d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 14 Nov 2025 17:31:57 +0100 Subject: [PATCH 237/275] Cleanup: move slot_parameters to where they are used --- openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index c1a4dd152d..f74685ed71 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -122,15 +122,6 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, nrLDPC_TB_encoding_parameters_t TBs[n_dlsch]; memset(TBs, 0, sizeof(TBs)); - nrLDPC_slot_encoding_parameters_t slot_parameters = {.frame = frame, - .slot = slot, - .nb_TBs = n_dlsch, - .threadPool = &gNB->threadPool, - .tinput = tinput, - .tprep = tprep, - .tparity = tparity, - .toutput = toutput, - .TBs = TBs}; int num_segments = 0; @@ -280,6 +271,15 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, dlsch_offset += ceil_mod(dlsch_size, 8 * 64); } + nrLDPC_slot_encoding_parameters_t slot_parameters = {.frame = frame, + .slot = slot, + .nb_TBs = n_dlsch, + .threadPool = &gNB->threadPool, + .tinput = tinput, + .tprep = tprep, + .tparity = tparity, + .toutput = toutput, + .TBs = TBs}; gNB->nrLDPC_coding_interface.nrLDPC_coding_encoder(&slot_parameters); for (int i = 0; i < n_dlsch; i++) { From 0713b2f655128d2d6a21e96aea4afb147d1603cb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 4 Nov 2025 13:09:28 +0100 Subject: [PATCH 238/275] Rewrite doc/SW-archi-graph.md --- doc/SW-archi-graph.md | 165 ++++++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 71 deletions(-) diff --git a/doc/SW-archi-graph.md b/doc/SW-archi-graph.md index cf826e6e43..467ce5cd04 100644 --- a/doc/SW-archi-graph.md +++ b/doc/SW-archi-graph.md @@ -1,74 +1,97 @@ +This document is a high-level overview over the L1 threading mechanism. + ```mermaid flowchart TB - A[ru_thread] --> RFin>block rx_rf] --> feprx - feprx --> half-slot --> end_feprx - feprx --> second-thread -- block_end_feprx --> end_feprx>feprx] - end_feprx --> rx_nr_prach_ru -rx_nr_prach_ru -- block_queue_singleton --> resp_L1>resp L1] -resp_L1 -- async launch --> rx_func -resp_L1 -- immediate return --> RFin - -subgraph rxfunc -rx_func_implem[rx_func] - subgraph rxfuncbeg - handle_nr_slot_ind - --> rnti_to_remove-mgmt - --> L1_nr_prach_procedures - --> apply_nr_rotation_RX - end - subgraph phy_procedures_gNB_uespec_RX - fill_ul_rb_mask - --> pucch(decode each gNB->pucch) - -->nr_fill_ul_indication - --> nr_ulsch_procedures - --> nr_ulsch_decoding - --> segInParallel[[all segments decode in parallel]] - --> barrier_end_of_ulsch_decoding - end - subgraph NR_UL_indication - handle_nr_rach - --> handle_nr_uci - --> handle_nr_ulsch - subgraph gNB_dlsch_ulsch_scheduler - run_pdcp - --> schedule_xxxx - end - handle_nr_ulsch --> gNB_dlsch_ulsch_scheduler - subgraph NR_Schedule_response - L1_tx_free3>L1_tx_free] - --> handle_nr_nfapi_xxx_pdu - --> sendTxFilled((L1_tx_filled)) - --> nr_fill_ul_xxx - --> nr_schedule_rx_prach() - end - gNB_dlsch_ulsch_scheduler --> NR_Schedule_response - end - rx_func_implem --> rxfuncbeg - rxfuncbeg --> phy_procedures_gNB_uespec_RX -phy_procedures_gNB_uespec_RX --> NR_UL_indication --- block_queue_block_PNF_monolithic --> L1_tx_free2>L1 tx filled] --- async launch --> tx_func -L1_tx_free2 -- send_msg --> rsp((resp_L1)) -end -rx_func --> rxfunc - - -subgraph tx_func - direction LR - subgraph phy_procedures_gNB_TX - dcitop[nr_generate dci top] - --> nr_generate_csi_rs - --> apply_nr_rotation_TX - -- send_msg --> end_tx_func((L1_tx_out)) - end - subgraph tx_reorder_thread - L1_tx_out>L1_tx_out] - --> reorder{re order} --> reorder - reorder --> ru_tx_func - reorder --> L1_tx_free((L1_tx_free)) - ru_tx_func --> feptx_prec - --> feptx_ofdm - end -end - + ru_thread --> RFin[block rx_rf] --> UL{is UL slot?} + UL -- yes --> wait_free_rx_tti --> fep_rx --> rx_nr_prach_ru --> msg:L1_tx_out + UL -- no --> msg:L1_tx_out + msg:L1_tx_out -- asnyc launch --> L1_tx_thread + msg:L1_tx_out --> RFin ``` + +The main thread is `ru_thread()`. It blocks on reception of radio samples +(either time domain or frequency domain). In the case of an UL slot, it waits +that no more than N UL jobs are scheduled (via `wait_free_rx_tti()`, which +waits on queue `L1_rx_out`, cf. the RX L1 processing further below). Then: + +- if the radio is time domain-based, it performs RX front-end processing (RX + FEP -> `fep_rx()`, i.e. DFT) to reach a frequency domain representation of + the RX signal, as well as does DFT for PRACH. +- if the radio is frequency domain-based, nothing is done. + +Afterwards, it triggers TX processing by pushing a message into the FIFO queue +`L1_tx_out`, which asynchronously starts a TX job in `L1_tx_thread()` (see +below). After that, it blocks again on reception on the radio. + + +```mermaid +flowchart TB + L1_tx_thread --> TX_in[block L1_tx_out] --> NR_slot_indication + + subgraph tx_func + + NR_slot_indication --> msg:resp_L1 --> phy_procedures_gNB_tx --> ru_tx_func + NR_slot_indication["run the scheduler: + - monolithic: run_scheduler_monolithic() + - nFAPI: send indication via pnf_send_slot_ind()"] + msg:resp_L1 -- async launch --> L1_RX_thread + end + ru_tx_func --> TX_in +``` + +The `L1_tx_thread()` processes individual TX jobs sequentially, by waiting for +new messages on queue `L1_tx_out`, signalling individual TX jobs. For each +message, it calls `tx_func()` which does in order: + +- run the scheduler through `NR_slot_indication`, which corresponds to a + "Slot.indication" in FAPI parlance. This runs the scheduler, and schedules a + given slot (either downlink, uplink, or both). +- trigger RX processing by pushing a message into the FIFO queue `resp_L1`, + asynchronously starting an RX job in `L1_rx_thread()` (see below). +- process the current L1 TX job through `phy_procedures_gNB_tx()` +- write to the radio board via `ru_tx_func()`. + +After these steps, `tx_func()` return to `L1_tx_thread()`, which will wait for +the next TX job. + +```mermaid +flowchart TB + L1_rx_thread --> RX_in[block resp_L1] --> L1_nr_prach_proc + + subgraph rx_func + + L1_nr_prach_proc --> phase_comp{apply phase comp.?} + phase_comp -- yes --> apply_nr_rotation --> phy_procedures_gNB_uespec_RX + phase_comp -- no --> phy_procedures_gNB_uespec_RX + phy_procedures_gNB_uespec_RX --> NR_ul_indication --> msg:L1_rx_out + NR_ul_indication["run the scheduler: NR_UL_indication()"] + msg:L1_rx_out -- async signal free --> ru_thread + end + msg:L1_rx_out --> RX_in +``` + +The `L1_rx_thread()` processes individual RX jobs sequentially. It waits for a +new RX job through the queue `resp_L1`, and then calls `rx_func()`, which does +in order: + +- run PRACH processing via `L1_nr_prach_proc()` +- optionally apply rotation to the RX signal if phase compensation is to be + applied +- run the current L1 RX job through (`phy_procedures_gNB_uespec_RX()`), which + notably includes PUCCH, PUSCH, SRS processing +- call the scheduler through `NR_ul_indication()`, which corresponds to FAPI + uplink messages (e.g., `RX_data.indication`, `CRC.indication`, + `UCI.indication` etc.) +- signal completion via FIFO queue `L1_rx_out()`, which tells `ru_thread()` + that RX processing finished. + +The signalling of scheduler data is done through a variable `UL_INFO`, which is +filled by `L1_nr_prach_proc()` (for PRACH) and `phy_procedures_gNB_uespec_RX()` +(for PUCCH, PUSCH, SRS). + +After these steps, `rx_func()` returns to `L1_rx_thread()`, which will wait the +next RX job. + +Note that while individual TX (RX) jobs are run sequentially through +`L1_tx_thread()` (`L1_rx_thread()`), both TX and RX processing run in +parallel. From bd8f088ffa8a39d2f976faf569cf99c38e2092a4 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 11 Nov 2025 16:14:17 +0100 Subject: [PATCH 239/275] Update documentation FAPI --- doc/NR_NFAPI_archi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/NR_NFAPI_archi.md b/doc/NR_NFAPI_archi.md index 8065ddb4fc..8e6647f73c 100644 --- a/doc/NR_NFAPI_archi.md +++ b/doc/NR_NFAPI_archi.md @@ -161,9 +161,9 @@ unpack function is called. ## P7 DL Transmission by VNF DL messages are scheduled at the VNF, through gNB_dlsch_ulsch_scheduler(). gNB_dlsch_ulsch_scheduler() is called when -handling a SLOT.indication message in trigger_scheduler(). +handling a SLOT.indication message in phy_nr_slot_indication(). -The function trigger_scheduler(nfapi_nr_slot_indication_scf_t *slot_ind) calls the functions oai_nfapi_[DL P7 msg]_ +The function phy_nr_slot_indication(nfapi_nr_slot_indication_scf_t *slot_ind) calls the functions oai_nfapi_[DL P7 msg]_ req(), calling in turn call the send_p7_msg function pointer, which contain the logic to pack the message into a buffer and send it to the PNF. Finally, NR_UL_indication is called to process the other P7 messages received from the PNF that were put in theire @@ -215,7 +215,7 @@ bool nfapi_vnf_p7_tx_data_req(nfapi_vnf_p7_config_t* config, nfapi_nr_tx_data_re graph TD softmodem_start[nr-softmodem init] --> P5_configuration[configure_nr_nfapi_vnf] --> transport_init[Transport mechanism init] --> send_first_msg[Send first P5 message to PNF] transport_init[Transport mechanism init] --> P5_recv_loop[P5 receive loop] -- P5 Message processing --> P5_recv_loop[P5 receive loop] - P5_recv_loop[P5 receive loop] -- Start.response callback called --> P7_config[P7 Configuration] --> P7_loop[P7 Loop] --> P7_msg_recv[P7 message received] --> call_vnf_handle_p7[Call vnf_nr_handle_p7_msg] --> hdr_unpack[Unpack header] --> call_specific_p7_handler[Call specific P7 message handler] --> slot_ind[Is a SLOT.indication?] -- Yes --> trig_scheduler[Call trigger_scheduler] --> gNB_ulsch_dlsch_sched[Call gNB_dlsch_ulsch_scheduler] --> send_p7_dl[Process and send P7 messages to PNF] --> call_ul_ind[Call NR_UL_indication] --> proc_p7[Process P7 messages in queue] --> P7_loop[P7 Loop] + P5_recv_loop[P5 receive loop] -- Start.response callback called --> P7_config[P7 Configuration] --> P7_loop[P7 Loop] --> P7_msg_recv[P7 message received] --> call_vnf_handle_p7[Call vnf_nr_handle_p7_msg] --> hdr_unpack[Unpack header] --> call_specific_p7_handler[Call specific P7 message handler] --> slot_ind[Is a SLOT.indication?] -- Yes --> trig_scheduler[Call phy_nr_slot_indication] --> gNB_ulsch_dlsch_sched[Call gNB_dlsch_ulsch_scheduler] --> send_p7_dl[Process and send P7 messages to PNF] --> call_ul_ind[Call NR_UL_indication] --> proc_p7[Process P7 messages in queue] --> P7_loop[P7 Loop] slot_ind[Is a SLOT.indication?] -- No --> put_queue[Put message in queue] --> P7_loop[P7 Loop]; From f5239f49b09e4d5d0f15dc472958c7c7a8d5ee46 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 15 Oct 2025 12:20:52 +0200 Subject: [PATCH 240/275] PDCP: Increase upper DRB ID limit --- openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c index 07edfb5814..f4dc5c1d5d 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c @@ -178,7 +178,7 @@ void nr_pdcp_ue_add_srb_pdcp_entity(nr_pdcp_ue_t *ue, int srb_id, nr_pdcp_entity /* must be called with lock acquired */ void nr_pdcp_ue_add_drb_pdcp_entity(nr_pdcp_ue_t *ue, int drb_id, nr_pdcp_entity_t *entity) { - if (drb_id < 1 || drb_id > 5) { + if (drb_id < 1 || drb_id > MAX_DRBS_PER_UE) { LOG_E(PDCP, "%s:%d:%s: fatal, bad drb id\n", __FILE__, __LINE__, __FUNCTION__); exit(1); } From a9ad90a26ace63c5ed5f79c2f5bbe0b955e12259 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Mon, 3 Nov 2025 09:54:10 +0100 Subject: [PATCH 241/275] Add FOR_EACH_SEQ_ARR to .clang-format --- .clang-format | 1 + 1 file changed, 1 insertion(+) diff --git a/.clang-format b/.clang-format index a7248821a9..3b0be979ba 100644 --- a/.clang-format +++ b/.clang-format @@ -62,6 +62,7 @@ ForEachMacros: - BOOST_FOREACH - RB_FOREACH - UE_iterator + - FOR_EACH_SEQ_ARR IncludeBlocks: Preserve IncludeCategories: - Regex: '^' From a58cf178edf8fa190def6f06d177bcc77275257b Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 17 Jun 2025 11:38:48 +0200 Subject: [PATCH 242/275] RRC PDU Session Release: end-to-end DRB and PDU session release This commit implements proper PDU Session Release handling across RRC, PDCP, F1AP, E1AP to ensure clean resource cleanup when a PDU session is terminated. Key changes: NGAP: In `rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND()`: * Populates E1AP Bearer Modification for each released session * If a CU-UP is connected, triggers `bearer_context_mod()` for PDU teardown PDCP: * Added `nr_pdcp_release_drbs()` to remove all DRBs linked to a given PDU session * Called from `e1_bearer_context_modif()` for correct clean-up during session release F1AP UE Context Modification: Replace direct RRC message transfer with F1AP UE Context Modification Request to properly handle PDU session release according to 3GPP TS 38.331. 3GPP TS 38.331 `5.3.5.6.4 DRB release` says: whether or not the RLC and MAC entities associated with a PDCP entity are reset or released is determined by the CellGroupConfig. Thus, updated CellGroupConfig needs to be received by the UE. This has to be retrieved by CUCP from the DU. Therefore: After receiving an NGAP_PDUSESSION_RELEASE_COMMAND, the CUCP shall: 1) forward (store for later) the NAS PDU to the UE, if present as per 3GPP TS 38.413, and 2) send a UE Context Modification to the DU with the list of DRBs to release and the RRC container (RRCReconfiguration with NAS PDU) After reception of UE Context Modification Response, the list of drbs to release is prepared based on PDU session status PDU_SESSION_STATUS_TORELEASE in rrc_gNB_generate_dedicatedRRCReconfiguration and then encoded in drb_ToReleaseList for a new RRCReconfiguration with the new cellGroupConfig. Key changes: * rrc_gNB_generate_dedicatedRRCReconfiguration_release() now uses F1AP UE Context Modification Request instead of direct RRC transfer * Only DRBs to be released are now added to the list. * The ASN.1 memory allocation is entirely done in build_RRCReconfiguration_IEs * No longer needed to store ASN1 struct in UE context GTP-U: * Previous implementation had incomplete tunnel cleanup during PDU session release - Missing distinction between N3 (PDU session level) and F1-U (DRB level) tunnels - Lack of proper DRB identification for F1-U tunnel deletion Major Changes: * GTP tunnel teardown now handled in `e1_bearer_context_modif()` using the correct F1 or N3 GTP instance (`newGtpuDeleteOneTunnel()`) * Enhanced Tunnel Deletion Logic (cucp_cuup_handler.c): - Separate handling for N3 tunnels (1 per PDU session) and F1-U tunnels (1 per DRB) - Added proper DRB identification for F1-U tunnel cleanup - Improved error handling with detailed logging for each tunnel deletion - Enhanced logging to distinguish between N3 and F1-U tunnel operations * New PDCP API Function (nr_pdcp_oai_api.c/h): - Added nr_pdcp_get_drb_ids_for_pdusession() function - Enables retrieval of all DRB IDs associated with a specific PDU session - Supports proper F1-U tunnel cleanup by providing DRB identifiers in CUUP * Enhanced GTP Interface (gtp_itf.cpp/h): - Modified newGtpuDeleteOneTunnel() to accept tunnel_type parameter - Improved logging to distinguish between PDU session and DRB tunnel types - Better error messages for tunnel deletion failures - Enhanced debugging information for remaining tunnels Clean-up: * Ensured xid tracking works consistently between PDU Session Establishment (with NAS PDU) and dedicated RRCReconfiguration * Add function to release GTP-U tunnel in Bearer Context Modification Request handler * Do not send rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE when no PDU session are to be released: it is sent upon completion of the RRCReconfiguration and needs to contain released PDU Sessions (mandatory IE), therefore it is no necessary to send it in case there is no PDU session to release. Related to #867 Co-authored-by: rmagueta --- .../LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 5 +- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 55 +++++++- openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c | 24 ++++ openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h | 1 + openair2/RRC/NR/MESSAGES/asn1_msg.c | 8 +- openair2/RRC/NR/MESSAGES/asn1_msg.h | 3 +- .../RRC/NR/MESSAGES/tests/test_asn1_msg.cpp | 1 - openair2/RRC/NR/nr_rrc_defs.h | 2 - openair2/RRC/NR/nr_rrc_proto.h | 6 - openair2/RRC/NR/rrc_gNB.c | 128 +++++++++++------- openair2/RRC/NR/rrc_gNB_NGAP.c | 48 ++++--- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 5 +- openair3/ocp-gtpu/gtp_itf.cpp | 6 +- 13 files changed, 195 insertions(+), 97 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index 064857a8e3..5a982df256 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -381,12 +381,9 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE, { DevAssert(req_drbs != NULL && cellGroupConfig != NULL); instance_t f1inst = get_f1_gtp_instance(); - cellGroupConfig->rlc_BearerToReleaseList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToReleaseList)); AssertFatal(cellGroupConfig->rlc_BearerToReleaseList != NULL, "out of memory\n"); - /* Note: the actual GTP tunnels are already removed in the F1AP message - * decoding */ for (int i = 0; i < drbs_len; i++) { const f1ap_drb_to_release_t *drb = &req_drbs[i]; @@ -401,7 +398,7 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE, if (idx < cellGroupConfig->rlc_BearerToAddModList->list.count) { nr_mac_remove_lcid(&UE->UE_sched_ctrl, lcid); nr_rlc_release_entity(UE->rnti, lcid); - if (f1inst >= 0) + if (f1inst >= 0) /* Delete F1 tunnel */ newGtpuDeleteOneTunnel(f1inst, UE->rnti, drb->id); asn_sequence_del(&cellGroupConfig->rlc_BearerToAddModList->list, idx, 1); long *plcid = malloc(sizeof(*plcid)); diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index 4b95963257..ecdf3dfd72 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -259,17 +259,58 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) get_e1_if()->bearer_setup_response(&resp); } +static void release_gtpu_tunnel(uint32_t ue_id, int pdusession_id) +{ + instance_t n3inst = get_n3_gtp_instance(); + + // Handle N3 tunnel (1 tunnel per PDU session) + if (n3inst >= 0) { + LOG_I(E1AP, "UE %u: Deleting N3 tunnel for PDU session %d\n", ue_id, pdusession_id); + int result = newGtpuDeleteOneTunnel(n3inst, ue_id, pdusession_id); + if (result == GTPNOK) { + LOG_E(E1AP, "UE %u: Failed to delete N3 tunnel for PDU session %d\n", ue_id, pdusession_id); + } + } +} + +static void release_f1_drbs(uint32_t ue_id, int pdusession_id) +{ + instance_t f1inst = get_f1_gtp_instance(); + // Handle F1 tunnels (1 tunnel per DRB, fetch PDCP entities and get DRB IDs) + int drb_ids[MAX_DRBS_PER_UE]; + int n_drbs = nr_pdcp_get_drb_ids_for_pdusession(ue_id, pdusession_id, drb_ids); + if (n_drbs > 0) { + LOG_I(E1AP, "UE %u: Deleting %d F1 tunnels for PDU session %d\n", ue_id, n_drbs, pdusession_id); + // Delete each F1 tunnel using the DRB IDs + for (int i = 0; i < n_drbs; i++) { + int drb_id = drb_ids[i]; + if (f1inst >= 0) { + int result = newGtpuDeleteOneTunnel(f1inst, ue_id, drb_id); + if (result == GTPNOK) { + LOG_E(E1AP, "UE %u: Failed to delete F1 tunnel for DRB %d\n", ue_id, drb_id); + } + } + // Release PDCP entity + nr_pdcp_release_drb(ue_id, drb_id); + } + } else { + LOG_W(E1AP, "UE %u: No DRBs found for PDU session %d\n", ue_id, pdusession_id); + } +} + /** * @brief Fill Bearer Context Modification Response and send to callback */ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) { - AssertFatal(req->numPDUSessionsMod > 0, "need at least one PDU session to modify\n"); + AssertFatal(req->numPDUSessionsMod > 0 || req->numPDUSessionsRem > 0, + "need at least one PDU session to modify or release (mod=%u, rel=%u)\n", + req->numPDUSessionsMod, + req->numPDUSessionsRem); e1ap_bearer_modif_resp_t modif = { .gNB_cu_cp_ue_id = req->gNB_cu_cp_ue_id, .gNB_cu_up_ue_id = req->gNB_cu_up_ue_id, - .numPDUSessionsMod = req->numPDUSessionsMod, }; instance_t f1inst = get_f1_gtp_instance(); @@ -284,6 +325,7 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) req->pduSessionMod[i].numDRB2Modify); modif.pduSessionMod[i].id = req->pduSessionMod[i].sessionId; modif.pduSessionMod[i].numDRBModified = req->pduSessionMod[i].numDRB2Modify; + modif.numPDUSessionsMod++; /* DRBs to modify */ for (int j = 0; j < req->pduSessionMod[i].numDRB2Modify; j++) { const DRB_nGRAN_to_mod_t *to_modif = &req->pduSessionMod[i].DRBnGRanModList[j]; @@ -347,6 +389,15 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) } } + /* PDU Session Resource To Remove List (see 9.3.3.12 of TS 38.463) */ + for (int i = 0; i < req->numPDUSessionsRem; i++) { + modif.pduSessionMod[i].id = req->pduSessionRem[i].sessionId; + modif.numPDUSessionsMod++; + release_gtpu_tunnel(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId); + release_f1_drbs(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId); + nr_sdap_delete_entity(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId); + } + get_e1_if()->bearer_modif_response(&modif); } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index 94d44fe8ce..3fd4c4bdd4 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -653,6 +653,30 @@ void nr_pdcp_add_srbs(eNB_flag_t enb_flag, LOG_W(PDCP, "nr_pdcp_add_srbs() with void list\n"); } +/** @brief Get DRB IDs for a specific PDU session */ +int nr_pdcp_get_drb_ids_for_pdusession(ue_id_t ue_id, long pdusession_id, int *drb_ids) +{ + int num_found = 0; + nr_pdcp_manager_lock(nr_pdcp_ue_manager); + nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id); + if (ue) { + for (int i = 0; i < MAX_DRBS_PER_UE; i++) { + nr_pdcp_entity_t *drb = ue->drb[i]; + if (drb && drb->pdusession_id == pdusession_id) { + if (num_found < MAX_DRBS_PER_UE) { + drb_ids[num_found] = drb->rb_id; + num_found++; + } else { + LOG_W(PDCP, "Found more DRBs than MAX_DRBS_PER_UE=%d\n", MAX_DRBS_PER_UE); + break; + } + } + } + } + nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + return num_found; +} + uint64_t get_pdcp_optmask(void) { return pdcp_optmask; diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h index 884ea11f60..75435ed695 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h @@ -66,6 +66,7 @@ void nr_pdcp_reconfigure_srb(ue_id_t ue_id, int srb_id, long t_Reordering); void nr_pdcp_reconfigure_drb(ue_id_t ue_id, int drb_id, NR_PDCP_Config_t *pdcp_config); void nr_pdcp_release_srb(ue_id_t ue_id, int srb_id); void nr_pdcp_release_drb(ue_id_t ue_id, int drb_id); +int nr_pdcp_get_drb_ids_for_pdusession(ue_id_t ue_id, long pdusession_id, int *drb_ids); void add_srb(int is_gnb, ue_id_t UEid, diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 2136e966ab..585b1e48e2 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -519,7 +519,13 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re cfg->drb_ToAddModList = params->drb_config_list; cfg->securityConfig = params->security_config; cfg->srb3_ToRelease = NULL; - cfg->drb_ToReleaseList = params->drb_release_list; + if (params->n_drb_rel) { + asn1cCalloc(cfg->drb_ToReleaseList, to_release); + for (int i = 0; i < params->n_drb_rel; i++) { + asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release); + *DRB_release = params->drb_rel[i]; + } + } } /* measConfig */ diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 48a0ea734b..eb8da91c2a 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -64,7 +64,8 @@ typedef struct { uint8_t transaction_id; NR_SRB_ToAddModList_t *srb_config_list; NR_DRB_ToAddModList_t *drb_config_list; - NR_DRB_ToReleaseList_t *drb_release_list; + int *drb_rel; + int n_drb_rel; NR_SecurityConfig_t *security_config; NR_MeasConfig_t *meas_config; byte_array_t dedicated_NAS_msg_list[MAX_DRBS_PER_UE]; diff --git a/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp b/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp index 1f92c5aae5..025978218e 100644 --- a/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp +++ b/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp @@ -88,7 +88,6 @@ TEST(nr_asn1, paging) void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params) { ASN_STRUCT_FREE(asn_DEF_NR_MeasConfig, params.meas_config); - ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, params.drb_release_list); ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, params.drb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, params.srb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SecurityConfig, params.security_config); diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 27e17e03f0..e8820bcc76 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -167,8 +167,6 @@ typedef struct nr_handover_context_s nr_handover_context_t; typedef struct gNB_RRC_UE_s { time_t last_seen; // last time this UE has been accessed - NR_DRB_ToReleaseList_t *DRB_ReleaseList; - NR_SRB_INFO_TABLE_ENTRY Srb[NR_NUM_SRB]; NR_MeasConfig_t *measConfig; nr_handover_context_t *ho_context; diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 777ea9a8d1..2fc7bf5e5e 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -66,12 +66,6 @@ void *rrc_gnb_task(void *args_p); int nr_rrc_reconfiguration_req(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, const int dl_bwp_id, const int ul_bwp_id); -void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc, - gNB_RRC_UE_t *ue_p, - uint8_t xid, - uint32_t nas_length, - uint8_t *nas_buffer); - NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid); bool ue_associated_to_cuup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue); diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index bc4cca38f1..1e08c9cf25 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -629,10 +629,10 @@ static NR_ReportConfigToAddMod_t *prepare_a3_event_report(const nr_a3_event_t *a void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params) { - ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, params.drb_release_list); ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, params.drb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, params.srb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SecurityConfig, params.security_config); + free(params.drb_rel); for (int i = 0; i < params.num_nas_msg; i++) FREE_AND_ZERO_BYTE_ARRAY(params.dedicated_NAS_msg_list[i]); } @@ -710,19 +710,37 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC nr_rrc_reconfig_param_t params = {.cell_group_config = UE->masterCellGroup, .transaction_id = xid, .drb_config_list = DRBs, - .drb_release_list = UE->DRB_ReleaseList, .meas_config = UE->measConfig, .srb_config_list = SRBs}; - UE->DRB_ReleaseList = NULL; // pointer transferred to params FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &UE->pduSessions) { pdusession_t *session = &item->param; + // Collect NAS PDUs if (session->nas_pdu.len > 0) { params.dedicated_NAS_msg_list[params.num_nas_msg++] = session->nas_pdu; session->nas_pdu.buf = NULL; session->nas_pdu.len = 0; LOG_D(NR_RRC, "Transfer NAS info with size %ld to RRCReconfiguration params\n", session->nas_pdu.len); } + // Collect DRBs to release for PDU sessions marked for release + if (item->status == PDU_SESSION_STATUS_TORELEASE) { + if (!params.drb_rel) + params.drb_rel = calloc_or_fail(MAX_DRBS_PER_UE, sizeof(int)); + FOR_EACH_SEQ_ARR (drb_t *, drb, &UE->drbs) { + if (drb->pdusession_id == session->pdusession_id) { + if (params.n_drb_rel >= MAX_DRBS_PER_UE) { + LOG_E(NR_RRC, "UE %d: Too many DRBs to release (max %d)\n", UE->rrc_ue_id, MAX_DRBS_PER_UE); + break; + } + params.drb_rel[params.n_drb_rel++] = drb->drb_id; + LOG_D(NR_RRC, + "UE %d: Added DRB %d to release list for PDU session %d\n", + UE->rrc_ue_id, + drb->drb_id, + session->pdusession_id); + } + } + } } if (UE->nas_pdu.len > 0) { @@ -750,21 +768,24 @@ static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_ { /* do not re-establish PDCP for any bearer */ nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue_p, 0, false); - ue_p->xids[params.transaction_id] = RRC_PDUSESSION_ESTABLISH; + FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { + if (item->status == PDU_SESSION_STATUS_NEW) { + item->status = PDU_SESSION_STATUS_DONE; + continue; + } + // Set xid for all PDU sessions + item->xid = params.transaction_id; + } + + // Set xid for RRC transaction + ue_p->xids[params.transaction_id] = params.n_drb_rel > 0 ? RRC_PDUSESSION_RELEASE : RRC_PDUSESSION_ESTABLISH; + byte_array_t msg = rrc_gNB_encode_RRCReconfiguration(rrc, ue_p, params); if (msg.len <= 0) { LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id); return; } - FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { - if (item->param.nas_pdu.buf) - item->xid = params.transaction_id; - if (item->status < PDU_SESSION_STATUS_ESTABLISHED) { - item->status = PDU_SESSION_STATUS_DONE; - } - } - LOG_UE_DL_EVENT(ue_p, "Generate RRCReconfiguration (bytes %ld, xid %d)\n", msg.len, params.transaction_id); const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len); @@ -835,44 +856,27 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t free_byte_array(msg); } -//----------------------------------------------------------------------------- -void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc, - gNB_RRC_UE_t *ue_p, - uint8_t xid, - uint32_t nas_length, - uint8_t *nas_buffer) -//----------------------------------------------------------------------------- +static void rrc_gNB_send_f1_drb_release_request(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, int *drb_to_release, int n_drb_to_release) { - nr_rrc_reconfig_param_t params = {.transaction_id = xid}; + f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); + f1ap_ue_context_mod_req_t req = { + .gNB_CU_ue_id = ue_p->rrc_ue_id, + .gNB_DU_ue_id = ue_data.secondary_ue, + .servCellIndex = 0, + }; + req.plmn = malloc_or_fail(sizeof(*req.plmn)); + *req.plmn = rrc->configuration.plmn[0]; + req.nr_cellid = malloc_or_fail(sizeof(*req.nr_cellid)); + *req.nr_cellid = rrc->nr_cellid; + req.drbs_rel = malloc_or_fail(sizeof(*req.drbs_rel)); + req.drbs_rel_len = n_drb_to_release; + memcpy(req.drbs_rel, drb_to_release, n_drb_to_release * sizeof(int)); - NR_DRB_ToReleaseList_t *to_release = CALLOC(sizeof(*to_release), 1); - FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { - if ((item->status != PDU_SESSION_STATUS_TORELEASE) && item->xid != xid) - continue; - FOR_EACH_SEQ_ARR(drb_t *, drb, &ue_p->drbs) { - if (drb->pdusession_id == item->param.pdusession_id) { - asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release); - *DRB_release = drb->drb_id; // DRB ID - } - } - } - params.drb_release_list = to_release; - - if (nas_length > 0) { - params.dedicated_NAS_msg_list[params.num_nas_msg].buf = nas_buffer; - params.dedicated_NAS_msg_list[params.num_nas_msg++].len = nas_length; - } - byte_array_t msg = do_RRCReconfiguration(¶ms); - if (msg.len <= 0) { - LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id); - return; - } - LOG_DUMPMSG(NR_RRC, DEBUG_RRC, msg.buf, msg.len, "[MSG] RRC Reconfiguration\n"); - LOG_I(NR_RRC, "UE %d: Generate NR_RRCReconfiguration (bytes %ld)\n", ue_p->rrc_ue_id, msg.len); - const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; - nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len); - free_RRCReconfiguration_params(params); - free_byte_array(msg); + /* send UE Context Modification to DU without attaching any RRC container */ + rrc->mac_rrc.ue_context_modification_request(ue_data.du_assoc_id, &req); + LOG_I(NR_RRC, "UE %d: send F1 UE Context Modification Request with DRB release (%d DRBs)\n", ue_p->rrc_ue_id, req.drbs_rel_len); + free_ue_context_mod_req(&req); } static void fill_security_info(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, security_information_t *secInfo) @@ -1851,9 +1855,6 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U switch (UE->xids[xid]) { case RRC_PDUSESSION_RELEASE: { - gtpv1u_gnb_delete_tunnel_req_t req = {0}; - gtpv1u_delete_ngu_tunnel(rrc->module_id, &req); - // NGAP_PDUSESSION_RELEASE_RESPONSE rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid); } break; case RRC_PDUSESSION_ESTABLISH: @@ -2732,15 +2733,19 @@ void rrc_gNB_process_e1_bearer_context_setup_failure(e1ap_bearer_context_setup_f */ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t *resp) { + LOG_I(NR_RRC, "Received E1AP Bearer Context Modification Response for UE CU-CP ID %d\n", resp->gNB_cu_cp_ue_id); gNB_RRC_INST *rrc = RC.nrrrc[0]; rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id); if (ue_context_p == NULL) { LOG_E(RRC, "no UE with CU-CP UE ID %d found\n", resp->gNB_cu_cp_ue_id); return; } + gNB_RRC_UE_t *ue = &ue_context_p->ue_context; int n_drb_mod = 0; int drb_ids[MAX_DRBS_PER_UE] = {0}; + int drb_to_release[MAX_DRBS_PER_UE] = {0}; + int n_drb_to_release = 0; e1_pdcp_status_info_t pdcp_status[MAX_DRBS_PER_UE] = {0}; for (int i = 0; i < resp->numPDUSessionsMod; ++i) { const pdu_session_modif_t *pdu = &resp->pduSessionMod[i]; @@ -2753,13 +2758,32 @@ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t pdcp_status[n_drb_mod++] = *pdu->DRBnGRanModList[j].pdcp_status; } } + // Collect DRBs to release for PDU sessions marked for release + rrc_pdu_session_param_t *pdu_session = find_pduSession(&ue->pduSessions, pdu->id); + if (pdu_session && pdu_session->status == PDU_SESSION_STATUS_TORELEASE) { + FOR_EACH_SEQ_ARR(drb_t *, drb, &ue->drbs) { + if (drb->pdusession_id == pdu->id) { + DevAssert(n_drb_to_release < MAX_DRBS_PER_UE); + drb_to_release[n_drb_to_release++] = drb->drb_id; + } + } + if (n_drb_to_release == 0) { + LOG_E(NR_RRC, "UE %d: no DRBs to release for PDU session %ld\n", ue->rrc_ue_id, pdu->id); + } + } } + if (n_drb_mod) { LOG_I(NR_RRC, "UE %d: received PDU Status Info - send UL RAN Status Transfer\n", resp->gNB_cu_cp_ue_id); - gNB_RRC_UE_t *ue = &ue_context_p->ue_context; if (ue->ho_context && ue->ho_context->source) ue->ho_context->source->ho_status_transfer(rrc, ue, n_drb_mod, drb_ids, pdcp_status); } + + // Send F1 UE Context Modification Request with DRB release + if (n_drb_to_release) { + LOG_I(NR_RRC, "Send F1 UE Context Modification Request with DRB to release\n"); + rrc_gNB_send_f1_drb_release_request(rrc, ue, drb_to_release, n_drb_to_release); + } } /** @brief E1AP Bearer Context Modification Failure processing on CU-CP */ diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 3b30341371..570e3912fa 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1585,13 +1585,12 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_ return -1; } - LOG_I(NR_RRC, "[gNB %ld] gNB_ue_ngap_id %u \n", instance, gNB_ue_ngap_id); gNB_RRC_UE_t *UE = &ue_context_p->ue_context; - LOG_I( - NR_RRC, "PDU Session Release Command: AMF_UE_NGAP_ID %lu rrc_ue_id %u release_pdusessions %d \n", cmd->amf_ue_ngap_id, gNB_ue_ngap_id, cmd->nb_pdusessions_torelease); - bool found = false; - uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id); - UE->xids[xid] = RRC_PDUSESSION_RELEASE; + LOG_I(NR_RRC, "PDU Session Release: AMF_UE_NGAP_ID %lu rrc_ue_id %u release_pdusessions %d \n", + cmd->amf_ue_ngap_id, + gNB_ue_ngap_id, + cmd->nb_pdusessions_torelease); + e1ap_bearer_mod_req_t req = {0}; for (int pdusession = 0; pdusession < cmd->nb_pdusessions_torelease; pdusession++) { rrc_pdu_session_param_t *pduSession = find_pduSession(&UE->pduSessions, cmd->pdusession_release_params[pdusession].pdusession_id); if (!pduSession) { @@ -1599,30 +1598,35 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_ continue; } if (pduSession->status == PDU_SESSION_STATUS_FAILED) { - pduSession->xid = xid; continue; } if (pduSession->status == PDU_SESSION_STATUS_ESTABLISHED) { - found = true; - LOG_I(NR_RRC, "RELEASE pdusession %d \n", pduSession->param.pdusession_id); + LOG_I(NR_RRC, "NG Release PDU Session %d \n", pduSession->param.pdusession_id); + pdu_session_to_remove_t *release = &req.pduSessionRem[req.numPDUSessionsRem++]; + release->sessionId = pduSession->param.pdusession_id; + release->cause.type = E1AP_CAUSE_RADIO_NETWORK; + release->cause.value = E1AP_RADIO_CAUSE_NORMAL_RELEASE; pduSession->status = PDU_SESSION_STATUS_TORELEASE; - pduSession->xid = xid; } } - if (found) { - // TODO RRCReconfiguration To UE - LOG_I(NR_RRC, "Send RRCReconfiguration To UE \n"); - rrc_gNB_generate_dedicatedRRCReconfiguration_release(rrc, UE, xid, cmd->nas_pdu.len, cmd->nas_pdu.buf); + if (req.numPDUSessionsRem == 0) { + LOG_E(NR_RRC, "Received NG PDU Session Release Command but no PDU Sessions to release\n"); + return -1; + } else if (!ue_associated_to_cuup(rrc, UE)) { + LOG_E(NR_RRC, "UE %d is not associated to CU-UP\n", UE->rrc_ue_id); + // TODO handle, e.g., only trigger F1 release + return 0; } else { - // gtp tunnel delete - LOG_I(NR_RRC, "gtp tunnel delete all tunnels for UE %04x\n", UE->rnti); - gtpv1u_gnb_delete_tunnel_req_t req = {0}; - req.ue_id = UE->rnti; - gtpv1u_delete_ngu_tunnel(rrc->module_id, &req); - // NGAP_PDUSESSION_RELEASE_RESPONSE - rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid); - LOG_I(NR_RRC, "Send PDU Session Release Response \n"); + /* If present, store NAS-PDU in the UE context for later inclusion in RRCReconfiguration */ + if (cmd->nas_pdu.len > 0) { + UE->nas_pdu = copy_byte_array(cmd->nas_pdu); + } + LOG_I(NR_RRC, "Send F1 Bearer Context Modification Request with PDU Session release \n"); + req.gNB_cu_cp_ue_id = UE->rrc_ue_id; + req.gNB_cu_up_ue_id = UE->rrc_ue_id; + sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE); + rrc->cucp_cuup.bearer_context_mod(assoc_id, &req); } return 0; } diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 389213986e..c34c453e60 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -590,14 +590,13 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id) LOG_E(SDAP, "SDAP entities not established or Invalid range of pdusession_id [0, 256].\n"); return false; } - LOG_D(SDAP, "Deleting SDAP entity for UE %lx and PDU Session id %d\n", ue_id, entityPtr->pdusession_id); if (entityPtr->ue_id == ue_id && entityPtr->pdusession_id == pdusession_id) { sdap_info.sdap_entity_llist = sdap_info.sdap_entity_llist->next_entity; if (entityPtr->pdusession_sock != -1) remove_ip_if(entityPtr); free(entityPtr); - LOG_D(SDAP, "Successfully deleted Entity.\n"); + LOG_D(SDAP, "Successfully deleted SDAP entity for UE %lx and PDU Session id %d\n", ue_id, pdusession_id); return true; } else { while ((entityPtr->ue_id != ue_id || entityPtr->pdusession_id != pdusession_id) && entityPtr->next_entity != NULL @@ -613,7 +612,7 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id) remove_ip_if(entityPtr); } free(entityPtr); - LOG_D(SDAP, "Successfully deleted Entity.\n"); + LOG_D(SDAP, "Successfully deleted Entity for UE %lx and PDU Session id %d\n", ue_id, pdusession_id); return true; } } diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index ab14ee6241..9f22b05b09 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -905,7 +905,7 @@ int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id) } map::iterator rb_it = ue_it->second.bearers.find(rb_id); if (rb_it == ue_it->second.bearers.end()) { - LOG_E(GTPU, "%s() UE %ld has no bearer %d, available\n", __func__, ue_id, rb_id); + LOG_E(GTPU, "%s() UE %ld has no tunnel for bearer %d\n", __func__, ue_id, rb_id); pthread_mutex_unlock(&globGtp.gtp_lock); return !GTPNOK; } @@ -913,9 +913,9 @@ int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id) globGtp.te2ue_mapping.erase(teid); ue_it->second.bearers.erase(rb_id); pthread_mutex_unlock(&globGtp.gtp_lock); - LOG_I(GTPU, "Deleted tunnel TEID %d (RB %d) for ue id %ld, remaining bearers:\n", teid, rb_id, ue_id); + LOG_I(GTPU, "Deleted tunnel TEID 0x%x for bearer %d of UE %ld, remaining tunnels:\n", teid, rb_id, ue_id); for (auto b : ue_it->second.bearers) - LOG_I(GTPU, "bearer %ld\n", b.first); + LOG_I(GTPU, "Bearer %ld\n", b.first); return !GTPNOK; } From 7d5e20fd03ec7e21cf2d80e34413a1a05c66b981 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 3 Sep 2025 22:57:48 +0200 Subject: [PATCH 243/275] gtpu: cleanup unused newGtpuDeleteTunnels() The partial-tunnel deletion API was buggy and redundant: * e.g. It erased entries from globGtp.te2ue_mapping but never erases the corresponding entries from inst->ue2te_mapping[ue].bearers All current use cases are already covered by: * newGtpuDeleteOneTunnel() for targeted deletion * newGtpuDeleteAllTunnels() for full cleanup Call sites should explicitly loop over newGtpuDeleteOneTunnel() if multiple tunnels need removal. --- openair3/ocp-gtpu/gtp_itf.cpp | 33 --------------------------------- openair3/ocp-gtpu/gtp_itf.h | 4 ---- 2 files changed, 37 deletions(-) diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 9f22b05b09..ed15d48f80 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -982,45 +982,12 @@ int gtpv1u_delete_all_s1u_tunnel(const instance_t instance, const rnti_t rnti) return newGtpuDeleteAllTunnels(instance, rnti); } -int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int *pdusession_id) -{ - LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", instance, ue_id); - pthread_mutex_lock(&globGtp.gtp_lock); - getInstRetInt(compatInst(instance)); - getUeRetInt(inst, ue_id); - int nb = 0; - - for (int i = 0; i < nbTunnels; i++) { - auto ptr2 = ptrUe->second.bearers.find(pdusession_id[i]); - - if (ptr2 == ptrUe->second.bearers.end()) { - LOG_E(GTPU, "[%ld] GTP-U instance: delete of not existing tunnel UE ID:RAB: %ld/%x\n", instance, ue_id, pdusession_id[i]); - } else { - globGtp.te2ue_mapping.erase(ptr2->second.teid_incoming); - nb++; - } - } - - if (ptrUe->second.bearers.size() == 0) - // no tunnels on this ue id, erase the ue entry - inst->ue2te_mapping.erase(ptrUe); - - pthread_mutex_unlock(&globGtp.gtp_lock); - LOG_I(GTPU, "[%ld] Deleted all tunnels for ue id %lu (%d tunnels deleted)\n", instance, ue_id, nb); - return !GTPNOK; -} - int gtpv1u_delete_x2u_tunnel(const instance_t instanceP, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { LOG_E(GTPU, "x2 tunnel not implemented\n"); return 0; } -int gtpv1u_delete_ngu_tunnel(const instance_t instance, gtpv1u_gnb_delete_tunnel_req_t *req) -{ - return newGtpuDeleteTunnels(instance, req->ue_id, req->num_pdusession, req->pdusession_id); -} - static gtpv1u_bearer_t create_bearer(int socket, const struct sockaddr_in *addr, uint32_t teid, uint16_t seq) { gtpv1u_bearer_t bearer = {.sock_fd = socket, .teid_outgoing = teid, .seqNum = seq}; diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index c31f8c935e..9274cd52f2 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -100,9 +100,6 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; gtpCallback callBack, gtpCallbackSDAP callBackSDAP); - int gtpv1u_delete_ngu_tunnel( const instance_t instance, - gtpv1u_gnb_delete_tunnel_req_t *req); - int gtpv1u_update_ue_id(const instance_t instanceP, ue_id_t old_ue_id, ue_id_t new_ue_id); // New API @@ -124,7 +121,6 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id); int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id); - int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int *pdusession_id); void gtpv1uSendDirect(instance_t instance, ue_id_t ue_id, int bearer_id, uint8_t *buf, size_t len, bool seqNumFlag, bool npduNumFlag); void gtpv1uSendDirectWithNRUSeqNum(instance_t instance, From 3cfe2e265ae1617766306abb4528e5741aa043d0 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 17 Jun 2025 13:32:21 +0200 Subject: [PATCH 244/275] RRC PDU Session Release: DRBs and PDU session teardown in NGAP PDU Session Release Response callback We need to keep PDU sessions and DRBs in list until the NGAP PDU Session Release Response is sent. * Introduced find_drb() and remove_drbs_by_pdu_session() helpers to locate and clean up DRBs linked to a specific PDU session * Targeted per-session DRB removal in rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE() * Ensures DRBs for unaffected PDU sessions remain intact * Remove unused PDU_SESSION_STATUS_RELEASED status: keeping this status is no longer relevant since the PDU sessions are effectively removed from the RRC lists Closes #867 Also, refactor rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND 3GPP TS 38.413 8.2.2 PDU Session Resource Release says: > upon reception the NG-RAN node shall execute the release of the requested PDU sessions. > For each PDU session to be released the NG-RAN node shall release the corresponding > resources over Uu and over NG, if any. Therefore, any PDU session in the pdusession_release_params list that is also present in the RRC list can be set to release, regardless of its status. The function signature was also updated along with other minor changes. --- openair2/RRC/NR/nr_rrc_defs.h | 1 - openair2/RRC/NR/rrc_gNB.c | 3 +- openair2/RRC/NR/rrc_gNB_NGAP.c | 40 +++++++++--------- openair2/RRC/NR/rrc_gNB_NGAP.h | 2 +- openair2/RRC/NR/rrc_gNB_radio_bearers.c | 56 +++++++++++++++++++++++++ openair2/RRC/NR/rrc_gNB_radio_bearers.h | 4 ++ 6 files changed, 83 insertions(+), 23 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index e8820bcc76..e850ce373a 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -97,7 +97,6 @@ typedef enum pdu_session_satus_e { PDU_SESSION_STATUS_TOMODIFY, // ENDC NSA PDU_SESSION_STATUS_FAILED, PDU_SESSION_STATUS_TORELEASE, // to release DRB between eNB and UE - PDU_SESSION_STATUS_RELEASED } pdu_session_status_t; typedef struct pdusession_s { diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 1e08c9cf25..a6c24fc579 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2880,7 +2880,6 @@ static const char *get_pdusession_status_text(pdu_session_status_t status) case PDU_SESSION_STATUS_TOMODIFY: return "to-modify"; case PDU_SESSION_STATUS_FAILED: return "failed"; case PDU_SESSION_STATUS_TORELEASE: return "to-release"; - case PDU_SESSION_STATUS_RELEASED: return "released"; default: AssertFatal(false, "illegal PDU status code %d\n", status); return "illegal"; } return "illegal"; @@ -3014,7 +3013,7 @@ void *rrc_gnb_task(void *args_p) { break; case NGAP_PDUSESSION_RELEASE_COMMAND: - rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(msg_p, instance); + rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(&NGAP_PDUSESSION_RELEASE_COMMAND(msg_p), RC.nrrrc[instance]); break; case NGAP_DL_RAN_STATUS_TRANSFER: diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 570e3912fa..7bcecb2533 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1562,31 +1562,33 @@ void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE DevAssert(resp->nb_of_pdusessions_released < NGAP_MAX_PDU_SESSION); resp->pdusession_release[resp->nb_of_pdusessions_released++].pdusession_id = pdusession->pdusession_id; } - session->status = PDU_SESSION_STATUS_RELEASED; } } + for (int i = 0; i < resp->nb_of_pdusessions_released; ++i) { + rm_pduSession(&UE->pduSessions, &UE->drbs, resp->pdusession_release[i].pdusession_id); + } + LOG_I(NR_RRC, "NGAP PDUSESSION RELEASE RESPONSE: rrc_ue_id %u release_pdu_sessions %d\n", resp->gNB_ue_ngap_id, resp->nb_of_pdusessions_released); itti_send_msg_to_task (TASK_NGAP, rrc->module_id, msg_p); } -//------------------------------------------------------------------------------ -int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance) -//------------------------------------------------------------------------------ +/** @brief Process NG PDU Session Resource Release command (8.2.2 of 3GPP TS 38.413) + * upon reception the NG-RAN node shall execute the release of the requested PDU sessions. + * For each PDU session to be released the NG-RAN node shall release the corresponding + * resources over Uu and over NG, if any. */ +int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(ngap_pdusession_release_command_t *cmd, gNB_RRC_INST *rrc) { - uint32_t gNB_ue_ngap_id; - ngap_pdusession_release_command_t *cmd = &NGAP_PDUSESSION_RELEASE_COMMAND(msg_p); - gNB_ue_ngap_id = cmd->gNB_ue_ngap_id; - gNB_RRC_INST *rrc = RC.nrrrc[instance]; + uint32_t gNB_ue_ngap_id = cmd->gNB_ue_ngap_id; rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, gNB_ue_ngap_id); if (!ue_context_p) { - LOG_E(NR_RRC, "[gNB %ld] not found ue context gNB_ue_ngap_id %u \n", instance, gNB_ue_ngap_id); + LOG_E(NR_RRC, "[gNB %d] UE context not found for gNB_ue_ngap_id %u \n", rrc->module_id, gNB_ue_ngap_id); return -1; } gNB_RRC_UE_t *UE = &ue_context_p->ue_context; - LOG_I(NR_RRC, "PDU Session Release: AMF_UE_NGAP_ID %lu rrc_ue_id %u release_pdusessions %d \n", + LOG_I(NR_RRC, "NG PDU Session Release command: AMF_UE_NGAP_ID=%lu, rrc_ue_id=%u, nb_pdusessions_torelease=%d \n", cmd->amf_ue_ngap_id, gNB_ue_ngap_id, cmd->nb_pdusessions_torelease); @@ -1597,17 +1599,17 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_ LOG_E(NR_RRC, "Failed to release non-existing PDU Session %d\n", cmd->pdusession_release_params[pdusession].pdusession_id); continue; } - if (pduSession->status == PDU_SESSION_STATUS_FAILED) { + if (pduSession->status == PDU_SESSION_STATUS_TORELEASE) { + LOG_W(NR_RRC, "PDU Session %d already set to be released\n", pduSession->param.pdusession_id); continue; } - if (pduSession->status == PDU_SESSION_STATUS_ESTABLISHED) { - LOG_I(NR_RRC, "NG Release PDU Session %d \n", pduSession->param.pdusession_id); - pdu_session_to_remove_t *release = &req.pduSessionRem[req.numPDUSessionsRem++]; - release->sessionId = pduSession->param.pdusession_id; - release->cause.type = E1AP_CAUSE_RADIO_NETWORK; - release->cause.value = E1AP_RADIO_CAUSE_NORMAL_RELEASE; - pduSession->status = PDU_SESSION_STATUS_TORELEASE; - } + // Set PDU session to release, regardless of the status + LOG_I(NR_RRC, "Set PDU Session %d to release\n", pduSession->param.pdusession_id); + pdu_session_to_remove_t *release = &req.pduSessionRem[req.numPDUSessionsRem++]; + release->sessionId = pduSession->param.pdusession_id; + release->cause.type = E1AP_CAUSE_RADIO_NETWORK; + release->cause.value = E1AP_RADIO_CAUSE_NORMAL_RELEASE; + pduSession->status = PDU_SESSION_STATUS_TORELEASE; } if (req.numPDUSessionsRem == 0) { diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h index 91c7722c52..c3f692ff5f 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -78,7 +78,7 @@ void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, uint32_t void rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_UECapabilityInformation_t *const ue_cap_info); -int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance); +int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(ngap_pdusession_release_command_t *cmd, gNB_RRC_INST *rrc); void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid); diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index 5af84f0399..c636e9e639 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -141,6 +141,62 @@ rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusessio return added; } +static bool eq_drb_pdu_session_id(const void *vval, const void *vit) +{ + const int *id = (const int *)vval; + const drb_t *elem = (const drb_t *)vit; + return elem->pdusession_id == *id; +} + +/** @brief Finds the first DRB with the given PDU session ID. + * @return Pointer to matching drb_t or NULL if not found. */ +static drb_t *find_drb_by_pdusession_id(seq_arr_t *seq, int pdusession_id) +{ + DevAssert(seq); + DevAssert(pdusession_id > 0 && pdusession_id <= NGAP_MAX_PDU_SESSION); + elm_arr_t elm = find_if(seq, &pdusession_id, eq_drb_pdu_session_id); + if (elm.found) + return (drb_t *)elm.it; + return NULL; +} + +/** @brief Removes a DRB from the list + * @param drbs The DRB list + * @param drb Pointer to the DRB to remove */ +static void nr_rrc_rm_drb(seq_arr_t *drbs, drb_t *drb) +{ + DevAssert(drbs); + DevAssert(drb); + LOG_I(NR_RRC, "Removing DRB ID %d (PDU Session ID=%d)\n", drb->drb_id, drb->pdusession_id); + seq_arr_erase_deep(drbs, drb, free_drb); +} + +/** @brief Removes a PDU Session from the list by ID + * Also removes all associated DRBs for this PDU session. + * @return true if successfully removed, false if not found */ +bool rm_pduSession(seq_arr_t *sessions, seq_arr_t *drbs, int pdusession_id) +{ + DevAssert(sessions); + DevAssert(drbs); + + rrc_pdu_session_param_t *session = find_pduSession(sessions, pdusession_id); + + if (session) { + LOG_I(NR_RRC, "Removing PDU Session %d from RRC setup list\n", pdusession_id); + // Remove all associated DRBs first + drb_t *drb; + while ((drb = find_drb_by_pdusession_id(drbs, pdusession_id))) { + nr_rrc_rm_drb(drbs, drb); + } + // Then remove the PDU session + seq_arr_erase_deep(sessions, session, free_pdusession); + return true; + } + + LOG_W(NR_RRC, "pdusession_id=%d not found to remove\n", pdusession_id); + return false; +} + /** @brief Add drb_t item in the UE context list for @param pdusession_id */ drb_t *nr_rrc_add_drb(seq_arr_t *drb_ptr, int pdusession_id, nr_pdcp_configuration_t *pdcp) { diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h index f6821e314b..a1b09a5b02 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -39,6 +39,10 @@ rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusessio /// @brief get PDU session of UE ue through the DRB drb_id rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id); +/// @brief Remove PDU Session from RRC list +/// Also removes all associated DRBs for this PDU session. +bool rm_pduSession(seq_arr_t *sessions, seq_arr_t *drbs, int pdusession_id); + /// @brief set PDCP configuration in E1 Bearer Context Management message bearer_context_pdcp_config_t set_bearer_context_pdcp_config(const nr_pdcp_configuration_t pdcp, bool um_on_default_drb, From 653c0fd6d5c084b633af9584975c29d592cb939b Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 17 Jun 2025 13:26:35 +0200 Subject: [PATCH 245/275] Telnet: add ci command to trigger NGAP PDU Session Release This commit introduces a new telnet server command `pdu_session_release` to manually trigger the NGAP PDU Session Release Command message from the telnet ci shell. This is useful for integration testing of PDU session teardown behavior. Details: - Added `trigger_ngap_pdu_session_release()` to send NGAP_PDUSESSION_RELEASE_COMMAND to the RRC task, targeting the specified `gNB_ue_ngap_id`. - The command currently releases PDU session ID 10 (hardcoded). - Hooked into telnet shell with command: `pdu_session_release [gNB_ue_ngap_id]` Usage example: telnet > pdu_session_release 1 --- common/utils/telnetsrv/telnetsrv_ci.c | 82 +++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/common/utils/telnetsrv/telnetsrv_ci.c b/common/utils/telnetsrv/telnetsrv_ci.c index 291f1f37a2..8b00d529c5 100644 --- a/common/utils/telnetsrv/telnetsrv_ci.c +++ b/common/utils/telnetsrv/telnetsrv_ci.c @@ -39,6 +39,7 @@ #include "openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h" #include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" #include "openair2/RRC/NR/rrc_gNB_mobility.h" +#include "openair3/NGAP/ngap_gNB_ue_context.h" #define TELNETSERVERCODE #include "telnetsrv.h" @@ -284,6 +285,86 @@ static int get_current_bwp(char *buf, int debug, telnet_printfunc_t prnt) return 0; } +/** @brief Trigger NGAP PDU Session Release for one or more PDU sessions associated with a UE ID/ + * Syntax: trigger_pdu_session_release [ue_id=gNB_ue_ngap_id(int,opt)],pdusession_id(int)[,pdusession_id(int)...] + * - If the gNB_ue_ngap_id is omitted, it is fetched from the only UE present in the RRC layer + * - At least one valid PDU session ID must be provided + * @param[in] buf Comma-separated input string: [ue_id=gNB_ue_ngap_id(int,opt)],PDU1[,PDU2,...] + * @param[in] debug Not used. + * @param[in] prnt Callback for telnet output printing. + * @return 0 on success; negative value on error. */ +static int trigger_ngap_pdu_session_release(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (buf == NULL) { + ERROR_MSG_RET("Missing input. Usage: trigger_pdu_session_release [ue_id=gNB_ue_ngap_id(int,opt)],pdusession_id(int)[,pdusession_id(int)...]\n"); + } + + char *tokens[NGAP_MAX_PDU_SESSION + 1]; + int count = 0; + + for (char *tok = strtok(buf, ","); tok != NULL && count < (int)sizeofArray(tokens); tok = strtok(NULL, ",")) { + tokens[count++] = tok; + } + + if (count < 1) { + ERROR_MSG_RET("Invalid input. Usage: trigger_pdu_session_release [ue_id=gNB_ue_ngap_id(int,opt)],pdusession_id(int)[,pdusession_id(int)...]\n"); + } + + int gNB_ue_ngap_id = -1; + int pdu_start_index = 0; + + if (strncmp(tokens[0], "ue_id=", 6) == 0) { + gNB_ue_ngap_id = atoi(tokens[0] + 6); + pdu_start_index = 1; + } else { + // No UE ID: infer it + if (!RC.nrrrc) + ERROR_MSG_RET("No RRC present\n"); + rrc_gNB_ue_context_t *ue = get_single_rrc_ue(); + if (!ue) + ERROR_MSG_RET("No single UE in RRC present\n"); + gNB_ue_ngap_id = ue->ue_context.rrc_ue_id; + } + + if (pdu_start_index >= count) { + ERROR_MSG_RET("No pdusession_id(int) provided\n"); + } + + ngap_gNB_ue_context_t *ngap = ngap_get_ue_context(gNB_ue_ngap_id); + if (!ngap) { + ERROR_MSG_RET("No NGAP UE context for gNB_ue_ngap_id %d\n", gNB_ue_ngap_id); + } + + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PDUSESSION_RELEASE_COMMAND); + ngap_pdusession_release_command_t *msg = &NGAP_PDUSESSION_RELEASE_COMMAND(message_p); + memset(msg, 0, sizeof(*msg)); + + msg->amf_ue_ngap_id = ngap->amf_ue_ngap_id; + msg->gNB_ue_ngap_id = ngap->gNB_ue_ngap_id; + + int nb_sessions = 0; + for (int i = pdu_start_index; i < count; ++i) { + int sid = atoi(tokens[i]); + if (sid < 1 || sid > 255) { + ERROR_MSG_RET("Invalid pdusession_id(int): %s (must be between 1 and 255)\n", tokens[i]); + } + msg->pdusession_release_params[nb_sessions++].pdusession_id = sid; + } + + msg->nb_pdusessions_torelease = nb_sessions; + + if (prnt) { + prnt("Triggering NGAP PDU Session Release for gNB_ue_ngap_id=%d: releasing pdusession_id=%d", gNB_ue_ngap_id); + for (int i = 0; i < nb_sessions; ++i) { + prnt(" %d,", msg->pdusession_release_params[i].pdusession_id); + } + prnt("\n"); + } + + itti_send_msg_to_task(TASK_RRC_GNB, 0, message_p); + return 0; +} + static telnetshell_cmddef_t cicmds[] = { {"get_single_rnti", "", get_single_rnti}, {"force_reestab", "[rnti(hex,opt)]", trigger_reestab}, @@ -294,6 +375,7 @@ static telnetshell_cmddef_t cicmds[] = { {"fetch_du_by_ue_id", "[rrc_ue_id(int,opt)]", fetch_du_by_ue_id}, {"get_current_bwp", "[rnti(hex,opt)]", get_current_bwp}, {"trigger_n2_ho", "[neighbour_pci(uint32_t),ueId(uint32_t)]", rrc_gNB_trigger_n2_ho}, + {"pdu_session_release", "[gNB_ue_ngap_id(int,opt)]", trigger_ngap_pdu_session_release}, {"", "", NULL}, }; From 55c3cd2b3149cd36a841ec9d92e124fecf815589 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 13 Aug 2025 14:29:56 +0200 Subject: [PATCH 246/275] Telnet: add ciUE command to trigger establishment of a new PDU Session --- common/utils/telnetsrv/CMakeLists.txt | 2 +- common/utils/telnetsrv/telnetsrv_ciUE.c | 44 +++++++++++++++++++++++++ openair3/NAS/NR_UE/nr_nas_msg.c | 2 +- openair3/NAS/NR_UE/nr_nas_msg.h | 1 + 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/common/utils/telnetsrv/CMakeLists.txt b/common/utils/telnetsrv/CMakeLists.txt index a249596d97..40380c0418 100644 --- a/common/utils/telnetsrv/CMakeLists.txt +++ b/common/utils/telnetsrv/CMakeLists.txt @@ -62,7 +62,7 @@ add_dependencies(telnetsrv telnetsrv_ci) message(STATUS "Add CI specific telnet functions for nrUE in telnetsrv_ciUE.so") add_library(telnetsrv_ciUE MODULE telnetsrv_ciUE.c) -target_link_libraries(telnetsrv_ciUE PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs UTIL) +target_link_libraries(telnetsrv_ciUE PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs UTIL NR_L2_UE nr_nas) add_dependencies(telnetsrv telnetsrv_ciUE) message(STATUS "Add bearer specific telnet functions in libtelnetsrv_bearer.so") diff --git a/common/utils/telnetsrv/telnetsrv_ciUE.c b/common/utils/telnetsrv/telnetsrv_ciUE.c index 88320a6ce0..6f86f1678f 100644 --- a/common/utils/telnetsrv/telnetsrv_ciUE.c +++ b/common/utils/telnetsrv/telnetsrv_ciUE.c @@ -39,6 +39,7 @@ #include "openair2/RRC/NR_UE/rrc_proto.h" #include "openair1/PHY/phy_extern_nr_ue.h" #include "openair1/PHY/defs_nr_common.h" +#include "openair3/NAS/NR_UE/nr_nas_msg.h" #define TELNETSERVERCODE #include "telnetsrv.h" @@ -52,6 +53,14 @@ const char* NR_UE_L2_STATE_STR[] = { #undef UE_STATE }; +static int get_default_ue_id(void) +{ + NR_UE_RRC_INST_t *rrc = get_NR_UE_rrc_inst(0); + if (!rrc) + return -1; + return rrc->ue_id; +} + /** * Get the synchronization state of a UE. * @@ -132,6 +141,40 @@ static int get_dl_toa(char *buf, int debug, telnet_printfunc_t prnt) return 0; } +static int add_pdu_session(char *buf, int debug, telnet_printfunc_t prnt) +{ + int ue_id = -1; + int pdusession_id = -1; + + if (!buf) { + ERROR_MSG_RET("Missing argument: expected PDUSessionID[,UE_ID]\n"); + } + + // Try parsing values in the form: "PDUSessionID[,UE_ID]" + int n = sscanf(buf, "%d,%d", &pdusession_id, &ue_id); + if (n == 1) { + // Only PDUSessionID provided: use default UE ID + ue_id = get_default_ue_id(); + if (ue_id < 0) + ERROR_MSG_RET("No default UE context found\n"); + } else if (n != 2) { + ERROR_MSG_RET("Invalid format: expected PDUSessionID[,UE_ID]\n"); + } + + if (pdusession_id < 0 || pdusession_id > 255) + ERROR_MSG_RET("PDUSessionID must be in range [0,255]\n"); + if (ue_id < 0) + ERROR_MSG_RET("UE_ID must be >= 0\n"); + + nr_ue_nas_t *nas = get_ue_nas_info(ue_id); + if (!nas) + ERROR_MSG_RET("No NAS context found for UE_ID %d\n", ue_id); + + request_pdusession(nas, pdusession_id); + prnt("Triggered PDU session request for UE %d with ID %d\n", ue_id, pdusession_id); + return 0; +} + /* Telnet shell command definitions */ static telnetshell_cmddef_t cicmds[] = { {"sync_state", "[UE_ID(int,opt)]", get_sync_state}, @@ -140,6 +183,7 @@ static telnetshell_cmddef_t cicmds[] = { {"force_crnti_ra", "", force_crnti_ra}, {"deregistration", "", force_deregistration}, {"get_max_dl_toa", "[ant]", get_dl_toa}, + {"add_pdu_session", "[PDUSessionID(int)],[UE_ID(int,opt)]", add_pdu_session}, {"", "", NULL}, }; diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index d0230e0a03..5a58af6b84 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -1772,7 +1772,7 @@ static void send_nas_5gmm_ind(instance_t instance, const Guti5GSMobileIdentity_t itti_send_msg_to_task(TASK_RRC_NRUE, instance, msg); } -static void request_pdusession(nr_ue_nas_t *nas, int pdusession_id) +void request_pdusession(nr_ue_nas_t *nas, int pdusession_id) { MessageDef *message_p = itti_alloc_new_message(TASK_NAS_NRUE, nas->UE_id, NAS_PDU_SESSION_REQ); NAS_PDU_SESSION_REQ(message_p).pdusession_id = pdusession_id; diff --git a/openair3/NAS/NR_UE/nr_nas_msg.h b/openair3/NAS/NR_UE/nr_nas_msg.h index 2d62c799ea..7a19997444 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.h +++ b/openair3/NAS/NR_UE/nr_nas_msg.h @@ -113,5 +113,6 @@ void *nas_nrue_task(void *args_p); void *nas_nrue(void *args_p); void nas_init_nrue(int num_ues); void nr_ue_create_ip_if(const char *ifnameprefix, const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id); +void request_pdusession(nr_ue_nas_t *nas, int pdusession_id); #endif /* __NR_NAS_MSG_SIM_H__*/ From ed6a25be75cac37f259d35addd35279fbeedda83 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 30 Sep 2025 16:09:01 +0200 Subject: [PATCH 247/275] Add PDU session add/remove test cases to 25 PRB RFSIM pipeline - Enable telnet server on VNF (gNB) and UE in docker-compose.yaml - Add new test cases for PDU session management: * Add PDU session via UE telnet * Release PDU session via telnet * Verify connectivity after PDU session release - Integrate telnet commands into existing container_5g_rfsim_u0_25prb.xml pipeline - Test cases added before undeploy step in proper execution order --- .../xml_files/container_5g_rfsim_u0_25prb.xml | 26 +++++++++++++++++++ .../docker-compose.yaml | 10 ++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml index d563b3aab4..83bffa90ac 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml @@ -36,6 +36,11 @@ 020003 030001 030002 + 050001 + 050002 + 050003 + 020001 + 020002 100001 222222 @@ -139,6 +144,27 @@ 90 + + Custom_Command + Trigger PDU Session Release via gNB telnet + acamas + echo ci pdu_session_release 2 | ncat 192.168.71.140 9090 + + + + Custom_Command + Add PDU Session via UE telnet + acamas + echo ci add_pdu_session 6,7 | ncat 192.168.71.150 8091 + + + + Custom_Command + Trigger PDU Session Release via gNB telnet + acamas + echo ci pdu_session_release 6 | ncat 192.168.71.140 9090 + + Undeploy_Object true 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 722290aa38..c4a4e2ebea 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 @@ -97,8 +97,9 @@ services: - ALL environment: USE_ADDITIONAL_OPTIONS: --nfapi VNF --log_config.global_log_options level,nocolor,time + --telnetsrv --telnetsrv.listenaddr 192.168.71.140 --telnetsrv.shrmod ci NFAPI_TRACE_LEVEL: info - ASAN_OPTIONS: detect_leaks=0 + ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 depends_on: - oai-ext-dn networks: @@ -122,7 +123,7 @@ services: environment: USE_ADDITIONAL_OPTIONS: --nfapi PNF --rfsim --log_config.global_log_options level,nocolor,time NFAPI_TRACE_LEVEL: info - ASAN_OPTIONS: detect_leaks=0 + ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 depends_on: - oai-vnf networks: @@ -147,7 +148,10 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: --rfsim -r 25 --numerology 0 --uicc0.imsi 208990100001100 --band 66 -C 2152680000 --CO -400000000 --ssb 48 --rfsimulator.serveraddr 192.168.71.141 --extra-pdu-id 2 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: --rfsim -r 25 --numerology 0 --uicc0.imsi 208990100001100 --band 66 -C 2152680000 --CO -400000000 --ssb 48 + --rfsimulator.serveraddr 192.168.71.141 --extra-pdu-id 2 --log_config.global_log_options level,nocolor,time + --telnetsrv --telnetsrv.shrmod ciUE --telnetsrv.listenaddr 192.168.71.150 --telnetsrv.listenport 8091 + ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 depends_on: - oai-pnf networks: From c3ef64d292e5febb3734a5f19df7eea09a13f031 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 10 Sep 2025 10:24:17 +0200 Subject: [PATCH 248/275] Add PDU Session Release documentation --- doc/E1AP/e1ap_procedures.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/doc/E1AP/e1ap_procedures.md b/doc/E1AP/e1ap_procedures.md index c46c710333..8857eee26b 100644 --- a/doc/E1AP/e1ap_procedures.md +++ b/doc/E1AP/e1ap_procedures.md @@ -72,3 +72,36 @@ sequenceDiagram u->>c: BEARER CONTEXT MODIFICATION RESPONSE Note over c: e1apCUCP_handle_BEARER_CONTEXT_MODIFICATION_RESPONSE ``` + +## PDU Session Release + +```mermaid +sequenceDiagram + participant AMF + participant CUCP + participant CUUP + participant DU + participant UE + + AMF->>CUCP: NG PDU SESSION RESOURCE RELEASE COMMAND + Note over CUCP: ngap_gNB_handle_pdusession_release_command + CUCP->>CUCP: rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND + Note over CUCP: set status PDU_SESSION_STATUS_TORELEASE + CUCP->>CUUP: E1 Bearer Context Modification Request + Note over CUUP: release_gtpu_tunnel (GTP tunnel, PDCP, SDAP) + CUUP->>CUCP: E1 Bearer Context Modification Response + Note over CUCP: rrc_gNB_send_f1_drb_release_request + CUCP->>DU: F1 UE Context Modification Request + Note over DU: handle_ue_context_drbs_release (release in MAC/RLC) + DU->>CUCP: F1 UE Context Modification Response + Note over CUCP: rrc_CU_process_ue_context_modification_response + Note over CUCP: replace existing CellGroupConfig + Note over CUCP: rrc_gNB_generate_dedicatedRRCReconfiguration + CUCP->>UE: RRCReconfiguration (DRB release list, NAS PDU) + UE->>CUCP: RRCReconfigurationComplete + Note over CUCP: handle_rrcReconfigurationComplete + Note over CUCP: rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE + CUCP->>AMF: NG PDU SESSION RESOURCE RELEASE RESPONSE + Note over CUCP: rm_drbs_by_pdusession (from stored RRC list) + Note over CUCP: rm_pduSession (from stored RRC list) +``` From 1fea6108a8c546f9e33add264e1f078a669f49ee Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 15 Oct 2025 12:27:34 +0200 Subject: [PATCH 249/275] RRC: unify NG delay control before calling the RRC NGAP handler - Add delayed_action_state_t in the UE context for ongoing delayed action - Move delay check and timer re-enqueue to rrc thread in rrc_gNB.c (rrc_delay_transaction) - Call delay before NGAP setup/release handlers; remove local delay logic from rrc_gNB_NGAP.c - Clear on RRCReconfigurationComplete (reset_delayed_action) Closes #960 --- openair2/RRC/NR/nr_rrc_defs.h | 9 ++- openair2/RRC/NR/nr_rrc_proto.h | 1 + openair2/RRC/NR/rrc_gNB.c | 102 +++++++++++++++++++++++++-- openair2/RRC/NR/rrc_gNB_NGAP.c | 46 +----------- openair2/RRC/NR/rrc_gNB_UE_context.c | 2 - 5 files changed, 108 insertions(+), 52 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index e850ce373a..4d24a771c0 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -148,6 +148,12 @@ typedef enum { RRC_UECAPABILITY_ENQUIRY, } rrc_action_t; +/* Small state for delaying NG-triggered actions (setup/release) */ +typedef struct { + int max_delays; + bool ongoing_transaction; +} delayed_action_state_t; + typedef struct nr_redcap_ue_cap { bool support_of_redcap_r17; bool support_of_16drb_redcap_r17; @@ -241,8 +247,7 @@ typedef struct gNB_RRC_UE_s { byte_array_t nas_pdu; /* hack, see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() for more info */ - int max_delays_pdu_session; - bool ongoing_pdusession_setup_request; + delayed_action_state_t delayed_action; nr_redcap_ue_cap_t *redcap_cap; } gNB_RRC_UE_t; diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 2fc7bf5e5e..a36a0778c6 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -122,5 +122,6 @@ NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish); NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do_integrity, bool do_ciphering); void activate_srb(gNB_RRC_UE_t *UE, int srb_id); void e1_notify_pdcp_status(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const ngap_drb_status_t *drb_status); +void init_delayed_action(delayed_action_state_t *delayed_action); #endif diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index a6c24fc579..b18e820e91 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -115,6 +115,97 @@ mui_t rrc_gNB_mui = 0; +/* Per-transaction max_delays counter to limit retry attempts */ +#define MAX_DELAYS 100 + +/** @brief clone and re-enqueue an NGAP message after delaying + * delays the ongoing transaction (in msg_p) by setting a timer to wait + * 10ms; upon expiry, delivers to RRC, which sends the message to itself */ +static void delay_transaction(MessageDef *msg_p, int wait_us) +{ + MessagesIds id = ITTI_MSG_ID(msg_p); + AssertFatal(id == NGAP_PDUSESSION_SETUP_REQ || id == NGAP_PDUSESSION_RELEASE_COMMAND, + "delay_transaction(): unsupported message id %d\n", + id); + + MessageDef *new = itti_alloc_new_message(TASK_RRC_GNB, 0, id); + + // Copy only the specific message struct, not the entire union. + // The union (msg_t) contains all message types and is much larger than + // the allocated space (which is sized for the specific message type only). + if (id == NGAP_PDUSESSION_SETUP_REQ) { + NGAP_PDUSESSION_SETUP_REQ(new) = NGAP_PDUSESSION_SETUP_REQ(msg_p); + } else if (id == NGAP_PDUSESSION_RELEASE_COMMAND) { + NGAP_PDUSESSION_RELEASE_COMMAND(new) = NGAP_PDUSESSION_RELEASE_COMMAND(msg_p); + } + + int instance = msg_p->ittiMsgHeader.originInstance; + long timer_id; + timer_setup(0, wait_us, TASK_RRC_GNB, instance, TIMER_ONE_SHOT, new, &timer_id); +} + +static void reset_delayed_action(delayed_action_state_t *delayed_action) +{ + delayed_action->ongoing_transaction = false; + delayed_action->max_delays = 0; +} + +void init_delayed_action(delayed_action_state_t *delayed_action) +{ + delayed_action->ongoing_transaction = true; + delayed_action->max_delays = MAX_DELAYS; +} + +/* \brief checks if any transaction is ongoing for any xid of this UE */ +static bool transaction_ongoing(const gNB_RRC_UE_t *UE) +{ + for (int xid = 0; xid < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++xid) { + if (UE->xids[xid] != RRC_ACTION_NONE) + return true; + } + return false; +} + +/** @brief delay control: returns true if delayed, false if should proceed + * This is a hack. We observed that with some UEs, PDU session requests might + * come in quick succession, faster than the RRC reconfiguration for the PDU + * session requests can be carried out (UE is doing reconfig, and second PDU + * session request arrives). We don't have currently the means to "queue up" + * these transactions, which would probably involve some rework of the RRC. + * To still allow these requests to come in and succeed, we below check and delay transactions + * for 10ms. However, to not accidentally end up in infinite loops, the + * maximum number is capped on a per-UE basis as indicated in variable + * max_delays_pdu_session. See commit 277f8da0 for more details. */ +static bool rrc_delay_transaction(instance_t instance, MessageDef *msg_p) +{ + uint32_t cu_ue_id = 0; + if (ITTI_MSG_ID(msg_p) == NGAP_PDUSESSION_SETUP_REQ) { + cu_ue_id = NGAP_PDUSESSION_SETUP_REQ(msg_p).gNB_ue_ngap_id; + } else if (ITTI_MSG_ID(msg_p) == NGAP_PDUSESSION_RELEASE_COMMAND) { + cu_ue_id = NGAP_PDUSESSION_RELEASE_COMMAND(msg_p).gNB_ue_ngap_id; + } + AssertFatal(cu_ue_id > 0, "cu_ue_id not found in message %s\n", ITTI_MSG_NAME(msg_p)); + + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], cu_ue_id); + DevAssert(ue_context_p); + + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + bool delay = UE->delayed_action.ongoing_transaction && UE->delayed_action.max_delays > 0; + /* Check if any PDU session action is ongoing */ + if (delay || transaction_ongoing(UE)) { + int wait_us = 10000; + LOG_I(NR_RRC, + "UE %d: ongoing transaction, delaying incoming transaction by %d us\n", + UE->rrc_ue_id, + wait_us); + delay_transaction(msg_p, wait_us); + UE->delayed_action.max_delays--; + return true; /* delayed */ + } + LOG_D(NR_RRC, "UE %d: no delayed action ongoing, proceeding with incoming transaction\n", UE->rrc_ue_id); + return false; /* not delayed */ +} + typedef struct deliver_ue_ctxt_release_data_t { gNB_RRC_INST *rrc; f1ap_ue_context_rel_cmd_t *release_cmd; @@ -1856,6 +1947,7 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U switch (UE->xids[xid]) { case RRC_PDUSESSION_RELEASE: { rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid); + reset_delayed_action(&UE->delayed_action); } break; case RRC_PDUSESSION_ESTABLISH: if (UE->n_initial_pdu > 0) { @@ -1870,6 +1962,7 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U LOG_W(NR_RRC, "UE %d: RRC Reconfiguration Complete for PDU session establishment, but no PDU sessions were setup\n", UE->rrc_ue_id); + reset_delayed_action(&UE->delayed_action); break; case RRC_PDUSESSION_MODIFY: rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(rrc, UE, xid); @@ -1886,9 +1979,6 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U break; } - if (UE->xids[xid] == RRC_PDUSESSION_ESTABLISH) - UE->ongoing_pdusession_setup_request = false; - UE->xids[xid] = RRC_ACTION_NONE; for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) { if (UE->xids[i] != RRC_ACTION_NONE) { @@ -3005,7 +3095,8 @@ void *rrc_gnb_task(void *args_p) { break; case NGAP_PDUSESSION_SETUP_REQ: - rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(msg_p, instance); + if (!rrc_delay_transaction(instance, msg_p)) + rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(msg_p, instance); break; case NGAP_PDUSESSION_MODIFY_REQ: @@ -3013,7 +3104,8 @@ void *rrc_gnb_task(void *args_p) { break; case NGAP_PDUSESSION_RELEASE_COMMAND: - rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(&NGAP_PDUSESSION_RELEASE_COMMAND(msg_p), RC.nrrrc[instance]); + if (!rrc_delay_transaction(instance, msg_p)) + rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(&NGAP_PDUSESSION_RELEASE_COMMAND(msg_p), RC.nrrrc[instance]); break; case NGAP_DL_RAN_STATUS_TRANSFER: diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 7bcecb2533..8ba7b3e950 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -789,29 +789,6 @@ void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE return; } -/* \brief checks if any transaction is ongoing for any xid of this UE */ -static bool transaction_ongoing(const gNB_RRC_UE_t *UE) -{ - for (int xid = 0; xid < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++xid) { - if (UE->xids[xid] != RRC_ACTION_NONE) - return true; - } - return false; -} - -/* \brief delays the ongoing transaction (in msg_p) by setting a timer to wait - * 10ms; upon expiry, delivers to RRC, which sends the message to itself */ -static void delay_transaction(MessageDef *msg_p, int wait_us) -{ - MessageDef *new = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_SETUP_REQ); - ngap_pdusession_setup_req_t *n = &NGAP_PDUSESSION_SETUP_REQ(new); - *n = NGAP_PDUSESSION_SETUP_REQ(msg_p); - - int instance = msg_p->ittiMsgHeader.originInstance; - long timer_id; - timer_setup(0, wait_us, TASK_RRC_GNB, instance, TIMER_ONE_SHOT, new, &timer_id); -} - /** * @brief Fill PDU Session Resource Setup Response with a list of PDU Session Resources Failed to Setup * and send ITTI message to TASK_NGAP @@ -883,25 +860,6 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins UE->amf_ue_ngap_id = msg->amf_ue_ngap_id; - /* This is a hack. We observed that with some UEs, PDU session requests might - * come in quick succession, faster than the RRC reconfiguration for the PDU - * session requests can be carried out (UE is doing reconfig, and second PDU - * session request arrives). We don't have currently the means to "queue up" - * these transactions, which would probably involve some rework of the RRC. - * To still allow these requests to come in and succeed, we below check and delay transactions - * for 10ms. However, to not accidentally end up in infinite loops, the - * maximum number is capped on a per-UE basis as indicated in variable - * max_delays_pdu_session. */ - if (!UE->ongoing_pdusession_setup_request) - UE->max_delays_pdu_session = 100; - - if (UE->max_delays_pdu_session > 0 && (transaction_ongoing(UE) || UE->ongoing_pdusession_setup_request)) { - int wait_us = 10000; - LOG_I(RRC, "UE %d: delay PDU session setup by %d us, pending %d retries\n", UE->rrc_ue_id, wait_us, UE->max_delays_pdu_session); - delay_transaction(msg_p, wait_us); - UE->max_delays_pdu_session--; - return; - } pdusession_t to_setup[NGAP_MAX_PDU_SESSION] = {0}; for (int i = 0; i < msg->nb_pdusessions_tosetup; ++i) @@ -916,7 +874,8 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins send_ngap_pdu_session_setup_resp_fail(instance, msg, cause); rrc_forward_ue_nas_message(rrc, UE); } else { - UE->ongoing_pdusession_setup_request = true; + // Set ongoing_transaction flag to true + init_delayed_action(&UE->delayed_action); } } @@ -1630,6 +1589,7 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(ngap_pdusession_release_comm sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE); rrc->cucp_cuup.bearer_context_mod(assoc_id, &req); } + init_delayed_action(&UE->delayed_action); return 0; } diff --git a/openair2/RRC/NR/rrc_gNB_UE_context.c b/openair2/RRC/NR/rrc_gNB_UE_context.c index 9a2c3fb6f6..86838927ab 100644 --- a/openair2/RRC/NR/rrc_gNB_UE_context.c +++ b/openair2/RRC/NR/rrc_gNB_UE_context.c @@ -218,8 +218,6 @@ rrc_gNB_ue_context_t *rrc_gNB_create_ue_context(sctp_assoc_t assoc_id, ue->rrc_ue_id); bool success = cu_add_f1_ue_data(ue->rrc_ue_id, &ue_data); DevAssert(success); - ue->max_delays_pdu_session = 20; /* see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() */ - ue->ongoing_pdusession_setup_request = false; // Initialise setup PDU Sessions list seq_arr_init(&ue->pduSessions, sizeof(rrc_pdu_session_param_t)); From f9a8898e209975f16947188a0211fdf274714ca5 Mon Sep 17 00:00:00 2001 From: Raghavendra Dinavahi Date: Mon, 22 Sep 2025 15:38:00 +0200 Subject: [PATCH 250/275] UE: Epoch time handling of Target cell In case of Handover, NTNconfig sent in RRCreconfiguration triggering handover contains epoch time wrt timing of Target cell. NTNconfig in RRC needs to be processed only after timing is acquired on the target cell In case of Target cell, epoch time is the sfn nearest to the frame timing of the target cell. --- openair2/RRC/NR_UE/rrc_UE.c | 62 +++++++++++++++++++++++++++++------ openair2/RRC/NR_UE/rrc_defs.h | 2 ++ 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 24088749a5..27e1dc0bb1 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -210,7 +210,7 @@ static void get_epochtime_from_sib19scheduling(NR_UE_RRC_SI_INFO *SI_info, int * slot); } -static int eval_epoch_time(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int frame) +static int eval_epoch_time(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int frame, bool is_targetcell) { int epoch_frame = 0, epoch_subframe = 0; int diff_frames = 0; @@ -229,11 +229,19 @@ static int eval_epoch_time(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntnc ntncfg->epochTime_r17->sfn_r17 = epoch_frame; ntncfg->epochTime_r17->subFrameNR_r17 = epoch_subframe; } - // For serving cell, the field sfn indicates the current SFN or the next upcoming SFN - // after the frame where the message indicating the epochTime is received - // i.e. Epochframe can be present or future SFN - diff_frames = (epoch_frame - frame + 1024) % 1024; // According to 38.331 Epochtime is defined for serving cell like this - LOG_I(NR_RRC, "Epoch frame %d, ahead by %d frames\n", epoch_frame, diff_frames); + if (is_targetcell) { + // For Target cell,the SFN nearest to the frame needs to be considered + // i.e. Epochframe can be in the past or future + int diff1 = (frame - epoch_frame + 1024) % 1024; + int diff2 = (epoch_frame - frame + 1024) % 1024; + diff_frames = (diff1 < diff2) ? -diff1 : diff2; + } else { + // For serving cell, the field sfn indicates the current SFN or the next upcoming SFN + // after the frame where the message indicating the epochTime is received + // i.e. Epochframe can be present or future SFN + diff_frames = (epoch_frame - frame + 1024) % 1024; // According to 38.331 Epochtime is defined for serving cell like this + } + LOG_I(NR_RRC, "Epoch frame %d ahead by %d frames\n", epoch_frame, diff_frames); return diff_frames; } @@ -257,11 +265,11 @@ static int get_ntn_timervalues(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t * return sib19_timer_ms; } -static void nr_rrc_process_ntnconfig(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int frame) +static void nr_rrc_process_ntnconfig(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int frame, bool is_targetcell) { SI_info->SInfo_r17.sib19_validity = true; // Check if Epochtime is sent or not - int diff_frames = eval_epoch_time(SI_info, ntncfg, frame); + int diff_frames = eval_epoch_time(SI_info, ntncfg, frame, is_targetcell); if (ntncfg->ntn_UlSyncValidityDuration_r17) { // ulsyncvalidity duration configured int val430_ms = 0, sib19_timer_ms = 0; @@ -361,7 +369,7 @@ static void nr_decode_SI(NR_UE_RRC_SI_INFO *SI_info, NR_SystemInformation_t *si, sib19 = typeandinfo->choice.sib19_v1700; if (g_log->log_component[NR_RRC].level >= OAILOG_DEBUG) xer_fprint(stdout, &asn_DEF_NR_SIB19_r17, (const void *)sib19); - nr_rrc_process_ntnconfig(rrc, SI_info, sib19->ntn_Config_r17, frame); + nr_rrc_process_ntnconfig(rrc, SI_info, sib19->ntn_Config_r17, frame, false); break; default: break; @@ -1454,6 +1462,11 @@ static void nr_rrc_ue_decode_NR_BCCH_BCH_Message(NR_UE_RRC_INST_t *rrc, uint8_t *const bufferP, const uint8_t buffer_len) { + // MIB received on the target cell. Now process target ntncfg once new timing is received. + if (phycellid == rrc->phyCellID && rrc->target_ntncfg) { + rrc->process_target_ntncfg = true; + } + NR_BCCH_BCH_Message_t *bcch_message = NULL; rrc->phyCellID = phycellid; rrc->arfcn_ssb = ssb_arfcn; @@ -1754,13 +1767,20 @@ static void nr_rrc_process_reconfigurationWithSync(NR_UE_RRC_INST_t *rrc, // 3GPP TS38.331 section 5.3.5.5.2 nr_timer_stop(&tac->T430); + + if (rrc->target_ntncfg) { + ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg); + rrc->target_ntncfg = NULL; + rrc->process_target_ntncfg = false; + } if (reconfigurationWithSync->spCellConfigCommon && reconfigurationWithSync->spCellConfigCommon->ext2 && reconfigurationWithSync->spCellConfigCommon->ext2->ntn_Config_r17) { NR_NTN_Config_r17_t *ntncfg = reconfigurationWithSync->spCellConfigCommon->ext2->ntn_Config_r17; // EPOCH time is always sent if NTN config is sent through DCCH AssertFatal(ntncfg->epochTime_r17, "NTN-CONFIG sent in dedicated mode should have EPOCHTIME\n"); - nr_rrc_process_ntnconfig(rrc, &rrc->perNB[gNB_index].SInfo, ntncfg, rrc->current_frame); + const int copy_result = asn_copy(&asn_DEF_NR_NTN_Config_r17, (void **)&rrc->target_ntncfg, ntncfg); + AssertFatal(copy_result == 0, "unable to copy NR_NTN_Config_r17_t\n"); } } @@ -2626,6 +2646,13 @@ void *rrc_nrue(void *notUsed) nr_rrc_handle_timers(rrc); NR_UE_RRC_SI_INFO *SInfo = &rrc->perNB[NRRRC_FRAME_PROCESS(msg_p).gnb_id].SInfo; nr_rrc_SI_timers(SInfo); + if (rrc->process_target_ntncfg) { + // Process target NTNCFG as the target cells timing is acquired + nr_rrc_process_ntnconfig(rrc, &rrc->perNB[NRRRC_FRAME_PROCESS(msg_p).gnb_id].SInfo, rrc->target_ntncfg, rrc->current_frame, true); + ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg); + rrc->target_ntncfg = NULL; + rrc->process_target_ntncfg = false; + } break; case NR_RRC_MAC_INAC_IND: @@ -2806,6 +2833,14 @@ static void nr_rrc_initiate_rrcReestablishment(NR_UE_RRC_INST_t *rrc, NR_Reestab set_DRB_status(rrc, i, RB_SUSPENDED); } } + + // Free Target NTNcfg is stored + if (rrc->target_ntncfg) { + ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg); + rrc->target_ntncfg = NULL; + rrc->process_target_ntncfg = false; + } + // release the MCG SCell(s), if configured // no SCell configured in our implementation @@ -2996,6 +3031,13 @@ void nr_rrc_going_to_IDLE(NR_UE_RRC_INST_t *rrc, asn1cFreeStruc(asn_DEF_NR_UE_TimersAndConstants, tac->sib1_TimersAndConstants); } + // Free Target NTNcfg is stored + if (rrc->target_ntncfg) { + ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg); + rrc->target_ntncfg = NULL; + rrc->process_target_ntncfg = false; + } + // reset MAC NR_UE_MAC_reset_cause_t cause = (rrc->nrRrcState == RRC_STATE_DETACH_NR) ? DETACH : GO_TO_IDLE; nr_mac_rrc_message_t rrc_msg = {0}; diff --git a/openair2/RRC/NR_UE/rrc_defs.h b/openair2/RRC/NR_UE/rrc_defs.h index ae8837bff2..0672af7741 100644 --- a/openair2/RRC/NR_UE/rrc_defs.h +++ b/openair2/RRC/NR_UE/rrc_defs.h @@ -268,6 +268,8 @@ typedef struct NR_UE_RRC_INST_s { NR_SL_PreconfigurationNR_r16_t *sl_preconfig; // NTN params bool is_NTN_UE; + NR_NTN_Config_r17_t *target_ntncfg; + bool process_target_ntncfg; notifiedFIFO_t *mac_input_nf; } NR_UE_RRC_INST_t; From 6512a7a9ca71e71dd438f8d33b4d056e623882e6 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 18 Nov 2025 18:17:56 +0100 Subject: [PATCH 251/275] Notify security keys change to CUUP during re-establishment During re-establishment, new KgNB* is derived, requiring new UP keys (KUPint, KUPenc) to be sent to CU-UP. When do_drb_integrity = true, CU-UP needs the updated integrity keys to verify DRB PDUs. Without this update, CU-UP uses old keys while the UE uses new keys, causing integrity failures. The fix ensures security info is always sent during re-establishment, which is critical for DRB integrity. --- openair2/RRC/NR/rrc_gNB.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index b18e820e91..d067428a9b 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1063,15 +1063,10 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) } } -#if 0 - /* According to current understanding of E1 specifications, it is not needed - * to send security information because this does not change. - * But let's keep the code here in case it's needed. - */ - // Always send security information + /* During reestablishment, for DRB integrity protection security keys change (KgNB* is derived), + * so security information MUST be sent to CU-UP to update DRB security keys. */ req.secInfo = malloc_or_fail(sizeof(*req.secInfo)); fill_security_info(rrc, ue_p, req.secInfo); -#endif /* Send E1 Bearer Context Modification Request (3GPP TS 38.463) */ sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, ue_p); From e1dd62df5371bc60fde814cf36510fef135f8d89 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 18 Nov 2025 23:13:50 +0100 Subject: [PATCH 252/275] Fix N2 handover integrity failures by enabling ciphering during handover setup Problem: During N2 handover with integrity protection (NIA1/NIA2), the target gNB was disabling ciphering when configuring SRB1 security, while the UE continued using the old security context from the source gNB until it received and processed the RRC Reconfiguration with masterKeyUpdate. The first PDUs sent after CFRA were still ciphered with the old keys, but the target gNB had ciphering disabled, causing it to skip deciphering and attempt integrity verification on ciphered data, leading to MAC-I mismatches and integrity failures. Specification Reference: According to TS 33.501 section 6.11, "the UE shall keep the K gNB used in the source cell until the handover or a connection re-establishment has been completed successfully". This means the UE continues using the old security context (old KRRCint, KRRCenc) until it receives and processes the RRC Reconfiguration with masterKeyUpdate. The target gNB must therefore enable ciphering during handover setup to correctly decipher and verify integrity of PDUs sent with the old security context before the UE switches to the new keys. Root Cause: The code was calling nr_rrc_pdcp_config_security(UE, false) during handover setup, which disabled ciphering. This was incorrect because: 1. The UE already has security active from the source gNB 2. The UE continues using old ciphered keys until masterKeyUpdate 3. The UE sends ciphered PDUs immediately after CFRA succeeds 4. The target gNB must be able to decipher these PDUs to verify integrity Why it only manifested when drb_integrity = "yes": The bug was always present, but only caused visible failures when integrity verification was enabled. When drb_integrity = "no", the config typically also sets integrity_algorithms to only NIA0, resulting in UE->integrity_algorithm = 0, which causes has_integrity = 0 for SRB1, skipping integrity verification and hiding the underlying ciphering bug. When drb_integrity = "yes", integrity_algorithms typically includes NIA1/NIA2, resulting in has_integrity = 1 for SRB1, causing integrity checks to run on ciphered data and fail. Fix: Change nr_rrc_pdcp_config_security(UE, false) to nr_rrc_pdcp_config_security(UE, true) during handover setup to enable ciphering immediately, allowing the target gNB to correctly decipher and verify integrity of PDUs sent with the old security context. Note: This fix is specific to handover. During initial context setup, ciphering should remain disabled until SecurityModeComplete is received, as the UE hasn't activated ciphering yet at that point. --- openair2/RRC/NR/rrc_gNB_NGAP.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 8ba7b3e950..979fe0ac5e 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1192,7 +1192,11 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga // Activate SRBs activate_srb(UE, SRB1); activate_srb(UE, SRB2); - nr_rrc_pdcp_config_security(UE, false); + // During N2 handover, the UE continues using the old security context from the source gNB + // until it receives and processes the RRC Reconfiguration with masterKeyUpdate. The first + // PDUs sent after CFRA are still ciphered with the old keys. The target gNB must enable + // ciphering during handover setup to correctly decipher and verify integrity of these PDUs. + nr_rrc_pdcp_config_security(UE, true); // Process all PDU Session Resource Setup items from handover request DevAssert(msg->nb_of_pdusessions <= NGAP_MAX_PDU_SESSION); From 176b71ae7054bd4316072bb0290fc3cf892082d1 Mon Sep 17 00:00:00 2001 From: Rakesh BB Date: Wed, 19 Nov 2025 17:30:22 +0000 Subject: [PATCH 253/275] Fix MIB encoding in F1SetupRequest: use encode_MIB_NR_setup instead of encode_MIB_NR - The DU was using encode_MIB_NR() while constructing the GNB-DU System Information for the F1SetupRequest. - encode_MIB_NR() encodes a full NR_BCCH_BCH_Message_t, causing the MIB to be wrapped in a BCH container. - This results in incorrect MIB values in the F1SetupRequest PCAP (e.g., wrong systemFrameNumber, SCS, DMRS position, cellBarred). - According to 3GPP TS 38.473, the MIB carried in F1AP must be encoded as a raw NR_MIB_t. - The correct function encode_MIB_NR_setup() encodes MIB in the required format but was not used. - This patch replaces encode_MIB_NR() with encode_MIB_NR_setup() to ensure correct and standards-compliant MIB encoding in the F1SetupRequest. Closes issue #1034. --- openair2/GNB_APP/gnb_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 5238df77fa..518361baf7 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1127,7 +1127,7 @@ f1ap_gnb_du_system_info_t *get_sys_info(NR_BCCH_BCH_Message_t *mib, const NR_BCC sys_info->mib = calloc_or_fail(buf_len, sizeof(*sys_info->mib)); DevAssert(mib != NULL); - sys_info->mib_length = encode_MIB_NR(mib, 0, sys_info->mib, buf_len); + sys_info->mib_length = encode_MIB_NR_setup(mib->message.choice.mib, 0, sys_info->mib, buf_len); DevAssert(sys_info->mib_length == buf_len); DevAssert(sib1 != NULL); From fea86247da2464c4af45d05ba577a61f14e02ecb Mon Sep 17 00:00:00 2001 From: rmagueta Date: Tue, 18 Nov 2025 11:59:15 +0000 Subject: [PATCH 254/275] Do not configure NR_MeasurementTimingConfiguration during handover when neighboring cells are at the same frequency --- openair2/RRC/NR/rrc_gNB.c | 21 +----------------- openair2/RRC/NR/rrc_gNB_mobility.c | 35 +++++++++++++++++++++++++----- openair2/RRC/NR/rrc_gNB_mobility.h | 2 ++ 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index b18e820e91..3b1aeb0928 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -3407,26 +3407,7 @@ void rrc_gNB_generate_UeContextSetupRequest(const gNB_RRC_INST *rrc, } nr_rrc_du_container_t *du = get_du_for_ue((gNB_RRC_INST *)rrc, ue_p->rrc_ue_id); - byte_array_t *meas_timing_config = NULL; - if (du->mtc && ue_p->measConfig && ue_p->measConfig->measObjectToAddModList) { - NR_MeasObjectToAddModList_t *mo_list = ue_p->measConfig->measObjectToAddModList; - NR_ARFCN_ValueNR_t ssbFrequency0; - for (int i = 0; i < mo_list->list.count; i++) { - NR_MeasObjectToAddMod_t *mo = mo_list->list.array[i]; - if (mo->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) { - NR_MeasObjectNR_t *monr = mo->measObject.choice.measObjectNR; - if (i == 0) { - ssbFrequency0 = *monr->ssbFrequency; - } else if (ssbFrequency0 != *monr->ssbFrequency) { - meas_timing_config = calloc_or_fail(1, sizeof(*meas_timing_config)); - meas_timing_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE); - meas_timing_config->len = do_NR_MeasurementTimingConfiguration(du->mtc, meas_timing_config->buf, NR_RRC_BUF_SIZE); - cu2du.meas_timing_config = meas_timing_config; - break; - } - } - } - } + cu2du.meas_timing_config = get_meas_timing_config(du->mtc, ue_p->measConfig); int nb_srb = 1; f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs)); diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index bfe5dece8c..e570682ce8 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -192,12 +192,7 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, meas_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE); meas_config->len = do_NR_MeasConfig(ue->measConfig, meas_config->buf, NR_RRC_BUF_SIZE); - byte_array_t *meas_timing_config = NULL; - if (target_du->mtc) { - meas_timing_config = calloc_or_fail(1, sizeof(*meas_timing_config)); - meas_timing_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE); - meas_timing_config->len = do_NR_MeasurementTimingConfiguration(target_du->mtc, meas_timing_config->buf, NR_RRC_BUF_SIZE); - } + byte_array_t *meas_timing_config = get_meas_timing_config(target_du->mtc, ue->measConfig); byte_array_t *hpi = malloc_or_fail(sizeof(*hpi)); *hpi = copy_byte_array(*ho_prep_info); @@ -618,3 +613,31 @@ void nr_HO_N2_trigger_telnet(gNB_RRC_INST *rrc, uint32_t neighbour_pci, uint32_t nr_rrc_trigger_n2_ho(rrc, UE, scell_du->nr_pci, neighbour); } + +// This function detects if there are at least two different ssbFrequency values, and if so, returns meas_timing_config; +// otherwise, it returns NULL. When you return NULL, the measurement gaps will not be configured. +byte_array_t *get_meas_timing_config(const NR_MeasurementTimingConfiguration_t *mtc, const NR_MeasConfig_t *measConfig) +{ + if (!mtc || !measConfig || !measConfig->measObjectToAddModList) + return NULL; + + byte_array_t *meas_timing_config = NULL; + NR_MeasObjectToAddModList_t *mo_list = measConfig->measObjectToAddModList; + NR_ARFCN_ValueNR_t ssbFrequency0 = 0; + for (int i = 0; i < mo_list->list.count; i++) { + NR_MeasObjectToAddMod_t *mo = mo_list->list.array[i]; + if (mo->measObject.present != NR_MeasObjectToAddMod__measObject_PR_measObjectNR) + continue; + NR_MeasObjectNR_t *monr = mo->measObject.choice.measObjectNR; + if (i == 0) { + ssbFrequency0 = *monr->ssbFrequency; + } else if (ssbFrequency0 != *monr->ssbFrequency) { + meas_timing_config = calloc_or_fail(1, sizeof(*meas_timing_config)); + meas_timing_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE); + meas_timing_config->len = do_NR_MeasurementTimingConfiguration(mtc, meas_timing_config->buf, NR_RRC_BUF_SIZE); + break; + } + } + + return meas_timing_config; +} diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index bd0fd8e22c..88e3548220 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -108,4 +108,6 @@ void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_ void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue); +byte_array_t *get_meas_timing_config(const NR_MeasurementTimingConfiguration_t *mtc, const NR_MeasConfig_t *measConfig); + #endif /* RRC_GNB_MOBILITY_H_ */ From bff0e5364522ab50297b9b65bf9c4f3821707fc6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 21 Nov 2025 01:15:32 +0100 Subject: [PATCH 255/275] CI build analysis: print log file errors The generated HTML from CI builds so far only shows the number of errors, but not the errors themselves. This is inconvenient, as this means that developers have to go to the logs. This commit modifies AnalyzeBuildLogs(), which already iterates the entire file, to store the lines containing "error:" (as it would be emitted by a compiler). The callers of AnalyzeBuildLogs() now also print the analysis outcome in a more concise manner. Also, change the return parameter of AnalyzeBuildLogs() to a tuple to make the variable handling easier, and remove the unused warnings list. --- ci-scripts/cls_cluster.py | 6 +++--- ci-scripts/cls_containerize.py | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/ci-scripts/cls_cluster.py b/ci-scripts/cls_cluster.py index 4e13cdf7b3..8a9919c700 100644 --- a/ci-scripts/cls_cluster.py +++ b/ci-scripts/cls_cluster.py @@ -397,9 +397,9 @@ class Cluster: # Analyze the logs collectInfo = {} for image, lf in log_files: - ret = cls_containerize.AnalyzeBuildLogs(image, lf) - imgStatus = ret['status'] - msg = f"size {imageSize[image]}, analysis of {os.path.basename(lf)}: {ret['errors']} errors, {ret['warnings']} warnings" + imgStatus, errors = cls_containerize.AnalyzeBuildLogs(image, lf) + info = f"Analysis of {os.path.basename(lf)}: {imgStatus=}, size {imageSize[image]}, {len(errors)} errors" + msg = "\n".join([info] + errors) HTML.CreateHtmlTestRowQueue(image, 'OK' if imgStatus else 'KO', [msg]) status = status and imgStatus diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index cd07851319..71e437676c 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -86,9 +86,9 @@ def CreateTag(ranCommitID, ranBranch, ranAllowMerge): return tagToUse def AnalyzeBuildLogs(image, lf): - errorandwarnings = {} committed = False tagged = False + errors = [] with open(lf, mode='r') as inputfile: for line in inputfile: lineHasTag = re.search(f'Successfully tagged {image}:', str(line)) is not None @@ -97,11 +97,13 @@ def AnalyzeBuildLogs(image, lf): # the OpenShift Cluster builder prepends image registry URL lineHasCommit = re.search(r'COMMIT [a-zA-Z0-9\.:/\-]*' + image, str(line)) is not None committed = committed or lineHasCommit - errorandwarnings['errors'] = 0 if committed or tagged else 1 - errorandwarnings['warnings'] = 0 - errorandwarnings['status'] = committed or tagged - logging.info(f"Analyzing {image}, file {lf}: {errorandwarnings}") - return errorandwarnings + if re.search(r'error:|Errors|ERROR', line): + errors.append(f"=> {line.strip()}") + status = (committed or tagged) and len(errors) == 0 + logging.info(f"Analyzing {image}, file {lf}: {status=}, {len(errors)} errors") + for e in errors: + logging.info(e) + return status, errors def GetImageName(ssh, svcName, file): ret = ssh.run(f"docker compose -f {file} config --format json {svcName} | jq -r '.services.\"{svcName}\".image'", silent=True) @@ -460,9 +462,9 @@ class Containerize(): # Analyze the logs for name, lf in log_files: - ret = AnalyzeBuildLogs(name, lf) - imgStatus = ret['status'] - msg = f"size {allImagesSize[name]}, analysis of {os.path.basename(lf)}: {ret['errors']} errors, {ret['warnings']} warnings" + imgStatus, errors = AnalyzeBuildLogs(name, lf) + info = f"Analysis of {os.path.basename(lf)}: {imgStatus=}, size {allImagesSize[name]}, {len(errors)} errors" + msg = "\n".join([info] + errors) HTML.CreateHtmlTestRowQueue(name, 'OK' if imgStatus else 'KO', [msg]) status = status and imgStatus From 96ec2be8871f18219c79b3f420942fa0b10bcdff Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Mon, 24 Nov 2025 11:14:31 +0100 Subject: [PATCH 256/275] CI: Ensure EPC is always terminated in NSA-B200 pipeline This change updates the NSA-B200 CI pipeline to guarantee that the EPC is properly terminated regardless of pipeline success or failure. This avoids leaving EPC processes running after failed test. --- ci-scripts/xml_files/container_nsa_b200_quectel.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci-scripts/xml_files/container_nsa_b200_quectel.xml b/ci-scripts/xml_files/container_nsa_b200_quectel.xml index 336211e558..c73c6dadca 100644 --- a/ci-scripts/xml_files/container_nsa_b200_quectel.xml +++ b/ci-scripts/xml_files/container_nsa_b200_quectel.xml @@ -247,6 +247,7 @@ UndeployCoreNetwork Terminate EPC + true ltebox-nepes From 0386751da8c03d46d47540f0e966e8e2a82810d0 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Mon, 24 Nov 2025 11:45:47 +0100 Subject: [PATCH 257/275] Fix UTC timestamp documentation After merging !3751, the UTC timestamp now prints only the time. Update the documentation to reflect this simplified format. --- common/utils/LOG/DOC/rtusage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/utils/LOG/DOC/rtusage.md b/common/utils/LOG/DOC/rtusage.md index c1cd959137..cf2c78da5b 100644 --- a/common/utils/LOG/DOC/rtusage.md +++ b/common/utils/LOG/DOC/rtusage.md @@ -21,7 +21,7 @@ The following options can be specified to trigger the information added in the h - `line_num`: adds the (source code) line number - `time`: add the time since the system started in format `ss.ssssss` (seconds and microseconds sourced from `CLOCK_MONOTONIC`) - `wall_clock`: add the system-wide clock time that measures real (i.e., wall-clock) time in format `ss.ssssss` (seconds and microseconds since 1970-01-01 00:00:00 Coordinated Universal Time (UTC)) -- `utc_time`: add the UTC (Coordinated Universal Time) time in format `YYYY-MM-DD hh:mm:ss.ssssss UTC`. Note that this time is independent of the current time zone (it shows GMT). Also, printing this time has additional overhead compared to other time methods (due to time conversion and formatting). +- `utc_time`: add the UTC (Coordinated Universal Time) time in format `[hh:mm:ss.ssssss]`. Note that this time is independent of the current time zone (it shows GMT). Also, printing this time has additional overhead compared to other time methods (due to time conversion and formatting). Note: `time`, `utc_time` and `wall_clock` are mutually exclusive and cannot be used together. From 0360175042ca020e0475d4ecceed398ea86c8f1e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 13:04:02 +0100 Subject: [PATCH 258/275] Use UL functions for Qm/R determination During refactoring, the wrong functions and correspondingly tables have been used to calculate UL TB size. Fix through correct functions. This should fix these error messages: 3624774.778195 [MAC] E (nr_get_Qm_dl:2034) Invalid MCS table index 3 (expected in range [0,2]) 3624774.778207 [MAC] E (nr_get_code_rate_dl:2066) Invalid MCS table index 3 (expected in range [0,2]) 3624774.778211 [NR_MAC] E (nr_compute_tbs:63) Error in compute TBS with a NULL input, returning NULL TBS Fixes: 9449f777ccd2 ("Remove TBS table and recompute on demand") --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 3f546d41b4..a9b68b4dab 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -2081,8 +2081,8 @@ static int pf_ul(gNB_MAC_INST *nrmac, /* Create UE_sched for UEs eligibale for new data transmission*/ /* Calculate coefficient*/ - const uint8_t Qm = nr_get_Qm_dl(selected_mcs, current_BWP->mcs_table); - const uint16_t R = nr_get_code_rate_dl(selected_mcs, current_BWP->mcs_table); + const uint8_t Qm = nr_get_Qm_ul(selected_mcs, current_BWP->mcs_table); + const uint16_t R = nr_get_code_rate_ul(selected_mcs, current_BWP->mcs_table); const uint32_t tbs = nr_compute_tbs(Qm, R, 1, /* rbSize */ From be1150a82a3c7c006ac0f9a57a9d1d802963869a Mon Sep 17 00:00:00 2001 From: Reem Bahsoun Date: Mon, 24 Nov 2025 17:13:36 +0100 Subject: [PATCH 259/275] =?UTF-8?q?Update=20IP=20addresses=20for=20NG-AMF?= =?UTF-8?q?=20and=20NG-U=20interfaces=20in=20Aerial=20config=20files=20to?= =?UTF-8?q?=20match=20GH1=E2=80=99s=20new=20assigned=20IP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.conf | 4 ++-- .../conf_files/gnb-vnf.sa.band78.273prb.aerial.ul-heavy.conf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.conf b/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.conf index 9446d08ebf..9e95918e6f 100644 --- a/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.conf +++ b/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.conf @@ -176,8 +176,8 @@ gNBs = NETWORK_INTERFACES : { - GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.202"; - GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.202"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.53"; + GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.53"; GNB_PORT_FOR_S1U = 2152; # Spec 2152 }; diff --git a/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.ul-heavy.conf b/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.ul-heavy.conf index 43207d63ec..3410875961 100644 --- a/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.ul-heavy.conf +++ b/ci-scripts/conf_files/gnb-vnf.sa.band78.273prb.aerial.ul-heavy.conf @@ -168,8 +168,8 @@ gNBs = NETWORK_INTERFACES : { - GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.202"; - GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.202"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.53"; + GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.53"; GNB_PORT_FOR_S1U = 2152; # Spec 2152 }; From b7454cf5d43acae8301498bd645f21a072f49736 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 20 Nov 2025 14:57:54 +0100 Subject: [PATCH 260/275] Enable DRB integrity and ciphering in F1 and N2 HO pipelines with Quectel --- ci-scripts/conf_files/gnb-cu.sa.f1.ho.conf | 4 ++-- ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci-scripts/conf_files/gnb-cu.sa.f1.ho.conf b/ci-scripts/conf_files/gnb-cu.sa.f1.ho.conf index f16a94b472..ca1d9de0d7 100644 --- a/ci-scripts/conf_files/gnb-cu.sa.f1.ho.conf +++ b/ci-scripts/conf_files/gnb-cu.sa.f1.ho.conf @@ -53,7 +53,7 @@ security = { # preferred ciphering algorithms # the first one of the list that an UE supports in chosen # valid values: nea0, nea1, nea2, nea3 - ciphering_algorithms = ( "nea0" ); + ciphering_algorithms = ( "nea2", "nea0" ); # preferred integrity algorithms # the first one of the list that an UE supports in chosen @@ -63,7 +63,7 @@ security = { # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' drb_ciphering = "yes"; - drb_integrity = "no"; + drb_integrity = "yes"; }; log_config : diff --git a/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf b/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf index dc24284c18..577e23a87d 100644 --- a/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf +++ b/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf @@ -209,7 +209,7 @@ security = { # preferred ciphering algorithms # the first one of the list that an UE supports in chosen # valid values: nea0, nea1, nea2, nea3 - ciphering_algorithms = ( "nea0" ); + ciphering_algorithms = ( "nea2", "nea0" ); # preferred integrity algorithms # the first one of the list that an UE supports in chosen @@ -219,7 +219,7 @@ security = { # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' drb_ciphering = "yes"; - drb_integrity = "no"; + drb_integrity = "yes"; }; log_config : From 1e610af168374a69cebc07fe3ffd014182458399 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 14 Oct 2025 14:23:52 +0200 Subject: [PATCH 261/275] taking into account maximum number of layers for PDSCH supported by the UE for TBSlbrm --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 8 +++- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c | 38 ++++++++++--------- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h | 2 +- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index fd1cf7f9f3..86eed9d575 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -1113,7 +1113,12 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE // TODO: verify the case where maxMIMO_Layers is NULL, in which case // in principle maxMIMO_layers should be given by the maximum number of layers // for PDSCH supported by the UE for the serving cell (5.4.2.1 of 38.212) - long maxMIMO_Layers = UE->sc_info.maxMIMO_Layers_PDSCH ? *UE->sc_info.maxMIMO_Layers_PDSCH : 1; + long ue_supp_nl = ue_supported_dl_layers(scc, UE->capability); + long maxMIMO_Layers = UE->sc_info.maxMIMO_Layers_PDSCH ? *UE->sc_info.maxMIMO_Layers_PDSCH : ue_supp_nl; + if (maxMIMO_Layers < 1) { + LOG_D(NR_MAC, "Both maxMIMO_Layers_PDSCH and UE supported layers are not present, defaulting to 1\n"); + maxMIMO_Layers = 1; + } const int nl_tbslbrm = min(maxMIMO_Layers, 4); nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu = prepare_pdsch_pdu(dl_tti_pdsch_pdu, nr_mac, @@ -1395,5 +1400,4 @@ void nr_schedule_ue_spec(module_id_t module_id, /* PREPROCESSOR */ gNB_mac->pre_processor_dl(gNB_mac, &pdsch); - } diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index dff2f2b663..4ba5cc6fd9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -585,22 +585,13 @@ static void config_csiim(int do_csirs, } -void set_dl_maxmimolayers(NR_PDSCH_ServingCellConfig_t *pdsch_servingcellconfig, - const NR_ServingCellConfigCommon_t *scc, - const NR_UE_NR_Capability_t *uecap, - int maxMIMO_layers) +long ue_supported_dl_layers(const NR_ServingCellConfigCommon_t *scc, const NR_UE_NR_Capability_t *uecap) { - if(!pdsch_servingcellconfig->ext1) - pdsch_servingcellconfig->ext1=calloc(1,sizeof(*pdsch_servingcellconfig->ext1)); - if(!pdsch_servingcellconfig->ext1->maxMIMO_Layers) - pdsch_servingcellconfig->ext1->maxMIMO_Layers = calloc(1,sizeof(*pdsch_servingcellconfig->ext1->maxMIMO_Layers)); - NR_SCS_SpecificCarrier_t *scs_carrier = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]; int band = *scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0]; const frequency_range_t freq_range = get_freq_range_from_band(band); const int scs = scs_carrier->subcarrierSpacing; const int bw_size = scs_carrier->carrierBandwidth; - NR_FeatureSets_t *fs = uecap ? uecap->featureSets : NULL; if (fs) { const int bw_mhz = get_supported_bw_mhz(freq_range, get_supported_band_index(scs, freq_range, bw_size)); @@ -610,16 +601,29 @@ void set_dl_maxmimolayers(NR_PDSCH_ServingCellConfig_t *pdsch_servingcellconfig, if (scs == dl_fs->supportedSubcarrierSpacingDL && supported_bw_comparison(bw_mhz, &dl_fs->supportedBandwidthDL, dl_fs->channelBW_90mhz) && dl_fs->maxNumberMIMO_LayersPDSCH) { - long ue_supported_layers = (2 << *dl_fs->maxNumberMIMO_LayersPDSCH); - if (maxMIMO_layers == -1) - *pdsch_servingcellconfig->ext1->maxMIMO_Layers = NR_MAX_SUPPORTED_DL_LAYERS < ue_supported_layers ? NR_MAX_SUPPORTED_DL_LAYERS : ue_supported_layers; - else - *pdsch_servingcellconfig->ext1->maxMIMO_Layers = maxMIMO_layers < ue_supported_layers ? maxMIMO_layers : ue_supported_layers; - return; + return (2 << *dl_fs->maxNumberMIMO_LayersPDSCH); } } } - *pdsch_servingcellconfig->ext1->maxMIMO_Layers = 2; + return -1; +} + +static void set_dl_maxmimolayers(NR_PDSCH_ServingCellConfig_t *pdsch_servingcellconfig, + const NR_ServingCellConfigCommon_t *scc, + const NR_UE_NR_Capability_t *uecap, + int maxMIMO_layers) +{ + if(!pdsch_servingcellconfig->ext1) + pdsch_servingcellconfig->ext1 = calloc(1, sizeof(*pdsch_servingcellconfig->ext1)); + if(!pdsch_servingcellconfig->ext1->maxMIMO_Layers) + pdsch_servingcellconfig->ext1->maxMIMO_Layers = calloc(1, sizeof(*pdsch_servingcellconfig->ext1->maxMIMO_Layers)); + + long l = ue_supported_dl_layers(scc, uecap); + long ue_supported_layers = l > 1 ? l : 2; // min UE supported layers = 2 + if (maxMIMO_layers == -1) + *pdsch_servingcellconfig->ext1->maxMIMO_Layers = min(NR_MAX_SUPPORTED_DL_LAYERS, ue_supported_layers); + else + *pdsch_servingcellconfig->ext1->maxMIMO_Layers = min(maxMIMO_layers, ue_supported_layers); } // TODO: Implement to b_SRS = 1 and b_SRS = 2 diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h index 466f18c0de..9a556dc5ea 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h @@ -134,5 +134,5 @@ NR_CellGroupConfig_t * update_cellGroupConfig_for_BWP_switch(NR_CellGroupConfig_ NR_MeasurementTimingConfiguration_t *get_nr_mtc(uint8_t *buf, uint32_t len); measgap_config_t create_measgap_config(const NR_MeasurementTimingConfiguration_t *mtc, int scs, int min_rxtxtime); int encode_measgap_config(const measgap_config_t *c, uint8_t *buf); - +long ue_supported_dl_layers(const NR_ServingCellConfigCommon_t *scc, const NR_UE_NR_Capability_t *uecap); #endif From 10247cfbed390e8becb8cf2bc4587be07e8ee5ad Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 14 Oct 2025 14:52:13 +0200 Subject: [PATCH 262/275] taking into account maximum number of layers for PUSCH supported by the UE for TBSlbrm --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 5 ++--- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c | 11 ++++++++--- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 3f546d41b4..7488161ab7 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -2375,10 +2375,9 @@ nfapi_nr_pusch_pdu_t *prepare_pusch_pdu(nfapi_nr_ul_tti_request_t *future_ul_tti long *maxMIMO_Layers = UE->sc_info.maxMIMO_Layers_PUSCH; if (!maxMIMO_Layers && ul_bwp && ul_bwp->pusch_Config) maxMIMO_Layers = ul_bwp->pusch_Config->maxRank; + long lbrm_layers = maxMIMO_Layers ? *maxMIMO_Layers : ue_supported_ul_layers(UE->capability); AssertFatal (maxMIMO_Layers != NULL,"Option with max MIMO layers not configured is not supported\n"); - pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(ul_bwp->mcs_table, - UE->sc_info.ul_bw_tbslbrm, - *maxMIMO_Layers); + pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(ul_bwp->mcs_table, UE->sc_info.ul_bw_tbslbrm, lbrm_layers); } /* PUSCH PTRS */ if (sched_pusch->dmrs_info.ptrsConfig) { diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index 4ba5cc6fd9..3d0e3ed246 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -3739,10 +3739,11 @@ NR_CellGroupConfig_t *get_initial_cellGroupConfig(int uid, return cellGroupConfig; } -static long set_ul_max_layers(const nr_mac_config_t *configuration, const NR_UE_NR_Capability_t *uecap) +long ue_supported_ul_layers(const NR_UE_NR_Capability_t *uecap) { long ul_max_layers = 1; - if (uecap && uecap->featureSets + if (uecap + && uecap->featureSets && uecap->featureSets->featureSetsUplinkPerCC && uecap->featureSets->featureSetsUplinkPerCC->list.count > 0) { NR_FeatureSetUplinkPerCC_t *ul_feature_setup_per_cc = uecap->featureSets->featureSetsUplinkPerCC->list.array[0]; @@ -3758,11 +3759,15 @@ static long set_ul_max_layers(const nr_mac_config_t *configuration, const NR_UE_ ul_max_layers = 1; } } - ul_max_layers = min(ul_max_layers, configuration->pusch_AntennaPorts); } return ul_max_layers; } +static long set_ul_max_layers(const nr_mac_config_t *configuration, const NR_UE_NR_Capability_t *uecap) +{ + return min(ue_supported_ul_layers(uecap), configuration->pusch_AntennaPorts); +} + NR_CellGroupConfig_t * update_cellGroupConfig_for_BWP_switch(NR_CellGroupConfig_t *cellGroupConfig, const nr_mac_config_t *configuration, const NR_UE_NR_Capability_t *uecap, diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h index 9a556dc5ea..270aed4b71 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h @@ -134,5 +134,6 @@ NR_CellGroupConfig_t * update_cellGroupConfig_for_BWP_switch(NR_CellGroupConfig_ NR_MeasurementTimingConfiguration_t *get_nr_mtc(uint8_t *buf, uint32_t len); measgap_config_t create_measgap_config(const NR_MeasurementTimingConfiguration_t *mtc, int scs, int min_rxtxtime); int encode_measgap_config(const measgap_config_t *c, uint8_t *buf); +long ue_supported_ul_layers(const NR_UE_NR_Capability_t *uecap); long ue_supported_dl_layers(const NR_ServingCellConfigCommon_t *scc, const NR_UE_NR_Capability_t *uecap); #endif From b3c99d8ead1ab6230fbeb435f51e852fde2d741b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 14:45:41 +0100 Subject: [PATCH 263/275] Avoid recreation of LCIDs with wrong priority In all cases (NSA, HO), we create LCIDs before the UE triggers random access. Hence, process_addmod_bearers_cellGroupConfig() is not necessary, as it just tries to recreate an LCID. Furthermore, it includes a bug, as this function does set a wrong priority (0), so the sorting of LCIDs is not correct. This in turn creates problems with reestablishment, as LCID 1/SRB1 is not in the first position, making reestablishment fail (where we assume LCID 1 be in first position). The actual bug of setting priorities is fixed in the next commit. --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 1 - 1 file changed, 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 3f546d41b4..291e4266cb 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -832,7 +832,6 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, nr_mac_reset_ul_failure(UE_scheduling_control); reset_dl_harq_list(UE_scheduling_control); reset_ul_harq_list(UE_scheduling_control); - process_addmod_bearers_cellGroupConfig(&UE->UE_sched_ctrl, UE->CellGroup->rlc_BearerToAddModList); int ss_type; // we configure the UE using common search space with DCIX0 while waiting for a reconfiguration in SA // in NSA (or do-ra) there is no reconfiguration in NR From 87adaafcfca1746b0078a134d9bc2679acd9daa3 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 14:48:55 +0100 Subject: [PATCH 264/275] Refactor process_addmod_bearers_cellGroupConfig() and set priority Move the file process_addmod_bearers_cellGroupConfig() to the file where it is used. Correctly set priorities as requested in RLC bearer configuration. --- openair2/LAYER2/NR_MAC_gNB/config.c | 11 ++++++++++- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 17 ----------------- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 3 --- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index a0637ac107..5f808752a4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -1099,6 +1099,13 @@ void nr_mac_configure_sib1(gNB_MAC_INST *nrmac, const plmn_id_t *plmn, uint64_t AssertFatal(cc->sib1_bcch_length > 0, "could not encode SIB1\n"); } +static bool process_addmod_bearers_cellGroupConfig(NR_UE_sched_ctrl_t *sched_ctrl, const NR_RLC_BearerConfig_t *conf) +{ + int priority = conf->mac_LogicalChannelConfig->ul_SpecificParameters->priority; + nr_lc_config_t c = {.lcid = conf->logicalChannelIdentity, .priority = priority}; + return nr_mac_add_lcid(sched_ctrl, &c); +} + bool nr_mac_add_test_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t *CellGroup) { /* ideally, instead of this function, "users" of this function should call @@ -1120,7 +1127,9 @@ bool nr_mac_add_test_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t } int ss_type = NR_SearchSpace__searchSpaceType_PR_ue_Specific; configure_UE_BWP(nrmac, nrmac->common_channels[0].ServingCellConfigCommon, UE, false, ss_type, -1, -1); - process_addmod_bearers_cellGroupConfig(&UE->UE_sched_ctrl, CellGroup->rlc_BearerToAddModList); + const struct NR_CellGroupConfig__rlc_BearerToAddModList *l = CellGroup->rlc_BearerToAddModList; + for (int i = 0; l != NULL && i < l->list.count; ++i) + process_addmod_bearers_cellGroupConfig(&UE->UE_sched_ctrl, CellGroup->rlc_BearerToAddModList->list.array[i]); AssertFatal(CellGroup->rlc_BearerToReleaseList == NULL, "cannot release bearers while adding new UEs\n"); NR_SCHED_UNLOCK(&nrmac->sched_lock); LOG_I(NR_MAC, "Added new UE %x with initial CellGroup\n", rnti); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 128f2db336..a68c12092b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -3963,23 +3963,6 @@ void nr_mac_trigger_reconfiguration(const gNB_MAC_INST *nrmac, const NR_UE_info_ free_cellGroupConfig(cellGroup_for_UE); } -/* \brief add bearers from CellGroupConfig. - * - * This is a kind of hack, as this should be processed through a F1 UE Context - * setup request, but some modes do not use that (NSA/do-ra/phy_test). */ -void process_addmod_bearers_cellGroupConfig(NR_UE_sched_ctrl_t *sched_ctrl, const struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod) -{ - if (addmod == NULL) - return; /* nothing to do */ - - for (int i = 0; i < addmod->list.count; ++i) { - const NR_RLC_BearerConfig_t *conf = addmod->list.array[i]; - int lcid = conf->logicalChannelIdentity; - nr_lc_config_t c = {.lcid = lcid}; - nr_mac_add_lcid(sched_ctrl, &c); - } -} - long get_lcid_from_drbid(int drb_id) { return drb_id + 3; /* LCID is DRB + 3 */ diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 29398c9edf..e280a8cbe2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -480,9 +480,6 @@ bool nr_mac_check_ul_failure(gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t * void nr_mac_trigger_reconfiguration(const gNB_MAC_INST *nrmac, const NR_UE_info_t *UE, int new_bwp_id); -void process_addmod_bearers_cellGroupConfig(NR_UE_sched_ctrl_t *sched_ctrl, - const struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod); - bool nr_mac_add_lcid(NR_UE_sched_ctrl_t *sched_ctrl, const nr_lc_config_t *c); nr_lc_config_t *nr_mac_get_lc_config(NR_UE_sched_ctrl_t* sched_ctrl, int lcid); bool nr_mac_remove_lcid(NR_UE_sched_ctrl_t *sched_ctrl, long lcid); From 110bbd10bc5852421f7eb67e987946717c61496b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 25 Nov 2025 19:36:15 +0100 Subject: [PATCH 265/275] Simplify RLC reestablishment The idea is to reestablish all RLC entities (it's a new UE, and we set reestablishRLC on all bearerrs), so we should reestablish all RLC entities on gNB side as well, without any additional loop. Co-authored-by: Roberto Magueta --- openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index cf0f2286bc..8f98aabbee 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -681,11 +681,9 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req) /* new UE: tell the UE to reestablish RLC */ struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod = new_CellGroup->rlc_BearerToAddModList; for (int i = 0; i < addmod->list.count; ++i) { - asn1cCallocOne(addmod->list.array[i]->reestablishRLC, NR_RLC_BearerConfig__reestablishRLC_true); - for (int i = 1; i < seq_arr_size(&UE->UE_sched_ctrl.lc_config); ++i) { - nr_lc_config_t *lc_config = seq_arr_at(&UE->UE_sched_ctrl.lc_config, i); - nr_rlc_reestablish_entity(UE->rnti, lc_config->lcid); - } + NR_RLC_BearerConfig_t *bc = addmod->list.array[i]; + asn1cCallocOne(bc->reestablishRLC, NR_RLC_BearerConfig__reestablishRLC_true); + nr_rlc_reestablish_entity(UE->rnti, bc->logicalChannelIdentity); } } From b1b120551dfdf2cadf7f229b415a4de329952940 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 28 Nov 2025 08:24:25 +0100 Subject: [PATCH 266/275] Lower sl_ahead in do-ra config to reduce RA timeout risk --- ci-scripts/conf_files/gnb.band78.106prb.rfsim.phytest-dora.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/ci-scripts/conf_files/gnb.band78.106prb.rfsim.phytest-dora.conf b/ci-scripts/conf_files/gnb.band78.106prb.rfsim.phytest-dora.conf index eb7c1e01e2..737ef0aadf 100644 --- a/ci-scripts/conf_files/gnb.band78.106prb.rfsim.phytest-dora.conf +++ b/ci-scripts/conf_files/gnb.band78.106prb.rfsim.phytest-dora.conf @@ -195,6 +195,7 @@ RUs = ( max_pdschReferenceSignalPower = -27; max_rxgain = 50; eNB_instances = [0]; + sl_ahead = 3; sdr_addrs = "addr=192.168.10.2,second_addr=192.168.20.2"; } ); From d5eb935ecf5585ee2fade078679d91569d90d197 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 25 Nov 2025 01:23:42 +0100 Subject: [PATCH 267/275] Log preambles for pre-configured RA --- openair2/LAYER2/NR_MAC_gNB/config.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index a0637ac107..a94028928f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -1139,14 +1139,23 @@ void nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, NR_UE_info_t *UE) uint8_t num_preamble = cfra->resources.choice.ssb->ssb_ResourceList.list.count; ra->preambles.num_preambles = num_preamble; NR_COMMON_channels_t *cc = &nrmac->common_channels[0]; + char buf[200]; + int idx = 0; for (int i = 0; i < cc->num_active_ssb; i++) { for (int j = 0; j < num_preamble; j++) { if (cc->ssb_index[i] == cfra->resources.choice.ssb->ssb_ResourceList.list.array[j]->ssb) { // one dedicated preamble for each beam ra->preambles.preamble_list[i] = cfra->resources.choice.ssb->ssb_ResourceList.list.array[j]->ra_PreambleIndex; + if (idx < sizeof(buf) - 1) + idx += snprintf(buf + idx, sizeof(buf) - idx, " %d", ra->preambles.preamble_list[i]); break; } } } - LOG_I(NR_MAC, "Added new %s process for UE RNTI %04x with initial CellGroup\n", ra->cfra ? "CFRA" : "CBRA", UE->rnti); + LOG_I(NR_MAC, + "Added new %s process for UE RNTI %04x with initial CellGroup and %d preamble(s): %s\n", + ra->cfra ? "CFRA" : "CBRA", + UE->rnti, + num_preamble, + buf); } From f0151c21bdf255ea99f207b319f39c3652453c7b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 25 Nov 2025 01:24:23 +0100 Subject: [PATCH 268/275] Remove duplicate of RAR log above --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 8478df8916..b5357feb0d 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1629,8 +1629,6 @@ static void nr_generate_Msg2(module_id_t module_idP, ra->Msg3_frame, ra->Msg3_slot); - LOG_A(NR_MAC, "%d.%d Send RAR to RA-RNTI %04x\n", frameP, slotP, ra->RA_rnti); - tx_req->PDU_index = pduindex; tx_req->num_TLV = 1; tx_req->TLVs[0].length = TBS; From 8ea95a0e5263a67b85238ee704bcfe26c010e168 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 25 Nov 2025 01:11:22 +0100 Subject: [PATCH 269/275] CFRA: mark RA complete when sending Msg2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current contention-free random access forces a "Msg3" (which does not exist in CFRA) to be received by a UE. Sometimes, this "Msg3" is not received (for whatever reason), and the MAC declare that RA failed. This is problematic, as the spec says that RA is complete once Msg2 is received by the UE. To avoid this, inconditionally mark RA as complete as soon as we receive an indication of Msg3 (DTX or not). Note that after this change, we still send a UL grant in Msg2. This is because 38.321 ยง5.1.4 is not clear to me whether we should send UL grant (it does not explicitly exclude it), and it says > 3> if the Random Access Response includes a MAC subPDU with RAPID only: > [...] > 4> indicate the reception of an acknowledgement for SI request to > upper layers. which is NOT the case (but then I don't know/think we can have CFRA for SI request?). Since it also works with COTS UE, I leave Msg3 for the moment. The reason to not directly mark RA as complete when sending Msg2 is because of possible retransmissions in do-ra mode. In fact, in do-ra, there might already be data awaiting. In that case, the DLSCH scheduler schedules data _in the same slot as Msg2_, which the UE does not decode, leading to retransmissions. --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 47 +++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 170 +++++++++--------- 2 files changed, 115 insertions(+), 102 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index b5357feb0d..f8df95f679 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -785,6 +785,7 @@ static void nr_generate_Msg3_retransmission(module_id_t module_idP, { gNB_MAC_INST *nr_mac = RC.nrmac[module_idP]; NR_RA_t *ra = UE->ra; + DevAssert(!ra->cfra); NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id]; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP; @@ -1451,6 +1452,11 @@ static void nr_generate_Msg2(module_id_t module_idP, return; const NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP; + // check the feasibility of Msg3, the actual Msg3 allocation + // is further below. In the case of CFRA, we don't need Msg3, but 38.321 + // ยง5.1.4 does not clearly exclude Msg3, and UL TA might still be useful. + // Before the change in this commit, we used CFRA but required Msg3, which + // COTS UE would often (but not always) send. bool ret = get_feasible_msg3_tda(scc, get_delta_for_k2(ul_bwp->scs), ul_bwp->tdaList_Common, @@ -1523,6 +1529,7 @@ static void nr_generate_Msg2(module_id_t module_idP, return; } + // get an actual Msg3 allocation in CBRA bool msg3_ret = nr_get_Msg3alloc(nr_mac, CC_id, slotP, frameP, UE); if (!msg3_ret) { reset_beam_status(&nr_mac->beam_info, ra->Msg3_frame, ra->Msg3_slot, UE->UE_beam_index, n_slots_frame, ra->Msg3_beam.new_beam); @@ -1609,25 +1616,31 @@ static void nr_generate_Msg2(module_id_t module_idP, // DL TX request nfapi_nr_pdu_t *tx_req = &TX_req->pdu_list[TX_req->Number_of_PDUs]; - // Program UL processing for Msg3 + // If CFRA: 38.321 ยง5.1.4 does not clearly say (to me) if UL grant should be + // dropped or not, and COTS UE would often send Msg3 if we configured CFRA + // but required Msg3. Also, "if RAR includes a MAC subPDU with RAPID: [...] + // indicate the reception of an acknowledgement for SI request to upper + // layers." which is not the case here. nr_add_msg3(module_idP, CC_id, frameP, slotP, UE, (uint8_t *)&tx_req->TLVs[0].value.direct[0]); - // Start RA contention resolution timer in Msg3 transmission slot (current slot + K2) - // 3GPP TS 38.321 Section 5.1.5 Contention Resolution - start_ra_contention_resolution_timer( - ra, - scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->ra_ContentionResolutionTimer, - *ul_bwp->tdaList_Common->list.array[ra->Msg3_tda_id]->k2 + get_NTN_Koffset(scc), - ul_bwp->scs); + if (!ra->cfra) { + LOG_D(NR_MAC, + "UE %04x: %d.%d: Setting RA-Msg3 reception for SFN.Slot %d.%d\n", + UE->rnti, + frameP, + slotP, + ra->Msg3_frame, + ra->Msg3_slot); - LOG_D(NR_MAC, - "UE %04x: %d.%d: Setting RA-Msg3 reception (%s) for SFN.Slot %d.%d\n", - UE->rnti, - frameP, - slotP, - ra->cfra ? "CFRA" : "CBRA", - ra->Msg3_frame, - ra->Msg3_slot); + // Start RA contention resolution timer in Msg3 transmission slot (current slot + K2) + // 3GPP TS 38.321 Section 5.1.5 Contention Resolution + start_ra_contention_resolution_timer( + ra, + scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->ra_ContentionResolutionTimer, + *ul_bwp->tdaList_Common->list.array[ra->Msg3_tda_id]->k2 + get_NTN_Koffset(scc), + ul_bwp->scs); + + } tx_req->PDU_index = pduindex; tx_req->num_TLV = 1; @@ -1652,6 +1665,7 @@ static void nr_generate_Msg2(module_id_t module_idP, vrb_map[bwp_info.bwpStart + rb + rbStart] |= SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols); } + // In CFRA: in Msg3 handling, will unconditionally mark succeeded ra->ra_state = nrRA_WAIT_Msg3; } @@ -1673,6 +1687,7 @@ static void nr_generate_Msg4_MsgB(module_id_t module_idP, NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; NR_SearchSpace_t *ss = sched_ctrl->search_space; NR_RA_t *ra = UE->ra; + DevAssert(!ra->cfra); const char *ra_type_str = ra->ra_type == RA_2_STEP ? "MsgB" : "Msg4"; NR_ControlResourceSet_t *coreset = sched_ctrl->coreset; AssertFatal(coreset != NULL, "Coreset cannot be null for RA %s\n", ra_type_str); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 3f546d41b4..c345089696 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -725,13 +725,6 @@ static void handle_nr_ul_harq(gNB_MAC_INST *nrmac, static void handle_msg3_failed_rx(gNB_MAC_INST *mac, NR_RA_t *ra, rnti_t rnti, int harq_round_max) { - // for CFRA (NSA) do not schedule retransmission of msg3 - if (ra->cfra) { - LOG_W(NR_MAC, "UE %04x RA failed at state %s (NSA msg3 reception failed)\n", rnti, nrra_text[ra->ra_state]); - nr_release_ra_UE(mac, rnti); - return; - } - if (ra->msg3_round >= harq_round_max - 1) { LOG_W(NR_MAC, "UE %04x RA failed at state %s (Reached msg3 max harq rounds)\n", rnti, nrra_text[ra->ra_state]); nr_release_ra_UE(mac, rnti); @@ -756,6 +749,7 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, const uint16_t rssi) { gNB_MAC_INST *mac = RC.nrmac[mod_id]; + NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; NR_UE_info_t *UE = find_ra_UE(&mac->UE_info, rnti); if (!UE) { LOG_E(NR_MAC, "UL SDU discarded. Couldn't finde UE with RNTI %04x \n", rnti); @@ -768,6 +762,31 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, return; } + // CFRA: we scheduled Msg3 (which does not exist in CFRA, see also + // nr_generate_Msg2()). We did not mark RA as complete right away, as the + // DLSCH scheduler might schedule in the same slot as Msg2 if RLC has data + // (which can only happen in do-ra), so we mark it as complete now. + if (ra->cfra) { + NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; + nr_mac_reset_ul_failure(sched_ctrl); + reset_dl_harq_list(sched_ctrl); + reset_ul_harq_list(sched_ctrl); + // we configure the UE using common search space with DCIX0 while waiting for a reconfiguration in SA + // in NSA (or do-ra) there is no reconfiguration in NR + int ss_type = IS_SA_MODE(get_softmodem_params()) ? NR_SearchSpace__searchSpaceType_PR_common + : NR_SearchSpace__searchSpaceType_PR_ue_Specific; + configure_UE_BWP(mac, scc, UE, false, ss_type, -1, -1); + // initialize ta_frame in case there is no Msg3 received + UE->UE_sched_ctrl.ta_frame = (frame + 100) % MAX_FRAME_NUMBER; + if (!transition_ra_connected_nr_ue(mac, UE)) { + LOG_E(NR_MAC, "cannot add UE %04x: list is full\n", UE->rnti); + delete_nr_ue_data(UE, NULL, &mac->UE_info.uid_allocator); + } else { + LOG_A(NR_MAC, "(rnti 0x%04x) CFRA procedure succeeded!\n", UE->rnti); + } + return; // TODO: handle Msg3 in case it has been received? + } + const int target_snrx10 = mac->pusch_target_snrx10; if (!sdu) { // NACK if (ra->ra_state != nrRA_WAIT_Msg3) @@ -826,94 +845,73 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, UE_scheduling_control->ta_update = timing_advance; UE_scheduling_control->raw_rssi = rssi; LOG_D(NR_MAC, "[UE %04x] PUSCH TPC %d and TA %d\n", UE->rnti, UE_scheduling_control->tpc0, UE_scheduling_control->ta_update); - NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; - if (ra->cfra) { - LOG_A(NR_MAC, "(rnti 0x%04x) CFRA procedure succeeded!\n", UE->rnti); - nr_mac_reset_ul_failure(UE_scheduling_control); - reset_dl_harq_list(UE_scheduling_control); - reset_ul_harq_list(UE_scheduling_control); - process_addmod_bearers_cellGroupConfig(&UE->UE_sched_ctrl, UE->CellGroup->rlc_BearerToAddModList); - int ss_type; - // we configure the UE using common search space with DCIX0 while waiting for a reconfiguration in SA - // in NSA (or do-ra) there is no reconfiguration in NR - if (IS_SA_MODE(get_softmodem_params())) - ss_type = NR_SearchSpace__searchSpaceType_PR_common; - else - ss_type = NR_SearchSpace__searchSpaceType_PR_ue_Specific; - configure_UE_BWP(mac, scc, UE, false, ss_type, -1, -1); - if (!transition_ra_connected_nr_ue(mac, UE)) { - LOG_E(NR_MAC, "cannot add UE %04x: list is full\n", UE->rnti); - delete_nr_ue_data(UE, NULL, &mac->UE_info.uid_allocator); - return; - } - } else { - LOG_D(NR_MAC, "[RAPROC] Received %s:\n", ra->ra_type == RA_2_STEP ? "MsgA-PUSCH" : "Msg3"); - for (uint32_t k = 0; k < sdu_len; k++) { - LOG_D(NR_MAC, "(%i): 0x%x\n", k, sdu[k]); - } - // 3GPP TS 38.321 Section 5.4.3 Multiplexing and assembly - // Logical channels shall be prioritised in accordance with the following order (highest priority listed first): - // - MAC CE for C-RNTI, or data from UL-CCCH; - // This way, we need to process MAC CE for C-RNTI if RA is active and it is present in the MAC PDU - // Search for MAC CE for C-RNTI - rnti_t crnti = lcid_crnti_lookahead(sdu, sdu_len); - if (crnti != 0) { // 3GPP TS 38.321 Table 7.1-1: RNTI values, RNTI 0x0000: N/A - // Replace the current UE by the UE identified by C-RNTI - NR_UE_info_t *old_UE = find_nr_UE(&mac->UE_info, crnti); - if (!old_UE) { - // The UE identified by C-RNTI no longer exists at the gNB - // Let's abort the current RA, so the UE will trigger a new RA later but using RRCSetupRequest instead. A better - // solution may be implemented - LOG_W(NR_MAC, "No UE found with C-RNTI %04x, ignoring Msg3 to have UE come back with new RA attempt\n", UE->rnti); - nr_release_ra_UE(mac, rnti); - return; - } - // in case UE beam has changed - old_UE->UE_beam_index = UE->UE_beam_index; - // Reset UL failure for old UE - nr_mac_reset_ul_failure(&old_UE->UE_sched_ctrl); - // Reset HARQ processes - reset_dl_harq_list(&old_UE->UE_sched_ctrl); - reset_ul_harq_list(&old_UE->UE_sched_ctrl); + LOG_D(NR_MAC, "[RAPROC] Received %s:\n", ra->ra_type == RA_2_STEP ? "MsgA-PUSCH" : "Msg3"); + for (uint32_t k = 0; k < sdu_len; k++) { + LOG_D(NR_MAC, "(%i): 0x%x\n", k, sdu[k]); + } - // Only trigger RRCReconfiguration if UE is not performing RRCReestablishment - // The RRCReconfiguration will be triggered by the RRCReestablishmentComplete - if (!old_UE->reconfigSpCellConfig) { - LOG_I(NR_MAC, "Received UL_SCH_LCID_C_RNTI with C-RNTI 0x%04x, triggering RRC Reconfiguration\n", crnti); - // Trigger RRCReconfiguration - nr_mac_trigger_reconfiguration(mac, old_UE, -1); - // we configure the UE using common search space with DCIX0 while waiting for a reconfiguration - configure_UE_BWP(mac, scc, old_UE, false, NR_SearchSpace__searchSpaceType_PR_common, -1, -1); - } + // 3GPP TS 38.321 Section 5.4.3 Multiplexing and assembly + // Logical channels shall be prioritised in accordance with the following order (highest priority listed first): + // - MAC CE for C-RNTI, or data from UL-CCCH; + // This way, we need to process MAC CE for C-RNTI if RA is active and it is present in the MAC PDU + // Search for MAC CE for C-RNTI + rnti_t crnti = lcid_crnti_lookahead(sdu, sdu_len); + if (crnti != 0) { // 3GPP TS 38.321 Table 7.1-1: RNTI values, RNTI 0x0000: N/A + // Replace the current UE by the UE identified by C-RNTI + NR_UE_info_t *old_UE = find_nr_UE(&mac->UE_info, crnti); + if (!old_UE) { + // The UE identified by C-RNTI no longer exists at the gNB + // Let's abort the current RA, so the UE will trigger a new RA later but using RRCSetupRequest instead. A better + // solution may be implemented + LOG_W(NR_MAC, "No UE found with C-RNTI %04x, ignoring Msg3 to have UE come back with new RA attempt\n", UE->rnti); nr_release_ra_UE(mac, rnti); - LOG_A(NR_MAC, "%4d.%2d RA with C-RNTI %04x complete\n", frame, slot, crnti); - - // Decode the entire MAC PDU - // It may have multiple MAC subPDUs, for example, a MAC subPDU with LCID 1 caring a RRCReestablishmentComplete - nr_process_mac_pdu(mod_id, old_UE, CC_id, frame, slot, sdu, sdu_len, -1); return; } + // in case UE beam has changed + old_UE->UE_beam_index = UE->UE_beam_index; + // Reset UL failure for old UE + nr_mac_reset_ul_failure(&old_UE->UE_sched_ctrl); + // Reset HARQ processes + reset_dl_harq_list(&old_UE->UE_sched_ctrl); + reset_ul_harq_list(&old_UE->UE_sched_ctrl); - // UE Contention Resolution Identity - // Store the first 48 bits belonging to the uplink CCCH SDU within Msg3 to fill in Msg4 - // First byte corresponds to R/LCID MAC sub-header - memcpy(ra->cont_res_id, &sdu[1], sizeof(uint8_t) * 6); + // Only trigger RRCReconfiguration if UE is not performing RRCReestablishment + // The RRCReconfiguration will be triggered by the RRCReestablishmentComplete + if (!old_UE->reconfigSpCellConfig) { + LOG_I(NR_MAC, "Received UL_SCH_LCID_C_RNTI with C-RNTI 0x%04x, triggering RRC Reconfiguration\n", crnti); + // Trigger RRCReconfiguration + nr_mac_trigger_reconfiguration(mac, old_UE, -1); + // we configure the UE using common search space with DCIX0 while waiting for a reconfiguration + configure_UE_BWP(mac, scc, old_UE, false, NR_SearchSpace__searchSpaceType_PR_common, -1, -1); + } + nr_release_ra_UE(mac, rnti); + LOG_A(NR_MAC, "%4d.%2d RA with C-RNTI %04x complete\n", frame, slot, crnti); - // Decode MAC PDU - // the function is only called to decode the contention resolution sub-header - // harq_pid set a non-valid value because it is not used in this call - nr_process_mac_pdu(mod_id, UE, CC_id, frame, slot, sdu, sdu_len, -1); - - LOG_I(NR_MAC, - "Activating scheduling %s for TC_RNTI 0x%04x (state %s)\n", - ra->ra_type == RA_2_STEP ? "MsgB" : "Msg4", - UE->rnti, - nrra_text[ra->ra_state]); - ra->ra_state = ra->ra_type == RA_2_STEP ? nrRA_MsgB : nrRA_Msg4; - LOG_D(NR_MAC, "TC_RNTI 0x%04x next RA state %s\n", UE->rnti, nrra_text[ra->ra_state]); + // Decode the entire MAC PDU + // It may have multiple MAC subPDUs, for example, a MAC subPDU with LCID 1 caring a RRCReestablishmentComplete + nr_process_mac_pdu(mod_id, old_UE, CC_id, frame, slot, sdu, sdu_len, -1); return; } + + // UE Contention Resolution Identity + // Store the first 48 bits belonging to the uplink CCCH SDU within Msg3 to fill in Msg4 + // First byte corresponds to R/LCID MAC sub-header + memcpy(ra->cont_res_id, &sdu[1], sizeof(uint8_t) * 6); + + // Decode MAC PDU + // the function is only called to decode the contention resolution sub-header + // harq_pid set a non-valid value because it is not used in this call + nr_process_mac_pdu(mod_id, UE, CC_id, frame, slot, sdu, sdu_len, -1); + + LOG_I(NR_MAC, + "Activating scheduling %s for TC_RNTI 0x%04x (state %s)\n", + ra->ra_type == RA_2_STEP ? "MsgB" : "Msg4", + UE->rnti, + nrra_text[ra->ra_state]); + ra->ra_state = ra->ra_type == RA_2_STEP ? nrRA_MsgB : nrRA_Msg4; + LOG_D(NR_MAC, "TC_RNTI 0x%04x next RA state %s\n", UE->rnti, nrra_text[ra->ra_state]); + return; } static void _nr_rx_sdu(const module_id_t gnb_mod_idP, From f6135ff1ff58d27d2ce627feb9145284582ccdbd Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Sun, 16 Nov 2025 17:10:41 +0100 Subject: [PATCH 270/275] fix(FHI 7.2): PRACH frame test A test was missing in the PRACH RX function of the FHI 7.2 in order to know if the frame had a PRACH occasion. This was leading to continuous warning messages as PRACH RX was triggered on frames in which PRACH was not expected by the scheduler. --- radio/fhi_72/oaioran.c | 8 +++++--- radio/fhi_72/oran-init.c | 16 ++++++---------- radio/fhi_72/oran-init.h | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/radio/fhi_72/oaioran.c b/radio/fhi_72/oaioran.c index 493221d6a9..31c754738d 100644 --- a/radio/fhi_72/oaioran.c +++ b/radio/fhi_72/oaioran.c @@ -172,14 +172,16 @@ static int read_prach_data(ru_info_t *ru, int frame, int slot) struct xran_fh_init *fh_init = get_xran_fh_init(); struct xran_fh_config *fh_cfg = get_xran_fh_config(0); - int prach_sym = get_prach_conf_duration(0); + nr_prach_info_t prach_info = get_prach_info(0); struct xran_ru_config *ru_conf = &fh_cfg->ru_conf; int slots_per_frame = 10 << fh_cfg->frame_conf.nNumerology; int slots_per_subframe = 1 << fh_cfg->frame_conf.nNumerology; int tti = slots_per_frame * (frame) + (slot); uint32_t subframe = slot / slots_per_subframe; - uint32_t is_prach_slot = xran_is_prach_slot(0, subframe, (slot % slots_per_subframe)); + // PRACH occasion in a frame if and only if SFN % x == y, TS 38.211 Table 6.3.3.2-2/3/4 + uint32_t is_prach_frame = (frame % prach_info.x == prach_info.y); + uint32_t is_prach_slot = is_prach_frame && xran_is_prach_slot(0, subframe, (slot % slots_per_subframe)); int nb_rx_per_ru = ru->nb_rx / fh_init->xran_ports; /* If it is PRACH slot, copy prach IQ from XRAN PRACH buffer to OAI PRACH buffer */ @@ -188,7 +190,7 @@ static int read_prach_data(ru_info_t *ru, int frame, int slot) LOG_W(HW, "we get rach data from ru, but it is not scheduled %d.%d\n", frame, slot); return -1; } - for (sym_idx = 0; sym_idx < prach_sym; sym_idx++) { + for (sym_idx = 0; sym_idx < prach_info.N_dur; sym_idx++) { for (int aa = 0; aa < ru->nb_rx; aa++) { int16_t *dst, *src; int idx = 0; diff --git a/radio/fhi_72/oran-init.c b/radio/fhi_72/oran-init.c index 07517dcf18..516c2795fe 100644 --- a/radio/fhi_72/oran-init.c +++ b/radio/fhi_72/oran-init.c @@ -30,9 +30,7 @@ #include "common/utils/assertions.h" #include "common/utils/LOG/log.h" -#include "common/utils/nr/nr_common.h" #include "common_lib.h" -#include "openair2/LAYER2/NR_MAC_COMMON/nr_prach_config.h" /* PRACH data samples are 32 bits wide (16bits for I/Q). Each packet contains * 840 samples for long sequence or 144 for short sequence. The payload length @@ -50,7 +48,7 @@ void *gxran_handle; static struct xran_fh_init g_fh_init = {0}; static struct xran_fh_config g_fh_config[XRAN_PORTS_NUM] = {0}; -static uint32_t g_prach_conf_duration[XRAN_PORTS_NUM] = {0}; +static nr_prach_info_t g_prach_info[XRAN_PORTS_NUM] = {0}; static uint32_t get_nSW_ToFpga_FTH_TxBufferLen(int mu, int sections) { @@ -470,11 +468,9 @@ int *oai_oran_initialize(struct xran_fh_init *xran_fh_init, struct xran_fh_confi // retrieve and store prach duration uint8_t idx = xran_fh_config[o_xu_id].prach_conf.nPrachConfIdx; const struct xran_frame_config *fc = &xran_fh_config[o_xu_id].frame_conf; - g_prach_conf_duration[o_xu_id] = - get_nr_prach_occasion_info_from_index(idx, - fc->nNumerology > 2 ? FR2 : FR1, - fc->nFrameDuplexType == XRAN_FDD ? duplex_mode_FDD : duplex_mode_TDD) - .N_dur; + g_prach_info[o_xu_id] = get_nr_prach_occasion_info_from_index(idx, + fc->nNumerology > 2 ? FR2 : FR1, + fc->nFrameDuplexType == XRAN_FDD ? duplex_mode_FDD : duplex_mode_TDD); } // store config after xran initialization -- xran makes modifications to @@ -504,9 +500,9 @@ struct xran_fh_config *get_xran_fh_config(uint32_t port_id) return &g_fh_config[port_id]; } -uint32_t get_prach_conf_duration(uint32_t port_id) +nr_prach_info_t get_prach_info(uint32_t port_id) { struct xran_fh_init *fh_init = get_xran_fh_init(); DevAssert(port_id < fh_init->xran_ports); - return g_prach_conf_duration[port_id]; + return g_prach_info[port_id]; } diff --git a/radio/fhi_72/oran-init.h b/radio/fhi_72/oran-init.h index e56b683a9f..419cbccce4 100644 --- a/radio/fhi_72/oran-init.h +++ b/radio/fhi_72/oran-init.h @@ -22,6 +22,9 @@ #ifndef ORAN_INIT_H #define ORAN_INIT_H +#include "common/utils/nr/nr_common.h" +#include "openair2/LAYER2/NR_MAC_COMMON/nr_prach_config.h" + typedef struct oran_bufs { struct xran_flat_buffer tx[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN][XRAN_NUM_OF_SYMBOL_PER_SLOT]; struct xran_flat_buffer tx_prbmap[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN]; @@ -62,6 +65,16 @@ int *oai_oran_initialize(struct xran_fh_init *fh_init, struct xran_fh_config *fh oran_buf_list_t *get_xran_buffers(uint32_t port_id); struct xran_fh_init *get_xran_fh_init(void); struct xran_fh_config *get_xran_fh_config(uint32_t port_id); -uint32_t get_prach_conf_duration(uint32_t port_id); + +/** + * @brief queries PRACH information necessary for the FHI + * + * @arg port_id XRAN port id + * @return PRACH information holding among other information: + * - N_dur PRACH duration from TS 38.211 Table 6.3.3.2-2/3/4 + * - x from TS 38.211 Table 6.3.3.2-2/3/4 + * - y from TS 38.211 Table 6.3.3.2-2/3/4 + */ +nr_prach_info_t get_prach_info(uint32_t port_id); #endif /* ORAN_INIT_H */ From d6e4e49fcb6e9b3cd49040fe0244b21672e84361 Mon Sep 17 00:00:00 2001 From: Reem Bahsoun Date: Thu, 27 Nov 2025 15:34:45 +0100 Subject: [PATCH 271/275] Reduce the time given to the UE to re-attach after failure Number of tries is reduced from 4 to 3 After each failure the timeout is incremented by 20 --- ci-scripts/cls_module.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci-scripts/cls_module.py b/ci-scripts/cls_module.py index cfdd96477f..17d45828c8 100644 --- a/ci-scripts/cls_module.py +++ b/ci-scripts/cls_module.py @@ -111,10 +111,11 @@ class Module_UE: return self._collectTrace(ctx) return None - def attach(self, attach_tries = 4, attach_timeout = 60): + def attach(self, attach_tries = 3, attach_timeout = 0): ip = None while attach_tries > 0: self._command(self.cmd_dict["attach"]) + attach_timeout += 20 timeout = attach_timeout logging.debug("Waiting for IP address to be assigned") ip = self.getIP(silent=False, reportNonZero=True) From 923b2ec1880c12a7d592135609411b570da606df Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 28 Nov 2025 13:27:29 +0100 Subject: [PATCH 272/275] Correctly copy nFAPI messages All nFAPI message have copy helpers, so use that for "loading" nFAPI messages into the L1. This also fixes a bug/error message 2. 0 no corresponding tx_data.request for dl_tti.request index 11, dropping because the loop over DL_tti.request assumed that all messages are PDSCH PDUs, when we can have others such as PDCCH, etc. In other words, when looking up indices ("11" in the example above), we were checking also inside other messages for SSB, PDCCH, etc, which is of course wrong. That loop was an attempt to validate matching PDSCH PDUs with TX_data.requests. This check is already done inside phy_procedures_gNB_TX() through an assert, but this commit replaces with the equivalent check previously inside nr_pnf_p7_get_msgs(). This is ok, as the L1 cannot trust the scheduler (in monolithic) to provide the correct information, either. Fixes: eefb9da69a48 ("Reimplement nFAPI message exchange after msgDataTx removal") --- nfapi/open-nFAPI/pnf/src/pnf_p7.c | 43 ++++++----------------- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 22 ++++++++---- 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/nfapi/open-nFAPI/pnf/src/pnf_p7.c b/nfapi/open-nFAPI/pnf/src/pnf_p7.c index 197bac22b7..8b684261a7 100644 --- a/nfapi/open-nFAPI/pnf/src/pnf_p7.c +++ b/nfapi/open-nFAPI/pnf/src/pnf_p7.c @@ -728,34 +728,19 @@ int nr_pnf_p7_get_msgs(pnf_p7_t* pnf_p7, ret_dl_tti->SFN = sfn; ret_dl_tti->Slot = slot; ret_dl_tti->dl_tti_request_body.nPDUs = 0; + nfapi_nr_dl_tti_request_t* dl_tti_req = &tx_slot_buffer->dl_tti_req; + if (dl_tti_req->SFN == sfn && dl_tti_req->Slot == slot) { + copy_dl_tti_request(dl_tti_req, ret_dl_tti); + tx_slot_buffer->dl_tti_req.SFN = -1; + tx_slot_buffer->dl_tti_req.Slot = -1; + } + ret_tx_data->SFN = sfn; ret_tx_data->Slot = slot; ret_tx_data->Number_of_PDUs = 0; - nfapi_nr_dl_tti_request_t* dl_tti_req = &tx_slot_buffer->dl_tti_req; nfapi_nr_tx_data_request_t* txd = &tx_slot_buffer->tx_data_req; - if (dl_tti_req->SFN == sfn && dl_tti_req->Slot == slot && txd->SFN == sfn && txd->Slot == slot) { - nfapi_nr_dl_tti_request_body_t* p_tti = &dl_tti_req->dl_tti_request_body; - int ret_idx = 0; - for (int i = 0; i < p_tti->nPDUs; ++i) { - int tx_index = p_tti->dl_tti_pdu_list[i].pdsch_pdu.pdsch_pdu_rel15.pduIndex - 1; - if (tx_index >= txd->Number_of_PDUs) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, - "%4d.%2d no corresponding tx_data.request for dl_tti.request index %d, dropping\n", - sfn, - slot, - tx_index); - continue; - } - ret_dl_tti->dl_tti_request_body.dl_tti_pdu_list[ret_idx] = p_tti->dl_tti_pdu_list[i]; - // TODO avoid huge copy - ret_tx_data->pdu_list[ret_idx] = txd->pdu_list[i]; - ret_idx++; - } - ret_dl_tti->dl_tti_request_body.nPDUs = ret_idx; - ret_tx_data->Number_of_PDUs = ret_idx; - - tx_slot_buffer->dl_tti_req.SFN = -1; - tx_slot_buffer->dl_tti_req.Slot = -1; + if (txd->SFN == sfn && txd->Slot == slot) { + copy_tx_data_request(txd, ret_tx_data); tx_slot_buffer->tx_data_req.SFN = -1; tx_slot_buffer->tx_data_req.Slot = -1; } @@ -764,10 +749,7 @@ int nr_pnf_p7_get_msgs(pnf_p7_t* pnf_p7, ret_ul_tti->Slot = slot; ret_ul_tti->n_pdus = 0; if (tx_slot_buffer->ul_tti_req.SFN == sfn && tx_slot_buffer->ul_tti_req.Slot == slot) { - nfapi_nr_ul_tti_request_t* p = &tx_slot_buffer->ul_tti_req; - ret_ul_tti->n_pdus = p->n_pdus; - for (int i = 0; i < p->n_pdus; ++i) - ret_ul_tti->pdus_list[i] = p->pdus_list[i]; + copy_ul_tti_request(&tx_slot_buffer->ul_tti_req, ret_ul_tti); tx_slot_buffer->ul_tti_req.SFN = -1; tx_slot_buffer->ul_tti_req.Slot = -1; } @@ -776,10 +758,7 @@ int nr_pnf_p7_get_msgs(pnf_p7_t* pnf_p7, ret_ul_dci->Slot = slot; ret_ul_dci->numPdus = 0; if (tx_slot_buffer->ul_dci_req.SFN == sfn && tx_slot_buffer->ul_dci_req.Slot == slot) { - nfapi_nr_ul_dci_request_t* p = &tx_slot_buffer->ul_dci_req; - ret_ul_dci->numPdus = p->numPdus; - for (int i = 0; i < p->numPdus; ++i) - ret_ul_dci->ul_dci_pdu_list[i] = p->ul_dci_pdu_list[i]; + copy_ul_dci_request(&tx_slot_buffer->ul_dci_req, ret_ul_dci); tx_slot_buffer->ul_dci_req.SFN = -1; tx_slot_buffer->ul_dci_req.Slot = -1; } diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index e9ff83cf40..6ea1a197e2 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -308,13 +308,21 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, break; case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: { int tx_data_idx = dl_tti_pdu->pdsch_pdu.pdsch_pdu_rel15.pduIndex; - DevAssert(tx_data_idx < TX_req->Number_of_PDUs); - // reuse dlsch variables, as there are multiple very large memory - // buffers - gNB->dlsch[num_pdsch].pdsch_pdu = &dl_tti_pdu->pdsch_pdu; - gNB->dlsch[num_pdsch].pdu = (uint8_t *)TX_req->pdu_list[tx_data_idx].TLVs[0].value.direct; - DevAssert(num_pdsch < gNB->max_nb_pdsch); - num_pdsch++; + if (tx_data_idx < TX_req->Number_of_PDUs && TX_req->pdu_list[tx_data_idx].PDU_index == tx_data_idx) { + // reuse dlsch variables, as there are multiple very large memory + // buffers + gNB->dlsch[num_pdsch].pdsch_pdu = &dl_tti_pdu->pdsch_pdu; + gNB->dlsch[num_pdsch].pdu = (uint8_t *)TX_req->pdu_list[tx_data_idx].TLVs[0].value.direct; + DevAssert(num_pdsch < gNB->max_nb_pdsch); + num_pdsch++; + } else { + LOG_E(NR_PHY, + "%4d.%2d no corresponding tx_data.request for dl_tti.request index %d (out of %d)\n", + frame, + slot, + tx_data_idx, + TX_req->Number_of_PDUs); + } } break; } } From 1368bb5a5c3235bde1576b74d8f86430c8226a09 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 28 Nov 2025 08:26:23 +0100 Subject: [PATCH 273/275] CFRA: handle potential Msg3 Since the parent commit, CFRA is inconditionally marked complete on Msg3 reception (successful or not). This works because the current implementation still sends a UL grant (see parent commit for details). In this commit, handle Msg3 if received in CFRA (notably for TA and power). Note that since we declare RA complete, UE->ra does not exist anymore, and so an intermediate variable is introduced to capture the CFRA flag. The code should be such that if we are in CFRA, we never access any UE->ra variable. --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index c345089696..8347749aae 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -766,6 +766,7 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, // nr_generate_Msg2()). We did not mark RA as complete right away, as the // DLSCH scheduler might schedule in the same slot as Msg2 if RLC has data // (which can only happen in do-ra), so we mark it as complete now. + bool cfra = ra->cfra; if (ra->cfra) { NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; nr_mac_reset_ul_failure(sched_ctrl); @@ -784,11 +785,13 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, } else { LOG_A(NR_MAC, "(rnti 0x%04x) CFRA procedure succeeded!\n", UE->rnti); } - return; // TODO: handle Msg3 in case it has been received? } const int target_snrx10 = mac->pusch_target_snrx10; if (!sdu) { // NACK + if (cfra) // no Msg3 on CFRA, no problem + return; + if (ra->ra_state != nrRA_WAIT_Msg3) return; @@ -818,10 +821,11 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, if (no_sig) { LOG_W(NR_MAC, "MSG3 ULSCH with no signal\n"); - handle_msg3_failed_rx(mac, ra, rnti, mac->ul_bler.harq_round_max); + if (!cfra) + handle_msg3_failed_rx(mac, ra, rnti, mac->ul_bler.harq_round_max); return; } - if (ra->ra_type == RA_2_STEP) { + if (!cfra && ra->ra_type == RA_2_STEP) { // random access pusch with RA-RNTI if (ra->RA_rnti != rnti) { LOG_E(NR_MAC, "expected TC_RNTI %04x to match current RNTI %04x\n", ra->RA_rnti, rnti); @@ -894,6 +898,9 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, return; } + if (cfra) + return; // rest not relevant for CFRA + // UE Contention Resolution Identity // Store the first 48 bits belonging to the uplink CCCH SDU within Msg3 to fill in Msg4 // First byte corresponds to R/LCID MAC sub-header From aa353fdcff257cc65c158e416faa945e62cfd119 Mon Sep 17 00:00:00 2001 From: maki Date: Wed, 3 Dec 2025 13:48:14 +0900 Subject: [PATCH 274/275] dec3 --- .../gnb.sa.band78.106prb.rfsim.2x2.conf | 332 ++++++++++++++++++ docker/Dockerfile.nakaolab.test | 56 +++ flexric/etc/flexric/flexric.conf | 9 + flexric/lib/flexric/libgtp_sm.so | Bin 0 -> 168936 bytes flexric/lib/flexric/libkpm_sm.so | Bin 0 -> 2556040 bytes flexric/lib/flexric/libmac_sm.so | Bin 0 -> 176016 bytes flexric/lib/flexric/libpdcp_sm.so | Bin 0 -> 178120 bytes flexric/lib/flexric/librc_sm.so | Bin 0 -> 3430016 bytes flexric/lib/flexric/librlc_sm.so | Bin 0 -> 170976 bytes flexric/lib/flexric/libslice_sm.so | Bin 0 -> 226528 bytes flexric/lib/flexric/libtc_sm.so | Bin 0 -> 327632 bytes tools/scripts/traffic_runner.sh | 98 ++++++ 12 files changed, 495 insertions(+) create mode 100644 ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.2x2.conf create mode 100644 docker/Dockerfile.nakaolab.test create mode 100644 flexric/etc/flexric/flexric.conf create mode 100644 flexric/lib/flexric/libgtp_sm.so create mode 100644 flexric/lib/flexric/libkpm_sm.so create mode 100644 flexric/lib/flexric/libmac_sm.so create mode 100644 flexric/lib/flexric/libpdcp_sm.so create mode 100644 flexric/lib/flexric/librc_sm.so create mode 100644 flexric/lib/flexric/librlc_sm.so create mode 100644 flexric/lib/flexric/libslice_sm.so create mode 100644 flexric/lib/flexric/libtc_sm.so create mode 100755 tools/scripts/traffic_runner.sh diff --git a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.2x2.conf b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.2x2.conf new file mode 100644 index 0000000000..2550662fa6 --- /dev/null +++ b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.2x2.conf @@ -0,0 +1,332 @@ +Active_gNBs = ("gnb-rfsim"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +e2_agent = { + enabled = "yes"; + near_ric_ip_addr = "192.168.71.21"; + near_ric_port = 36421; + sm_dir = "/usr/local/lib/flexric/"; +} + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gnb-rfsim"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + # plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; sd = 0x1; }) }); + plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1; sd = 0xffffff; }) }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + pdsch_AntennaPorts_XP = 2; + pusch_AntennaPorts = 2; + do_CSIRS = 1; + do_SRS = 1; + min_rxtxtime = 6; + + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 0; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3300.60 MHz + 53*12*30e-3 MHz = 3319.68 + absoluteFrequencySSB = 621312; # 641280 + dl_frequencyBand = 78; + # this is 3300.60 MHz + dl_absoluteFrequencyPointA = 620040; # 640008 + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 12956; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 12956; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 12; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + # ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -70; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + +# Dedicated Serving Cell Configuration +servingCellConfigDedicated = ({ + # BWP-Downlink + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + dl_bwp-Id_2 = 2; + dl_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + dl_bwp-Id_3 = 3; + dl_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp3_subcarrierSpacing = 1; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + # UplinkConfig + # BWP-Uplink + # BWP 1 Configuration + ul_bwp-Id_1 = 1; + ul_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + ul_bwp-Id_2 = 2; + ul_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + ul_bwp-Id_3 = 3; + ul_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp3_subcarrierSpacing = 1; + + firstActiveUplinkBWP-Id = 1; #BWP-Id + } +); + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ({ipv4 = "192.168.71.132";}); + + NETWORK_INTERFACES : + { + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.140"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.140"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + #pusch_TargetSNRx10 = 150; + #pucch_TargetSNRx10 = 200; + ulsch_max_frame_inactivity = 0; + dl_max_mcs = 16; # there are retransmissions if more +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; + #pucch0_dtx_threshold = 120; +} +); + +RUs = ( +{ + local_rf = "yes" + nb_tx = 2 + nb_rx = 2 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + eNB_instances = [0]; + ##beamforming 1x2 matrix: 1 layer x 2 antennas + bf_weights = [0x00007fff, 0x0000]; + #clock_src = "internal"; + # sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal" + sdr_addrs = "serial=XXXXXXX"; +} +); + +THREAD_STRUCT = ( +{ + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_SINGLE_THREAD"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; +} +); + +rfsimulator : +{ + serveraddr = "server"; +}; + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : +{ + global_log_level ="info"; + hw_log_level ="info"; + phy_log_level ="info"; + mac_log_level ="info"; + rlc_log_level ="info"; + pdcp_log_level ="info"; + rrc_log_level ="info"; + ngap_log_level ="debug"; + f1ap_log_level ="debug"; +}; diff --git a/docker/Dockerfile.nakaolab.test b/docker/Dockerfile.nakaolab.test new file mode 100644 index 0000000000..1ac5af2768 --- /dev/null +++ b/docker/Dockerfile.nakaolab.test @@ -0,0 +1,56 @@ +FROM ran-base-temp:latest AS ran-build +ARG BUILD_OPTION + +RUN rm -Rf /oai-ran +WORKDIR /oai-ran +COPY . . + +# build all targets so final targets can be created afterwards +RUN /bin/sh oaienv && \ + cd cmake_targets && \ + mkdir -p log && \ + ./build_oai -c --ninja \ + --gNB --RU --nrUE -w SIMU\ + --build-lib "telnetsrv enbscope uescope nrscope" \ + -w USRP -t Ethernet \ + --build-e2 --cmake-opt -DKPM_VERSION=KPM_V2_03 --cmake-opt -DE2AP_VERSION=E2AP_V2 \ + --noavx512 \ + --build-tool-opt -k10 \ + --cmake-opt -DCMAKE_C_FLAGS="-Werror" --cmake-opt -DCMAKE_CXX_FLAGS="-Werror" $BUILD_OPTION && \ + echo "---- ldd on executables ----" && \ + ldd ran_build/build/*softmodem* ran_build/build/nr-cuup ran_build/build/oairu && \ + echo "---- ldd on shared libraries ----" && \ + ldd ran_build/build/*.so + +RUN set -eux; \ + apt-get update; \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + ca-certificates curl wget iproute2 iputils-ping dnsutils jq \ + iperf iperf3 ffmpeg gdb\ + nghttp2-client || true; \ + if ! command -v h2load >/dev/null 2>&1; then \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends nghttp2 || true; \ + fi; \ + # Try to install sipp from apt; if unavailable, build from source + if apt-cache show sipp >/dev/null 2>&1; then \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends sipp; \ + else \ + echo "sipp not in apt, building from source..."; \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + build-essential cmake git libncurses5-dev libpcap-dev libssl-dev libpcre3-dev; \ + git clone --depth=1 https://github.com/SIPp/sipp.git /tmp/sipp; \ + cd /tmp/sipp; cmake -DENABLE_SSL=1 -DENABLE_PCAP=1 -DCMAKE_BUILD_TYPE=Release .; \ + make -j"$(nproc)"; make install; strip /usr/local/bin/sipp || true; \ + rm -rf /tmp/sipp; \ + fi; \ + rm -rf /var/lib/apt/lists/* + +# Traffic runner +COPY tools/scripts/traffic_runner.sh /usr/local/bin/traffic_runner +RUN chmod +x /usr/local/bin/traffic_runner + +COPY flexric/etc/flexric /usr/local/etc/flexric/ +COPY flexric/lib/flexric /usr/local/lib/flexric/ +RUN echo "/usr/local/lib/flexric" > /etc/ld.so.conf.d/flexric.conf && ldconfig + +ENTRYPOINT ["/bin/bash"] \ No newline at end of file diff --git a/flexric/etc/flexric/flexric.conf b/flexric/etc/flexric/flexric.conf new file mode 100644 index 0000000000..fda5bb4a7c --- /dev/null +++ b/flexric/etc/flexric/flexric.conf @@ -0,0 +1,9 @@ +[NEAR-RIC] +NEAR_RIC_IP = 192.168.71.21 +NEAR_RIC_PORT = 36422 + +[XAPP] +DB_DIR = /tmp/ + +[LOG] +LEVEL = debug diff --git a/flexric/lib/flexric/libgtp_sm.so b/flexric/lib/flexric/libgtp_sm.so new file mode 100644 index 0000000000000000000000000000000000000000..1cbd2f13b62f855b01e4b22337a867c8caaacd2f GIT binary patch literal 168936 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 literal 0 HcmV?d00001 diff --git a/flexric/lib/flexric/libkpm_sm.so b/flexric/lib/flexric/libkpm_sm.so new file mode 100644 index 0000000000000000000000000000000000000000..562a9f801d4a1824fa06f30fc23983d8acd1f9c2 GIT binary patch literal 2556040 zcmeFadz>Rh)&JkSun-j3fPg^|8sriLmf>m;gyu5XJaUP^YCvGK?1k*ZUgGWmL0Jth ziy6n!5m|@|7!Wmh;Td@Z!^ov)Q6?Z_K*flN0TFv-(Gd_Mp!==r`kbEhhi(0Sf4_fO zUPE%;Id$rsQ>Ut{E1AqE`%gJ(=8PGZ{M*br#+sHa%fSnYzwaX|ie;>~s{ij}?H%ZD z9=`I%CynilHF>m>Mn}_G`l*fH=J7ov|HxG#_9Rm;*o>YuuC=M9gxdQJbcga2C=w`-yu zKkfy6dDMOAe`o$-VB!Cl{<3JdaC_qcKR&yMu`iF(-v?E@EzL{+SN}&ktuxSH+&C29 zX3WVW(i`Mb+t5|<{?q1KDm|X|Wiz85yuJfu{rczrvf+dW?|$cgk9^>OW1m=d-{If> z$(`%8>UQd9Z}p?w>WIr}_gw?d4#y|=O3t*mvUaq#Z*BJ9wd3~Ms=S3&F0X&9)=jIQ z{nXF?>gPc9)2Dt8RX<0lpZBVt_o<&_)X%Z%=Q#Cqg8Dg8{pf%F>gSY2Kls5bne&d_ z`4g=dzI5#~FKm7LAD+D|eUsaD{?~om&p)^CcOJ~H_}JM$c5k}msPR|s|5xhOkN(O% z=$Q*v+;-NnpSbPSffcJiGwD9}L1*7NpT7QmdmgrIk8l3^& z_FnXbJ-)vBYkT~8_J{tl@3nXAF}3~1`{y=ZIjaBpIrkm;)_2@^-J*M62%Oo({~l3n z`%UnDRAM*9m&NeU#Be=+_9pEAQn5|hpBuw(jp3_f`W=Yjx`u4^!~Z@MqdzZ(FHiWBZGq`;mGmAojX-;o&pfNHZTfA)^i-ywz{6~hZL_D5s*U<^N9+1GQB z{~Z?7@9k=$ZYr*Om4Qw1gJSG_IEIhJ*jW>!f1Ki*^52cop9uK2)-_+(E0<+`*F0F) z1^sSoz4drKLoDkW^I+Yt9=Er)wN7ao#qWUs7W{(`8GaeA57)x}L7y;uiCM<2UDWH^ z&N~+yzN^yH|1#iLFEM-%*f~AO^S0K`UpD*`dVy5`9)SMROAY@j{Fw{B^-{yX2>ws_ zGiS)~dthe~>`&cj_#NiKS_wPdWd>{pe>&g`FEe}t?Ef8km|AK0p2kh+el1Bb%ScVYjp zR~h{x`h5cWSB@KgA^g|H_w`P@>!?OTexWDq6?R-8d$XQtW!@|x?=gFv*us0bdh;& z)33~1P?|q)@sj29moE93Qk*w`)v86x-s1U7mRgHfF49Q#u;8M33knw*x@`VMi{@Qa zTsCjjvUw|)EU*^LU%GU~0&Cf#WebXzS-r2$zi`p=0Y$>smlds511pylY~ptuVwtpzKJm#tcI;c{!q(iIn8plf+x#X@W8!bPQ`wR~VjVQE;G zOXpv(XsL>K@rp=wESo>Dps-~5h4U7yP}R^|$ODU34g4p4UH;SA)>CBJqWP=lEu23v zKVmDOLEk|)7cYvus>CG==Uu20GOxI3<&qUqKc?IEdN)nq;P?egmd{hRE?lH1VHkhV zVEQGM^<`FJV8!yK*3u;xESs}x#hfGbvjyQZv!E!^djBaWAAiEUL*^VZ=kW0P!H0!U z4^zwW%;3MxtQq06>E|;;YP4pmI~92*kF!8#DBb^m|5-u%hFZI2UwrX9mZ&SnX4bD2 z)Bk3u=j-vhmaqB3wd!_wmh}g)9hNNFM&C_MK{oAYhVA7(8`~0VByBEKRos6I&w`H< zx4Qc7Tit&X-yXa}d@pbd_v>Q6CVIp((9aV0zzf9h2QL$U0K7{4LGV%H4};6Og82C} z@DAxe25voNG)3PLJ>nhcXNl+jVe*+L?m#XO&qIHdc;_`^$0J^Y+$8QpKl!_;|FwS^ zJ2r6(a+-J@`bFZ&8Rj&iOgs;{LOcb1kGR^RIJyTng~oe|>m z!Ry2?6#t2rAvcK^p>I9CX+Le^Q)r(i{#n?`6PI|)#Iukq#IG0siHn^M@lnV=@$bM+ z-!sv;oLx*kxWrEa&l9&H7l>z}Un8E`)7T#+ek{00{3P%e@l(M&#LohsBK{F@>-W)k zF91&xUjl9uFM{_GzXaSNelU2JcnLgD{1f0s;#Y!~iC+aiLj1GfHR4x;j}re9xJSGO z-Xi`@@DB0Yz^91c0d74T&HvrtN#gf|+r%FN?<4*wxI_F`;925Nf#-?;0lY|j19+ME zOW-5KUj?`R5RG@nyG;G(pNsM>!OO(A1|KEv{g<)RB>o_Hhxo6+yTpGFp8R9fpBKT? z#5enZ>DM8i0M8NM2E0goNAL>qcY@c5?+xA{ejvCeu^2d>%_C- zKXI{RJs=K`XoicH;QzKr4TqnNy znWp}&7o&L=xlBC$RdZiZiQyx}?SC4(RpNWWPCbSlYgd}@Q?S>iAK!SDj{T+$ra6p0W1+4Nf{Uh@qfCBDz|hIfePZ#Mb#WB3$tiPL^1 znuo8?H*WxBh}&piBYyp@rr&xDA0=+xXvVQYyb3$b7~UrCK<*I#+_A?0)Ze3VS+5z{ zCGMf$0&&r=5`P5x4dSBj6Mqu=RyXQT-?uP-V|bFdJ=5eZMZ639=@{-1e`n3)$0csT z?;P&k-L1ZxgR!ok^gMBrfaQX5OWV*S>1zSC05r(3koW zI}Uh(^o19RmuDFJRpNR0T_Zk%H>B&tJ?J->Lv9hb;ip{x#ecc3rLiv)zU%u<{`-jE zdm_e#coWyPEb({abAA=#_djZ`k0Zp7c*5{1@%bMx{d&aDLR?Ma^T1ogCH^+?EcE>t zZvD$-M)I@o@y4GNaoOkOiGTPuQ*Q<0=YkiB&j&9NUkqL*ei3+ucoBSr_{HE=;vWOA z5&tB3o%ogDqr?Zn8^k{c?h*eYc$0V)yhZ#5@HX+A!8^ovJI9QdPyGCk7(PXO4fGQm zqvL+iJfoi^{!MU~cIaaCgY2yuzON_-S{>M`6SF8jco zW!5dJf4gqv3iJQLK9G3lP@~@_zAyAU#Pi^O44)z{>uL(?tN7VLJT`GzSJT8D*v&A9 zoMjF_rGM#nLVsXN{i~4vVfaAU2=US-M!!z{$Eyt=C7!s-aG&^IiwvJ4zIut_UE;fa z+;9umbMgNJ_~4Q3cZ7fSMx&o5{p#(8_Yr^mCc|CgbMG=dOZ-pk4X+Tt;OVbhx&$35#RjVhNm}+&X@gvW_Ta*+rDOa zhWMJN4bKx7{|m&$|040iZ;YJ^aq)kI_`CmN^qa)R{}%C+9y0oE;^Kd4^JpHtQ;b|^ z-Z1M%lX(6#qu(Vy0ex#$)V_6>v6G15N#ZhZGsG?Q=Ma~5!zG@FevUchBJmFVZxF9l zO*}qvTjJV+;>G-z9z*^ix|!?ORDByTl)Zeu22Nozbrne;)b`;^pm)zE6DfF*C2Nw?zFZ zf+u2ll6W}ZiCc)@As)_m;(6%jn7`HdS0e7*Z+M;fApCC(+vvAWyawJS-nqi$Gre`R9y-u>iI-iB3-K4A zUnMU4(I)X%q3;uy^(BS*C*!i^4^7?5ycAvrPm{jzKH@T78RBW!abtL%xQthUcnA3{ z5idio5O>gTmADPwCT{%^Rn^!cIDdJH#c=E^!Nf=ZNPa7l@b9Z;5yTyg}SrV)E}3??@hSeUm&$ z-DZft1bw+4ioSD?sau!yg=dLNK6Au3hn+$UFB6x1R*2USXO%hRI&mBQc8PaZnY^WO zJ(qaP5-)MdTZ#A=pfCFZvC{#skiPH{;*z&2@mpZ09>YE2lD8)D4&rY!hwKyg&~No^ zG~ahJa+7%SUQ;I>;tseU!>5SL{wK9Vv|k%~HgVbiq=}cIpCMj@><}-)PdA2ViOYE9 zi05FZ5W~yFWxOiHJCLiyogGbHMv3>K-!5?(k2Kmzeag7F#PhJ@V4OvN^ggTuF+59L z-apL|uft9uhL?z!;a{0}7QeAwC0>U88u1QzJ%*1Gm+@^7_h6?P!#l)fe0}29+s!z3 ziRZync$EChc%+GUke@!{2hK3>Q@F%s91Fx-h%1$f&b$0OjqDOndZr%o#2xTL3@;Lw z`CcVngMT&RGT-aOEyxYx_RhwCk9ZM&He+~;xYR?N__?s-$8Zb%%Q#9sB#7rBr-*xy z`-u0U-x6^dk2-M~mnQK%>^Sd;j{B{%P(Q?<0W*1o0v8 zJn?UDW$YJ-cfc#ey~j;`R*8>*w~1TN!oOXkex3~dG;#0yMlVC$Li`T##^%QV9PzVZ zzeHT@SBU3fe}wopTOj|$FM|Chak1ZK4*MPAi!hE|;+MdF3hRW_x!6w=w~_Zg;$s+h zm-rR1FYA!lPyWh`Z-MlM7m3UKEfKH5ekF$2h|Bz~6Za5TgLoM{vs<*TygiIuAnqWp z3i15AjDCaoInZwt&+mo)c8~Tu0DYTy3p^de`-scBog?lc{ycG6w+qB=_+292fm|kT zwavI!V)zJg8TTsj4T!fM!#(0M?oHyhZSvbDUIw2cUPHebye{=@L$5$w#-&1h3U=DW zy^mqMQD32bxAg{ z{Tk~e=?gCsm;9HAZ-D(u46hNF{MU)+5od$A2f0byg8yyezT`*NIegyCd(Gt2C7y?! za9xD{h~$U#g%^p3`5`_4JCzt-BOd05xCOaE+=JXCUPix-eWLYm-DhN z{Soj)3{MjGW}5aX;uY9Q$M6hsYj0!6AujJXxx{OTCr7*pULxKZH}>nqB@Z6)2K;Zv z@D_2&L!0<0?D#RfOFWM}Sn24vNFEZz^Jt$UUPc}=%ztR?7Kr~8epZM}eN>5i@Uuqz z;kTOoXM?!ZRhziv-6w9t|0&{QTO0d{{i6Ai@v@2c{l?UJI)?WVmwBBb?jt{L49^pn zd0ilGBc2lRGUN(z3;tJ$ci>NB|7afMbB9glXx}0}V_TC?pZFF}n!F_r*tGo=ar@my z&nEu1*~Wf`Iqc_%i~j}UW!Ntg-#cmSSBUQp`*q@Czd^hM`yTQ4Y-jAZi5~*{9pW`` zKZZ{cm;H0@;I|o45nHL;Q)ip-v8p#w+#a6Sp8w z5ud%Iv6JYF+W80kw29ll!}=D(`-n@u<%s9uN1nLUTYB)jvK>s#69?zCvNR*^41~V=wO|R z;ZwwIeBQlF{QIzzI5e6E;VI%}w6}>L{tk>garvBlj<|($zY6gV{HZfXzir|#Kwmz0 zEca{9)5s6$3!fq``<5aoyN~D z@u}~beoIG0^B})(S!XW(5U;`iF7dxYKlPrdeYvi=#5Y1ePdxj3Gwy{LUL-EpsVZ>` z{?&-fb*fH05B&!7BTYPQ;tuke$VB5k8~&$>*HE81;{Of(5^-nL*sT*^4gDr@$zzwe zkah*!a#_eRI7 zgFLt~JWE{WZ;tqruv3WPCE^|US0?_$-OT*05|{aF9ToNe`afVCV|bFdT*p(ytFV)f z;ThsJ_~#J+%kIYiHgTzcpLibf6!E?GFm@70N8>&JITNo5AipcLvAuhzo}!Q`H{T2%>Rh%3vnCw16AVNLcd1b0k6mKQR0%f2JsZ^ zG-G&&xa7?zZb9xcKNfkBN1S8Z=r>1P@>U_9mw1Vn-*4=yCN6nP6L(-Y!yK|pyo`RU#3gS{;-17y+`7g1ojNY+|4*TB6SsU*x9J$( zM_lrjA>M|4H-_hlOWq2^%ZR7M9CC$t2mNM_kH#zYQ6O$3-U@TrZ4loJ`X2E-cr%8# zh|7MmOWZ@h)(KJnWj~o9ZlisQIpjX#j`&aflu0x0%^2PyF4y-qaTj*{7~UnGhish~ zjd!QL%yl_M+=l%!@l*a}{IA6D5#r*1mADH#^%&kDZbSBnKMVic#BJDj`lIn4@VxQg zjp141@_jxz;%V3^#PAaF4*V+#ONAG9s6o@|nKjr!(_B-Ge(ic8LT;_X&xCehc;xgZx z#BIoJ;vL9S#9QLO8y&|DFJQjJ@DbwSd?D_`PCbS_AJjnM-C5eBbYy5GE%lB_&iOctIUop>H`Hi&zW zo5b@H*XhwbxKpNn3NgG$T&@=-;wQpRC5BgtTaatS-2=_KD*Fq`fB60#>4)#%5f9(L zBObngM?8H0E*s6y>N&=rKH?7^VR(jk`2HR7g_7qOUL-E@mx$*j&oR74T;9KH6PNp@ z#0R7Cc3w91kR~qs+8ps0pr0ph{|$8&!;8#k8b3NU@i&-5ZW6cP zf9VX$&xed$C+q0R7aNWPh%aUE(K6K8Z`-tHe)+euH@D!^XZ({1oUX z&WieDf5gZc;-^DDN8CQ!=vRoJ0sR{B7I-~|j}n*tLz}pT{C0@T{=p}nhklp1h5RS+ zDC5d-c$f#`ImrX&f#gBBO+3s4@iOdYh&zy7;(5sf zbGcpyp^1 z!Ot}53-2Q?^CCn1v#{gF@I3Kwe2IIIOT=yL2S$kJ(Qli0I38#x@yfWQiI2ceiMX6= z*NKlpU-l_tzXRSRec>(QlFv5rAHj|v!>#k8^(^^J5YMB1int9qO}r&>5zltbI?#;a zE#lS<#=kak7k2y@-X)%gY@HvCSN7*A;_`RkGQ?{)n)U_a=fa-~aS#1Ah)W(k;x)-n z3~vz+$DR0qy2;(5qc zJ{oU0?!>L9%nZyB_pZjc6Hg+pGVwm}N(>(%F5_4wzANn1W4K3L#<5A*p%FGD{=ya-++ zUVuOK7(Pl|*0Bchg|O3%;cem$N&L_nm1>WkzL|t_){P*@mGm|5&AXa z9m!7&A0;mJ-ynXAs|u+ zmg~FqbJK5z_*rLvT zLOcik8u2`MJ%*1Gmvy5-{9M>+#_%@r4&vz$mvzG@?m+Gm?}MDWFq#J$cb9k>ein#_ z^N#qikkImD%|T;et8=ZKGhmx#;v8P=J!O%E5YMB1int9qO+1T!+r-21UK;JU1bv%$89W`s`-scD$Pm9AcH9`A zBc6xfdEzoJ3dB9=mxzyocZqw*XL?yQ57)p?hxqm4e+E3p}+v8gHj)WS98&;b)%s-Qs@?FA|qLl!!NArxL@f#A}GBMqKhxCvHJ* z5TAmb@``94y5dg^A0aOL_A2oy*r~_x25}GmdBkPk-X<=8AH7Rl;z?sX<+>(uxx}Tu zOT;&S-CP&S#2xTT3?Csb`^*M$8-9DlWuMt3-hqCbxbsmnep0^@ZxMd_q%VAmxUAb< z;#t^9pdYa>+$Jv9uQc&8{xVON4(ic8O zT-LoV@rPk2F%b1fc#61<_BL@@_tM16(9aMrg6D|K-*KoAxBh9?fjV)yA8Qle3i@5* z@^=K&7f0jW3HlE4BDfpFv&5y|a>UcHQ;6YZ;!^TmzfS>j>c689zE7+xkG z)-7=haaM`fAlHdo@V`O41AlUtMe`%^SBU3eN50QY>NXEvCw<|g#KXEJelF}ZV|bf* z2k~@>hjmNbf!rnD2f6gIXdcR!8@W!r1V80_;3N-a@t^dCw}?v~+Qcu19Y2P5iCd7Z zk4OENJS2$cp`Rk21uqkCz@JJCA0Zy@pNWscPCbSS$f%zii|Jaa;00y!Iud-ypsp^xMQ`Uy=AkwBJ7H+r&q}(=oh{xa2=W+=U%C zhUbY({tLuy#8V<(hFl?@N556#4tVC1(L6}}1>)ad z;vVAhh|9XvByK})6Hh|!5|@2Nx*W|<2Y$ND@xES(_@v~GxNU72oKRO{_y}{HmsW{) zC2uj@BQAB-B;JwyFo*0Dx8Q&EifDey&}$Mu4EpkYv9i7$4epb^@G0U_KV9M(*hzdU z>W}agaU1Pz;!;0p;$`S(h!??Ym1w`|8D@OzF?^J`T-O@JZP;nX@HTM={&k4Ub*)Rh zjO&^;81>&lzlj*0Brg7^h-Y9Y9m6xkYw+J8F8=3;ci?}XcozK@VtA3b_+KLK!cHZI zSBYC|Ode~*#s3Czr(NhnFTsuwF&6mbv!+r+Er zHyy+Kh>QOj;v=x*#_$|*5B}$gi~l9!_R}W+W#V=8TZ!Q##Kr$A@fz&ZV|atOeWl5} zM_l}G6EDO64)F&1^<(%Haq+)Pd=z#PL(zH=o+4g`|2A>)KSR6&|8vA;J*^NA*Hz;3 z`(Cp7dv}iWr_GtpkF24xzpHf5`PT(K5-lRso|(Uk3&C0 zT=WaXpMZXaxac>CKMnmhars=LPrQTiog#kDO7r_uiO)s-_hCOxTgqUev`P^Zxgq!GX8gn`>@|7zS&H(Pe^?}nl}sU zvP)d9%LU@%PlLXP263_9B<{g}i}v5a7T$j7VZLFK=FGS;#>!3?qt~Vv(a^0yC zm-W3#JY46AOFp~A%gCp7b=1#gC6mt-@%@p{3~{mV67Rr%miXzH8T$p|hrxb@xY(}} zcd&o05kL52#(smi3;XhUI=S!2Z*JyYlk|nRh|7I_o45!2ehjz17|pZXe*d_o{)MZo+K`x2gne2 zkS~Y0d>+6hZo$7C@jT=r@tWkn8qL3)PrAgdElgb%h|Bq8mH1ZBmvbhWj}Ca9^o5TS zm;AJe*AP#Kxa7wtZb9xc$N6d!kK(5-{u57ZiTM@7M~R2?i}>cS(~RM5;tu2v@$*-k z`gd@yEdFm<#=IbYG`Nhj=v&)hd`Vw;k+^+_shbk<*S9uyDlxoD+=5&qzWpbS|MI;T z;^&XxXZkDAx)t6>T)w|FL)?QMH-_h!!@oT7A0eM5;`05a@;x8oe|cMzAD{GvPZ1aY zyTmVtoy7IgJP1z_FQdIpynBVobB4J1Un9PBHtIiyj}i~-pLhXwnlZdh+=1L7e!v>z zf0wue`*Pne`9J83Mn7>wG;dE|V|bGII_Rf~-}q&t-$(q`VZ#f=UxlAV;_vvh(JvAI z;B|)Ah?gb*F?^J`#NQx(x#T~FcZkdRhK%PM@`-(f@Gzgm!+a7C^GST+^~fjj;m;dh zB>t{1AfLp;d=jrpK4bVOaf!b{{2Iw;4DS#R^NIb4Z(HyU0fetgaF67ipW-Ei4YiT{^xkM)7{g^v=qAUBAYV5b?wJH#cQ z`C4>dFWtfTQ;6Y3;xfJ^;sw~L#PBL{3v!M4v$vYOHHbT~FW(a&`8;JutzeKzP z`(@&T-!=BD#CL-I263_9B<|d7;%pKB%5BEJPkcYvm+!lkaV&x-Zi(hic#^p6do#pq z@W&x8`(Bs01^;ry^N@?g9r6F`Q9n<4yBU{63{Mi5d6XjV!cIDdXNcF}pF@0$QIr21 zaU1sA#A{f8ed2k@Q^bD(`-yKv^N>iHJlMqhz|%3jkGPz-WQZqW$Bp56;&R?nAnqZa z67e$R3UOQF`eroVmnE(k-bdWV-=oP8pOUy@c#gOS|MJAQy4{R>iMaednksR5-=s<0 z-r0cIZ^rNzamjO=__?s-$8hUg(L6|= z6U1$_Pces_CZ0#XZQ@~^-;VZs4fJi|*Mp~Hcpvfb+>&?|cH9`ABc6xfdE()@C2 zCE}ytRpPRqHi>)i(a9xrW!SIB zaF4juTa$Ph@wbV0Ap68E_}?WyCGjF3vOhUj;*H@W#O1!SN<0TU^%&kDZbSBn%YMF1 zJpYXG*tsn_?tATO#>m!k$q~0OnjR=&3afPKGrb2PW+2^89qvU zckm|hz3(>qE#j3Q8lJ$pwT$oaKQKH={AKVI@#SMiU(Ub9PM_qN^o3`MOZ++F2S}b{ zc$v7=Lyfq6|8|}D7yn}XA0;l|zuhFB*>3aTq`pNwX&IgvjnF!@LoH;%7#`Mf{AG;fe3XV{V}|sG9dXR{j0#U#3lY5@uK82hL?$-evk1-&ShnMr4AFfN8^?6flm^b ze5Q%Z_rUiNm+yfu5SKbE5|@0Ih|BlD*O=R8{j3w84?ap<;%^YoLcbZqJH%yPcZtjS zv-Q8xd`_%1e-|}D{IJUmPZBR(Vt60%#8rkDiT`Z}GvDRDLB=<;kI7Hs`%!;{Cy7gb zQp68~opcP(5U;^MhxpI;G=AoYOWtb4x7^F<*NGnk?hzl^-^AOD;Vt6weo&iu1$O)x z-X)%gY~lSS$^SC=Dg6kSd6y=AS^xWp%e-@m%le-sF6)1VxXil|;xg~5#AV*e=hVc1 z`#_WD^qtYV65dB#_AMFWyTXne!*j$v_?IXC&V5XsbclCg-;d!_#Kq4p@h4#?@q=i* z!c)Y{Xm1lgV_)NEgSdQ-(Pusf^S2T0SH>eld`sv%#2s)qhG&V(IbMml1OLjzhM!!OQ19*q{&U1{uPy96S#8}iH`&ctB zDdI`+9C2Bv3dB2zzexO?cf)_;jblu|b>d>bLEJ$+9`U=7w>I%dV82UT>?eL0%|i|L zlf-v95aUJs_pt90mvfsE@z=a^niFgP8m5D!&Iy&~Jmd1KuKj2Do)kG!L?$ zPY~bLHT9Du{>u#Nium298Qw?yJs&WRIzh8Kxn1YROuIN9jem?N%w3?C&f@i&Ms zgq>y#?+};$XX@T)KAroG>=N%BZ|beUe7(`H5`PW)4dS)?jJ{8NrxT2Q3$M#Qp$MLc z;Ys2$PgBGXgPn8?cZkb8b%|T>J4ZYZxj@`OzY@2^H3D8Iec_|TWna-C{#n>*#_$gD zaNNC^aVK7b{Q~ihj63l=px+=a@%zLdf_`E=>W}jt%N+fB#J4`ftV>Pe$AQZ@ zi=Ws3m&uP$`ogD(%Y93icolXMSZ~C>@D%Yf+S|n6{z2ovtXpDd6!u-x7oH_9^D9UE zJFruT;br17zpNic>v`Yn(d4zZ#{Wgi$!1K*$J(Ny0^P&*Li^OFgULrmKJCzt- zC2m2k5kK%OvmQ2x%RW5wVAOwk|FJ;4EdCI;4>9c<#MeOIBkqAWV|a_WoEvnBdx*z+ zDC)n=iv)2S?NiJl_Ytqb&o=S!{kES(`|UtK&HONbfgY<1+S34@DbwPOk=-FJOw-T7~UZ6As&yoeEy_K+=kpHo`jr3+!B{u z-wVV$&@U37bGFH6g?JZnNnXXi{Xx_-=?foaj=VL9Zw))m7~UZs&R^mVy;#AQF+AbzF96~o)a9mpNxvLEgeFHf40NIe#ki zAs2{u&~J&j2X42cab*y1I)?WVm-@*N?}HsThUbXa;9s7&)K7_c2lZ1X?xNpH3?Csb z{#S`Ruv3rW4dT`>OddVr;(wdC^Cweb9pZWP>&NgZ;^Kdocn)?FzmATt@D%Yp{I`jV z{~6*n`0o%ep8MoI1qC|1R-7{I{Nnj&B|PCSrJ!xcHwUUW1)<49^fR!+(dk_@5*0!T&sS z^jnDGMdIRriFgBcDlxoDyaWGh#Kr#xaeITQACGt&{WfEGi@5mTCf zuznM*2k}2eybS+s;y(IK$M8Pl;(vyC2X@>To+Dm^|9Rr#e~EYp{+EecXPWDEC5DeM zzrp0IO1umI>oL4R-1?QV;}IAC+r*t0u>KQI!hb)8PZ5{=cZnxpCxQKx>CLi)r-B{C%XPg%T(0kR;&L5r6EDG^E^)bTrJsuWDc3KTxP|qiL|m>HRpPQP zH;H@j$7c@vsozEYk@YY`T-Kceaan&V#ATj0h|7F$6Yt1;!1zmj%KYjhZliA7#N|FT z@pLq<4)oK+ML$P;68a_L<&DO(I`NmF-y|--7uhBLPw1zfiTYW)-N-KSEzdIRWP!MR z9;QlsJLorv%kLZe#CL(d+;2(#N5K=g4`ZGrF6%>vcpl^A5SR7ACGJ5#M?4R?NZb+s zpN;0TI2ZSWF+53J&cRZ|m%>guhG&S^;GaW$;v?qVBS&1$!P>+-e?~lih{jbGe~8!q zX7qBz2ccggo`2cs*NLx#ev`P}HTqrRw?IGjT+~ndA4YbGk3zpd+Cx>`g&%{0G=ZNPa7m0TyUgGk7B0llF z#N|ih%AaQB4Dl7v&k^q&X!I+@FM)oYcx{f+ZxjDC^t;66@7Jd>ZZa-6Ltn;Ic=E$$ zyj;>3o+U2lIXU8Y!+s%#mx;@HPK9_4d9N~uTqo|K-v;qf$q#YKfBH|+akM25#3lbF z;s-;&N?h{aBz_9?ed6U4jUOq@OBt85p`Rh{{MG0eh+hQ#3UT>7VuScv=*xN_{n|NG z4^7e+-eNuz^+~)Y@y2j#Lp1+V4+-KOj9-el4LMERle`fh0WT5vUN(`}i93=9;@&C7 z?=JDvp`Uso8dv@$BfG@sLcc&<#ra=sFY*4x^e^=++$JvdmL{IZ zxMqkukX_;y{Lc|@!JjH|?*!vtllTbibclPBpBO$xJj@UAI_xB-qWKYS6A$x4ybQY; z;tph&_$d1A5|=pBSkGj7 z{VMSmc!RjC|2}c+95XMh7o+3V2cC%GN#aruDdIc8PCAA=#HAiw;x+i4Bc6v`Aa0}I z6xKxbG}2JtD_X%mZeZJll(A;++>b^+r(?& zDcqlhdBgt7C2oDh%&!7*$!C@L_RyF6Ke3+&uamy;QR0%%26NbH#_$et$)``;g4|{P z3f2*M#P!!fzbWFog6D|KeyKvdEO{U<`=vJV&qKdUT)rPI{rBj)w+{L)aoNw5h<^+E zRpRz>#%`1Nz0miG%YG(>`*IogpF&^m*M*nQHFcFHec^q?r5-ZGE!c5mc%Hb_LxH%1 z{FaE9Ayyn=so+lpWhq#A$ zO2o^ME5t|9Z<}~{4)SWWZaWe$ac>W!mm~fM=;w*s=V85y;YH@y|CESN!G0x%*N98r z>cl<7-(U{8N!)_}rPreQkvQwbGtl>lJK)V2-XiYF{VDOIVaJc*UE&sG>-DJra@|i5 z&qF^&JPTg`iy?cser`q>_SU=Ppra9OkT%lRH|D!?=<^zgUz*eUk#1A2I=hwsp zd-&UWc&CRS+{67I{+=E_)x(`0-tFP2f$ z=^mc$;YaoGz8=0)56|@QxA$pgt;9zNQ`Pw3%|9&Yz= zuZQo^!<#+4zlXPa_?|ty-NX0l;hi3SNDqJW>6;mNGXrmC;LQxYnSnPm@MZ?y%)px& zcrycUX5h^XyqSUjmkhi#d$(8JH5(G{(5x^0Y)i{s>kZ7@*mBqWC~<#aX5--xDZFv- zht>aQ@9tPyzo1{4d}iaujf+PDSsxruJ~l1u1H;Mn)3QDgoVA2YKJb}bIW6mhp2-WQWqn{XdB(J?4{9clpO*DO@Z=%WvOYMO+;dvi z2PTu-Ps{qCWOC-TtPe;gU;JknzpdqGre%HbG5OfEtPeIO*H6pu((>)ovOeILym?yI z2a}UmPs{sg`BT%fKB$^pIW6mhw8;ymWqqJJdB(J?4ruPs{qib#msktPch!Uwl1`Umqw>J~J)rgUQLqre%FlGr4|R)(5+jw@=IZfOhic zX;~kLOx1&iPfg4EfOB%?w5$*GCNG$lkJR!R)3QF`oIHM7)(4D}hfK@*plx!` zP~N!roj=*qT0DDqblCL89 z6C__k@^X?FlYBnOA0qiQl20P}7?O`5c@D|@kh~koJCVFC$y<>8uZN=J`wGc_Ciyv% zpCb8jk{==Y0g~?}`A(8=Bl#O7-$?ScB!7> zQaB_gt^pjwXrjM_p(|I zto!k(^tTH151*+Y_WPGRIOUH2`B--LkE zsu%T>xvHJooFv`B)m?YZ>aKNhS_N{lJ4+cHAJbPYCBFClT6{n~yDE4#;WtzV?ofZ% z9dc57L(>nzRzhH5OIWath3-jW;e)VnNw0;$3I8OuR>DZ4*J4sE-Vrn$AJbPd<;gcf zi`&8CUegv6uCn;Dl1CoAKR(Jeu&A$9%Hmm}#ai%e!hh^l@hRA~o!H}F5ajVNpf6s^ zz}rFtBQWrV9s|mw3#L8PVTD=KDGUt$HE1_JrmtSg;QD^a+8HqDga#coWD|aJ+M-T< zXfZ7ozY2@``lT!uLyN}Q3IEUkkQg0XTfA5G@NcuW3>F<@aktRoFN0?j{`F!}S^K2g z;rv@`$HLl4##-w{iFj97J58(&ZF&s;73htRooozzJ~Xg33~bY5Kza03D5?zhWGozm z@510|#^5ob!AF8;6aL-ZFk`_Od{$Zfw=sSk7Uvp^8%~fIGqCvI(-wo;Kfx554mc=_ z_rgHV82EN*U^xuz7aCCWbi#jCiT~E4tzdATF?ddB@EgIi3BUb!iE-0eZw4KYk6mC4 z>>L_+2?nm~F`zsu{3nmDgTenc2AjuAj&^{-lX?uQVSB?|{16N*GX_2#8ki3Qvw93D zj~-WIPma!Xuh>x0O$S$3+`)^>?%=wTS|ZhwST;-I14x86raNOmufk`-fZkgw@%qrf zJ7D0Dz(Bupw!a#*R&jkm6^W8-?%>=yECyBj?z00vs(54Z?7w)k_v_a`t;Mq+@9!wZ zx@y!^kE&sROFcH!V_Q9{2EpqUcj#2l9XhY+4yx5Pr|xD?Rchz8-9cB`&!yaf zYpW(o^}KFm;k8v-eU(e!Wai5ltE>s^{&1`=!&sgDx1jud)wjCD;4-6v+rtE`m`an9 zw^K3=Vp`oPyMyL2W>q0%oJhxUU*A2ALc5<2vS z;Ms)Vd^s3gZO63ud*JE#*tw>K6}DK97T4+)Lu-u#Yt!0~;dOoLp`lt>cj^00ckov) zx!;~xtEs*pSL(V0cPN_-pSme`cx_9qqVC`$I^u7v3iJXpw5}bzu(qIE4X!H&jn{TG z99rj_*GzTIEh~#t=7nzXf*Pg4xvJjKWlxWZXpavUH_>Cp^yqA=N7uZTHLpcFU7Itn z<;`osAPo)c_}!r!j7=2RurYmuF(xH8Y>eJu%($woa%p#H*qFP)q`~b|G^40V#|@>R zOV?4rkRt?sOa3)kn z46O#ECljJlGqfI1JuOB?gQxxeLU(^tgafnu0aGC+3IBe=+{wg2urTqSB*EWv(g_Gp~o%rSyw~dhwjA3T3Trbkl~avYG2CYU+Dk#?YpsEkzw8 zw$*S|+5a9UOKyo$0I0ZtjA#~+v;^SO{A1% zg2-|nP7UQ%u%2UT1nYTTS5nFo<%( zU-*}x4oWI7rVchkHrARJ*M}|6M~g#ri{V+n36e8D_GzOx5b9kGy|*a6p4$MGkKU`% z!)X3F)BNzT`8C0_3I8X5)=3X0S64lD^@O`lne3Sz`WEV5y$al?4{H5i>tWJ!DZHb) z6rEgWI%ypx(H{dJ7n@F06R$X?Pj?p&CWFp~cRVhrlx!cwTe{kKHUseieG4#{bpmn1 z$*QInp1VEIb3M=V;q&1gU(=V=u&djR?F0-YL&XUv6+R!{@sFlY8!r4qo#QDNRE_D7 zy_I9ZBplvmO_0Fgy7lRxaQma%g5)WKty2c)1_?b^EjQD9hG08pRvR;!?g*yVU@+l$ zwHfYL6|Xic!~HeYySiY`txW`{Y>OwH%}@TJJG@OL$e_E00^6QErd&;`cuwB<`w_M7 zXv3!7evUEHXM3uwHiU(6D#mF21{LR;U~ges+=Ld`p^b$t4nT_$-2&^vU@+ye_p0fA z*J$YS;M!ny7+Raso(^Tz0N|;<&wE@~+}G#pg{{9f)PD*Sgw)HTUQT6XQKWDB%s(<6-6Af?mG! zd=Mi%PlcYlsyxi~dw6Xz*mDGxFsSYslukf~)|G-6HggBh)FGG64y6*n6IA49YI7ro zRt;6N_e@o3Yiowq4ee7@&4{(5hBgd!6m=E#3~d^kRWzq)%h0x=c|{9~Mz&8U6csHg zI%Rabf$o~J&iLRL&1PV&rBgPzCRqK3OacdkB^tRe2D$Gq>mgTbbdY$pLCXcru>B7< zXn9SB))j&m!V6G3pwaD&Ecs=%WLJ8&;3KNGZFksIlPa3oY9^{Kqf7W}X26VH+IaL% z$>%=CQ_?k4(&X`uAghDxbgnl&B|FL!U3hv;Q@daNlqTk1X-F|~P9g69M^H$L`Z zQ|Mm@F26Zarzm@opxaVA!n+Ay4`IYa^|TA zejt0Q%8eSfVMUZDe^R?jl`Gv`-ELqqjypSoiC5~cf%iT>LLR;*No-2V-& zz9dlBxBYV~D*Rv_p_?GA579+K?xIeWRl_#vR=0sV*Oc5}{xL|dvEG|pmD`RU`N4a1 z?7g||2DjCknpwF1J$kZ@k6o|Thi84|aGAHS2hS$_kNzR(Bgo-kFcSLWZ6?!OgFf^f zvWhWC&5$mL{+dd`MwL2Kmnt1wno>9QwJsebQETb*588W=DypYnP+vpqT%+qL+Eg@a z#FpvAEX;B|GvPlN%&#!(r9fG24sY5_O@e;C`0K}N@HiBn$*9FyPIl&2)gHX7_R?Di z#}MicaOHCwH%{+))E?4~?o4k!!sPG!+V=3Qj~piXyBhgB_*tF5o}KBCr3>U$g`=BO zGQ0$a?=^;JhK3ge&(!(f@0H=NxjH^cJVwdFOdJEjnCh59dy`u3sv3dHL_ z6FAaQqt#Uo1v}EoZ>o{S^SUzEo6+G}?^i9)3*TI5=pB2ks4}@vL_N4&&}D9J7rv#3 zF>v?}yGFZDbxhR=t0JSy1!kCYWM7Rq{Kr2oPGKKi61yvO*q zK8PLETU?dr31|8qV|d5^GB*-7+I_~{W~5*>m}ApcR|G+f=ott7<%ZOz$wh_)*8<($ z@QwvTMxn#gPmTOv-9fcFt3%JhxxSjwQ%Y0M>~8Qlq`5jA>0re4sprtCe&B#POt`_+ zv@WG7h1Jhoy6bTVr+b-Fy@VBa{L|4YlLbMSzMdsV^>;4bvZ3!VpI;ozg@G+ijc=)I z$$I{SftEh#&_}lFt;qEQc;!8jSJe53HWwVC27B_IRW%LvzrJSm>z1^b74#EHkJ>4$ zZmSO4W>TFMl;Zf}pW3t~@P5hwA3&AVnW5K>=c-Bc_lkcL*Le=C&)kMABKc~MFw7Cmy z^rz`mH`|A8hR~)D-6%(s+k8#I*=LPw$ESk6HT>K(d|5-Kd2YY2Um1{B&hb=){oUYI z?e53651-0%vpU!_tLQ)W7I`-CE8#)>pqm5x&D`xoyT69eNIaU-(5UA-F$KO`44G9W#XW5r5rbC>P?{Gtf~%G zXZmvpN3Os4G9zSDfJt}u|Ezb1cQ987y*modQtk@MUCpY&nMN^utM&63*aJ<*O9#nJ zc`A6O-XDJ=m?@@5F+7+}MXM*PDyzX;WBsO@UUS!arMK$bz2we*xA*+c?%;a&(Lb%} z%y73_FL=F5jHg`F4fH$HL4yZ%gXS9=1o$D<;K7$>pC63=!)TcpOXx6;Tt9owX=-g% z8K3oO6`vZKhcBHqd6d4HM;D^FQWy7E)L7|ld&cBoV2vHUSHMoU;rAwQ5MV_jp3TJ7rMn^+CLAJZvQuMV8TB^cWcZh z0((cUU#0zhK5%P%On>H1i9ZS(-h_s42^$8x4&&qO|52-y_EFhVi-}ooJKC>+stio{ z-_s3;PYo2uY;E4C-KEqhi^B5G=usXQg1_;ygRyjRlNAU;@8Gw($oAtliMA+X|If{gBtFYBL_ioph1OqWXhR;;&8Cu=} zmJ7AA?q@nG*R~BX^(QOA$rY zW-YjRG}l-u#-pZNO{qOr_u|LI6=m`^9djhUzA(PXHFjMP%J>*QQ*lh_Zy)@<@^Oh# z&5$Ge+ris&^U!L(senY-Duq_3Nh{^cE9$d3#Ooz6 z$JK)h`2lzEXZ|cR33MY_HdO+$iJsujRQDwr6`k5UU94t=x+xAfdp`@@86R6_{LH^w zM&pdYWA(=LFN4u&nZm9?eYsITI8^@-)GsyamQKq)Lhiruc|_8yF=$B$5gMD`}day`!}pv zy}|mV?&&J@q|R3LV)gd0r#0y5Jk?X+s(Ke-sDFd9wx6D_YD~23y9<P#V-%deOV1dkQQ})4KAA-0uRT>ZZodGFYM|8bhT!>tLf&O>?LN2`7(OIdC+h6 zk>va|=u3S*M)we&dRzqcJB&l$3Dx`I(2I`->bRdf8tQi%_0^&JUQoYZtLxg^UoR?p zZBdI!QH^;Mh5kyg5b4|plj`}P-T0W^c&k!BAT-#4yDN>sXocEg4e495a3F7kF&tU_ zZf}YGdKlZ=1cN&IB-HOSeUFCfC8)pB3S!3%QWol-QNJQo{}9wCw7R~A9HqAO*KBG> z8mamu?5X=T9s0qcy>|skQ1`>8dwBbF(98JPgi)Uvs+XbeYW3k+cR}w#qxZyKl7zdV zXDdDZk>0`8uMb|_9a6XS{)~6^cY7u&X6}&LA`dyc?zeuh)$_XkMJ1WtY90BJx~Wpj znJV-%lJ1&6&2W!warnAedyWORK^eW*__$l(vi_i48L7PdXF)2}f;Qo=Rpzhsf)6?V z5-G#>@42vA&Oo~pRlAA)4f>=lsKVf!qwNm4Nxe+Y{-1H|an99+5IIfz7);}&Vkk7d z5ow!z#JDu1Hrv74eMWNdfx%z--&2Q0W;@YVnTou0ZSc~|sw9r=e|gn$>T&=6mj}&C zt+rHVcd5K9CWcPcONDx>>9{+*^%hE0t7LG0zd|`Xxh4!Lt73Us7X1GQGihbUY--St zx=iUV)umn^g$#W(slC|m5hHD-9Qmtys&)wKuq)Ud{L$36Qq?Z#bQ%llt@6;48m}4> z6_kWI!)s4CyDI-NCTc_SsINYzr5kPfu1H-TM&Gz0ZkXl26AZ}s7>-Gv4h#H*U`kB* z$Nn@JsZr(jeYWb=U#_c3e{?b2R9_Dp__*y2p^fce z`oDVEpdU81_rFJVIHHDa0e;~SW>)CeXM<-G{sQe+bVK_V z^?Q8Ey(!h7vNqvwH7k(+!-jU3=~j^qt*QoP<4M&`&!$gZwqq_gtAc!uk1Y){G3%^% z$!c?F@NB}r{U<>p)m1>smhxO>>>duX&;8Yx$my`abOv9j@T za1ZhEZut1e2lf7TgSxr}i}48buQ2*&hx#`{{|83@$bNs-v8vtv{eEwL^t5V)`_i{6 zUG4f%c2}c1BS__7cGt_0ncY1A8uZSuU zvhr%5f)z3JYn4)gUwAF4;}};C&1i-`1{$>Zf2q51*|6aGvi~}#=rhf z8SC?R)6XXSa~|l;@L^ELHvXrf`V|8{Pt+3X4C6~>lKXLqRAbi-UA)gKc#k)k=xP;<-EFo2oAaQ)6OQ}a_s$BlB@QI)TVswR6yTWdvn&;_VILn(L9qnKUl zmpAn6IxZ*>y_xWicbMXg9`FaGZ$9XwTkbf9atm^Wa4 z;H>G-yUOgF9|WTIN9%i|T@G~|G~E}fp91yoD)r!!qHi@V|6u(Vw!UE+oEd~VWT|4( zuNX-RAz?&ap+Sx4cfwQCYHHy!ze^*fn~uguWilC*{`lBx-Ouo>cZ5z32hS$_gpMf~ z68%vDJv704z{<;DB-(+~X2euJ$CP%oKV3O=oft-+SD8M)v5Vw!XY_f^c#ubY1jAfM z!VhU*8miBR`Xa5am-eCHmxxrM=yxuH-N>Hks$#l2Ds1~q@JxOFFKp{5PjwvnC%DD! zOeZpsVRyg`gxP3n9WCp=Wq^8R@dZ~PJ-FG#^M{!h>N2IR9I4)$*9)Jrp-G^VdLC9n zVjItb)hK1O1I^@rO!u5%a)p=W_q;>W{jK2Hgul1fo9>N4b&ikWvYZIjYfyjXzA)X| z5eZQrQQnWXHIwd#cb2Y3&~_|rs{)@+w+>FHQrpw%*D|MDu^{;WL*2W8$5|EsJ4*)3qMIm&rVnPQ-CNzJ#(50;(naTszmTVlA*XhJhDk)*n z#r3f2(t0?5Mpdzv)VOYwrHW)N_Vl^jlqxX{*~}{{!V|Z5B7B)VA^-Ow51dTz7FYgx zk?(BgUs`MRcCOZgRx7Kux;f~)$Y=7>4~(~3^;fm}5?}qNR{aX8j=7uco#fah*7s|o zPTVR~%br2c$?L^%;DG>|q*EBAJBT$r*wDP8X!h}FzG`T8R4S>0mA>*yX<@|b{!i=v zdt2ASBdzY=ekFWi-KvthkBA+}X5Q4g_xZYeSlwF-b*XHi`9Kg>t5>XmRsF( zt*&;y)cL&Cd0Xqme4SUM#kpXAtphv^Y|!#5&SWge^FRXX5H%JBkowBs=7(WqF%N>O zN;88`V#@mRj79L*)G$OE_q*_65E==k4Y3vTW!47@8x;=GUxE z8yQzLLi{;Ze=k#$NaXPf9s<*LdR4BrgJceckj-SC}Q zz(>Y7IzMmqW@^2Ce7#v#Z%3^s9hnk;Z$LL^$(s=6Sn@ zIU+^cJ)+idoFPY-5~L`m)%0@2#u)y!!cOcuQT&fqI{ZoDcrN(m{T_d~f9|zG@rS%1$`TaPe&QPFDbsAJ-+9JW4U1S0zT@SlcO!H-Wgi& z#i(oFlU8r_eFfAiM}x!4k#sd=SZOa%n|c8XJ1?7wr;9_i`C^AAX71jk` zitZe%k_ZTl`;x^BEqo^rt!_o5qpyzU?o!ffVhE&i`buJ|{s_+Nf0#d{;sX4|(h z)~ckm%0s@&7!kEx@KaWiQ7k;p_PRkCaa3*Mnh1tv!dJimqG!Rdk~7yR)a z;ejQy>}a&KS^ItQ?TnV6vbZ@Pbl^D(z9hUs-h)=U%2)c2@wq`O^+5QMAAqS^?#$o zuEF=t$R2d;3nrgLi!6JB21R$s0O}P`=+rhpOUntdDC3FU1<3Hz;emF`l#yW2F0HH% zQ4x)@`B3YKt*(>2n=+03Kb zi`mVbq#Uq0pK`(79U>_faM>x=hF_veBp7FSKF06kQ4V3e-gn%vNy!H`R# zR@=5*uvuLgi-DEl@8_39)ou70!!79siKh5AvT2#9H zuUKN_5?Z=^sli`$x1$-AbZHkf>ny)W)1bpZ!~c_r?Dz0+S2Ml_Yiy)u2em9<<5>TKP{CTzfjKa`(@LOQ={B)!9`9 z%e~V!zDvi~W*B*yTZmbJIOK7x9o1FvQ#R?0Q8t~B)Z2KB?xXZGzB_7kd^aPzoQ`R_ z{G*=i>*WdO11Z@QmLhfoacQ2~e?=aUp0BW;9q(9htigKpXTkyzJg1vktF(nyy2V%O zG&yM1N@(nQ30P{dwozCod03CjQ!Y4a7y{0)N|UwHuD;TzjDV;opeTQDNS@rM4A71W zsJGlT|2V_`*qyHVHC08tjP*mBxiQ(2UH!t1T!SD82Xg~}j-N<) zQ*EE7@7R@+>?K%R46EheU+S^mVOZa|T^d|GRT`z6zF+_^(Pm8Z0F&~B{C@)U-89XL z&)4Gr8|Q|T+pYL~#Siw?)cJ0DRfx-G7HF-9eXVz_*5R&}_6Mbn{V{_v64zC-z9b(0 zuKJo?13aRqd`7LI-8A1&FI3cVk9xYHe(W}37zv*1T<0UKQj=EN##cJuDqY7)qx9|Z zu0816GYWo_@7vk>uDEYk32;pK73ZnBd)}{=8R^i{|;lCqKxnKu{?`1VJQfn){@9Ia^&l>P<1%8+Z-e$m; zx3RS{-NvXYeg9GPeUVQ*4BYJu?r#4)@sTDlqdnPh zTS;3>>9HQ18QYg=ye)!N>NG<)we6`n4;_=l5IO0@YbQi}a^+;?L-wD9 z`6LRLH5`q?O+|%YY7P~CIR!~cu>?-E;lei~oN^p44o#t9$JC;Kq5>N;46rYLF9hty z4FUELngq(mHXJpCvQ;;PfYojQu>3`UG;SD3yWJXsGW6+5P;qwY%vWM`hsl*9@ z!1kpgh*f-P%=3tfRu=IRxU0r12OvC{gq7d-9URCn!Tkyr zqj53~{64=}AW77(pl>`#ikohs8ZBYHkOPDpTXe>9rwv?|Q8wGF_H1j%)i=2V#bRyH zpN`VAms?U1oXCgNSMa9|#Nl^ha9s4N7Dzlkq$f8EvFY-k0AOm|80-m5Z+kO=m-QUF zt}hpBN5RO7Gw_e{`io>rGS&fD9S{x)NS7Oxm!TBcamTyP06ir1VjB5<;dTq0U2y2M zj<`EwNo?F7q%Vq3ICaPNRU`C{188ROPx43@zZ0wCC5J2>ITdfG;ccCkovUT5@pcB@ zo}q8g)3-Hv8^_yu`gVc7t;5^7{qzpdV1y~qn>Zs+POZ%aKPGu-aefg#VwJe)+9I-fg`!A4_EDn2Uy@Ov7Q=T8~|ef&UllH zahWKxVlMtsWn87whnB|LoicWcjm&2LqJ(_J6Y?n|yOttgQL>Z3}RsH`# z8IwoW<)K7vrRvv>4uMp(=b=PxrRsN_`^dgsAvQ6aS*tCd@}}$Cn?*iz!D;^oP*frT z1KIkb&OV$H?epERY=xC*8-5v0iN=tSVkNqKZ4J5$XW8+X#UZLBZ&jQ2G`i`9pc&`> z&(Xb;(S7^%q&sxPJJWgXG)Q+wVa-&yeEtX6A;shAW9yQyiWB@)@UfG|M3T+VfSSZ| z{;N@hcCpdf%w)yhKj^UEEs~Q9K1S>wGIlQ(epD6EO!H{E<_w4Ca6|JEMRSEm6E`$n zOG9WPN$lg1^C>aAu7HHfIG};1Glqr#oLHW0W*3FOn};8W7+?|{hF?=n59f<3D!eLP zPTAmb+aXm-8_bU?%-_D@SaX}f+#(E9;y9?kbv#+=iLa>vqUhqIAk~F;GvbNC!!Du7 z?qwMEQVbV(3|AY5pDqb;rDnzo?p%N%2ESvcsEgI@H2<%n;MvT+3T%o8)@Z;^Be0yD z)d?_>{_9b2MV)8CLV>BL_9vECU}#kNywc$AuW)n!b!<4>;FcE=S%dLf&a}-wUk)#V z=04iInsnQ_pU%^={soJQD!`Mf2n*TJ&tR_}B$__9!i(K8CsOQq9%^Da|5fRJLR1JR zi7D-yd)l8C0m%i&d`}q6*2Z%+Oze_VZM)?t6XgYnK_uwNTj4P^uMznCH&TCsj40qxqUGB>*vU0y>xrFpiSy)zP%B$!VydtphzE0%o zqZ7h%758$rT9gW#T@~46k8C$Xb^?((4XcS$8)`$py_}uTu9bXYxb)`kU1Gp3d+CAK zU5no_p8n@LRLV8;gjldM(tq}N$XP3%bky2TVCu=S;j1!sF}SBH(O>d#|6*`&CR|4} zu31RwS+vQXc(!kBe7gQA>oynnG=M2`ad&~ZIv00qimtf009YxVZ~njiQc>_9Yt92S zO0br#na7{~yOC1&E}|osD^XqibJ+UNhhau^aP#$xf0ouK^!{6l>Z|+d>lgnltxxFr zvkSzld+I9}{~We5?bp{toO~|L&t7$+aIzR5#J{fHpe`%X`H}lpr&&v-7sA?q+~=tM zJo`AYod2r!uM)w@X1>pUN|#Ua@8WB9Lj%>G9GPnG)m z0mqe3zs4u*|4zSW>aq6EA(y%l8sC4!o))j7icOVlLw64TG-ugv65eGqcPm+c`IjRr zB?c7n@ApGi^a1(>f*zS!(FcAPb`&;_&o#`kk@$GOP?pWyuOxrmlU!jWpZ6W%q-snZ zXT-_?8PvXxId8Igt@IB6)!tcZFdtNye|W{Q;TcgNtRNC*LeA|j3{|7H01IYPJbJ~{ zcSsKdM0D|lEFEB<|2}DXL1^K>O3Or}Wx3LFtfwVzw6vsz77jKAS}>x`l<_BeMdE+> zt`6i1F^Q0_FLMRdjvr6zxz6eGrx0UQ^Wq%N(B$$Se$)v-(cGwLidi1K1;!L6j+jFS zXllT|`hY{KYwG|U>z$(gH$qs;hy_G)qt%YzccsWVe99%j1y z7ryw8R(uMIhk|Z(lz|e@U<-q>zoNX*!^oRj|2fHnR#wT`mZ^qL%+L`Fn$wettvV+8 zZ-#ntJVAQfvoD>V!n#DSojzAfy-PI}twPg$S!7 zpfcH=z5QP>svrb%U_6C4xqO;fp3&|b3Evs=H?xr^X#1x6!E3Qe{xvP^ztEi_KermT zwEl`86P~vkr?Q6Lme5JAJh%;E%UxkB;&@d5>=Md_zcyqBi8jw~wjp2y0=!Cl1e+Uz zx2{6}HXnd0+Qr(|R9Z|jD;>L3LydYDg7ZyFZcvkP)@CnBe?doo2L}C|Acv%sAg_aEv(=!3uN36+ z2Dg03hmwoW&frU2^r7F*Q^Mx)Pszn79yvZVX{8pawPew|2U7s zTBUOCm9t<%L#VEOPuM%&o@Kz<*-MBYZ zZ8f0C5o*}Fst>@2 z2Ji2&AM|R^WzM)?YcQ`>m>qdX#lA+xE`XVfEwRBHyiYHnT}jVbmWnM28}vOcDH!x0 z;i^3wzuSPZ9oK-G9iL+OzNh$VJieFY3Fo6;PX2PifN}8Vr%(>72Kbjfu}reH(|X^A znFxN5}Mxf?jp-C~OB^zB^!Vcbkjkyvabm2%|&Mng!LW6MI4JcnOwdr{)gjwyK$kt2@4XpQUJIn zRTN_0P&r%9eo|3;LgoklB*`cH9lCEAx|u)+r_dzx`&RsJrS2wQJR)@Fg3VBT6gk_A zbeG84(FW7bsEK=+kI55m3|h2-a@P2*f}G8~cqBP{R&16(E3!9mO=Vf{2W#Ob`dxwH^6L|{Po|ez;*N=PPm^Fk$X?T^T&W} z9aafhUEVSh%T!&chuHiMu^ib9n-4+!#mkOO=Ng-)EJTfTc?FzvH%)>XhScPhkb^90 z@*CC=sfC;WUe%;LxI_DA7~yA^z^~TP<-Y25Y?4gphL~FMy;TKX7IT)({8<~?{gP|w zeIgmy|J4XMiRA>RHSV(-YqZAozQ&vKlnchOMtV*wjeg|8%~?#H>MlI0Q@yM3Td#1l zo7~K){(?b&j@8oTGd%SB4f-9I(WyQP=0(N*(2I^2XBzI012+cjI8iY@(XIT$2p+~ylpv;+x*`k$P^cvK z^H1r2R~dtJtPNk=upFg4nZ)n1<|3Y@B8k&Xwi|>yiy*+r##-^U)kAoaj4!b-6Rb`D z(KZDyxHi3HZ93`OY|I=$GSVsl69OwV&n&{YfK&HVLSdwn6=4@@bh&}^da;H6^n&>w z;q8X-#cvUzA*+L(_Rq#Rv`zJ{WvHS8| zOUe=O(Dw`^X>IQNIfC6;Qc-S`W>@9k@z1-?Iaib}7yKHdG=ge`k3bfBWTrev zuXx~HY(K4pq%d)i+cUD!ok-=B*tDkiTSfwdBlj6sE3|&M2#_` zK2D;1Ct7Whm|oD>yJ7~Akc&r9vY1ZCRCY6L3k|rf8-B?HpJc#)Gar?5GJyq{NdJFz zHB0S46OmCs>^jS^#LN?OWJx@f7)9p3IQBYLhdaBb6=dmQ;*X!w7<%-f2AVf|yQ(VopZ`G>?W= z@zSt1jsUZ4P))2`OuFe3wni$ZYHBbRZdLeo2)wn341?okXu@6(Wy;HI98=!u<5Mn} zb8!JoyawB%MnIpQJU)IFV$VlR9hrr~7JMHO#!lmHSbZwkxj}d{ZFFcgemUw886$9K zyEbGO-;jH)A>X+O0CSS!2nmh!H;GAc9XbP+PA0fv)5)pedhz>u809#)Y9&q_#%xqw zjF;%$kys-GD45}hEi?ZwjAKbu<|QpF&mXOjLVPQFT zGd7NUHI>O%tq(IJ1~uZnyektakBVNgaz%Y{G+W%P;@ z-;qjl&ELTu0`UZ*SNsoIdE)PIdPje!PyVLM_o)v}J;nP8%|0vn(ZY@5K|s-GD^q5u z%(r3=3SSpJEuoq={A@??4|zvGxc^kEUX4*pJ2 zOz_Px#&Ar~P5>U1?Gw_apR+EV$VR5i=Ls*lKDXMqde_(4rP!N^mK=HFSjZpuY3d%S zKc8&BQ#EI}ZhF_cX_5k*;sGu-fTsaqX!m3xd?W#)c=A3FUzDjRQOf0fs}tde48zun zVYwe}PBRQ|T_{q>8nE1+vc*0{`n%$l4O{9jMYd1bi3Xb`JsH zgSWkSYo@jda@0}~JCi~$c5NnxQz2Hx+MO3cg&1oLunH3(!A)XY-?0e2Nyo-xvSB6@UF}?0-lrPGN4? ztP^G<(*Fz#bMKJcEQ;GnHj~coe~9mkH$APX8ynGE%B{>YDT576x;fLdK=PH-b6r`x zkMtXYI3#@A8@0CKy~0!bt~}*}&nUG6VTpZSTH+VaS9^Q=KdZDADoMi%E4@&0bYgfb z?S4;tDEk_s?Pm0$;y-qP86;$>Z&Dl^3m&IWA3o%=yR_3}!V z$I!*=DltyWuSym<5!8h=-BZTRi!8?LUaeowq)|`sJY>7^)p&j^-}`f9gx7b?cD%MS zdrBE20)qt)*-(lSgO!s0^n^4to2g?Drpr@)=y*K zDDFY5Fc4QN2q5NsopVsKWM#b(c{x!#9wRWw0;BObtBsg9N;eExuxK0fgbJz&U)9il zxn`)8po?&>h2Ix=zqBKpIZJtTZm-k6cciJg;C2KzK$Hvw_wO+_66#sna1N7hscA^y z(?#;XX-LjjBx5|1TjVJh)DX!qF-*unQ#>8sA~0*Z7|5w=X{YfI<8o?gtM#o6xs3Oz zrLEDoIxfdL@OER3sn}`_Ftor*w!$iI>al!Zl`EiIzXjb*y2{`3cIzKpA0BJ$eeN8v z$y?CZVn#dL$akQ8k6Fot6vgQ=#c!(h}v0*igiWrpL1^u)Pu2K=O$HP zID+>1;R!M>UKNz>v;bZdETb%dp-L$?!!nC)Myc3T^E+=gncYeW`t7rhse2hg-#MFH zib3lJ%fwUtn$<7|W=j%C1 zF!u7sd)JK>>$a1j+er!B$D{kXJmrG?SN!sr?+C>2kPl6h)Xlhp?CAP6pMrN>=Y%@t zK@+Uq6vY!>Qnoh~EkF@{Aa-xIZ+v#JBFSN(tEG}}^qHIgvd@qI0Q(Calo%W@{GMgh z?W5G~?Wub}o{;~0mhYi4Qe&dku+jL<-@87&(Q0&R4JL7lRQ5{G`H}c>UtzvgxXLQ% zZP3}iitONcX?i7_j<;RGcw7@bu?L;^F8K0pVZ^4!oCl3rMvYk#mP2^HO^r)*lg1@R z!3=HW(Vl{Gqo5oVK#Z1T`^L1y?k-&pRmIziRC4ts7n1=2aewwXdaJ{=RAn@qIYjaN z?HR|Ky#-?~xcN+BW(V}|Zt2fu1%J`$Ul;VNJ2b25!SCLX{uRwuWY${W3LoqCTQ5)2 zt^}tGw)4A&WUjXHqaMjTdCCRPVbUuVuv#X4*AdSK=rqw+3aWnz%jsT@@h__iFUR_q zQ+>g4+2_Z}>kq~vi-wPr?*60nsy}4r*xxzs&9H`5Xv1iO%caJTt;RXZiYi}YN2~FN zF9|EWhF@Y8&eaMpKkZN~m8$T6Siw9v@Q`{vPV^f8c0s8I@p2FjGR7Ekj^D>RYfy#7 ziiz&+827d^{FXKXfM7ZNTYVFs*yErVi`!g?TRX(9Efu>hF}RtG1wA57*$fgGIePBu zTk%P2#otcnJy>nY$51IV6{V)*Hm(6Aysi#_r4!CUr=~n#X^@clN=VN_61jsfUH*>1 z%mqsUsZ?M|N#BGdvRp~hgi9uLGW4A<@1z)G)Jnzf^{b`~xB+F60B>bT7c0^;J<=XS zI#tLAkXU!3@!qiw7Z6cUk&NAz5lKE3)CMABpsvJdnp!XH%H$pu7gksP%8 z&16qY?3bxIZ7`z0+!fdQ+=iFW69$Sx2l5LIQdJ40mdX)u8ww?C82kWQivJ$Pe@`iX zRk!wy_jFLD#Hv=KNz{{ZA~3%exyWX0&-cJ@T{mATyhr}isiG&WF}D7buCC}n> z41r+W>+4=&buU6)(c%db5l^q`|{tk@<$L?LB#}TwD|@U7bs(Nec7PgqEHU? zP>wPvZ=WI*)3b`+KY*G}C?Mvm`}9~>H!52J>l`aEfL5}ADk@}oDkR|_NdAuDI?T13 z#|AdXO_;I4{uWZ8E}X$J(J(k`434VyFqHHaA(8^cl)PXC)R2TJ2~x2&o5%V#kG-&Y zEC4Ye68c+A@ z(Y|cvS*70rEgI6$w8{>KuiGo>=ZH+R!w&jj)*ZALAH?8%2=J+?K{?SO?VbB;gtM*2}odck`9U1Y!~JAK~Q zXtloHT5EXWxW_7N&WI?^3-%SBmM8fCpC2L|99o75$ELO(W7T@>X7p~a^j`d!W62+l zC7%Gj!-$NFTnw=uhg0}VL}s~&UpB+TOTd&ZJ(5%8DHp6daic_LlZ7#kGD4>*p=Uqp zxG^A4SpQH$#Vr!+v9;Bht~K`eHB#2!|2{#Q<^|+A5u9x1<62>iuh3yAx>%ujcv0)Y z-~~AACOIFR^_ZffFyAOUNGUt-5!d)XTQBVi%7zgI5dqAPhls*K6!j8O*xHaBtVqUs zBz5wX3$n*=q$sFg{sGQ+by&DT}j5|A6qZ&2+D>L z1s5tAA_{v^s7plQTti~VJPc-C^J9Vz`Tw8WNKx2Cp^sNY{LiNdBH zATBY=?82O*J!Ki|rE*XF~QgPlGS)rYxn`;K&$X$t?-A394{72)m*TI z6^0Xq4}5@NOg-GS=S2Sy>(eDG<}C%sQYc+l%K74wsLm(|hP?HU&XPri_z8=aM9V6} z>oGij(A{IiU<+?ONFyOcv+ozHE+KkUypDGPWTrTBE7K=gDDVd#{8$WHHe=Z=z20aa zZI*7&V~WLFcmR?+q7T$mVo?c8Eb_cHo_V%m>TYaQsv zAu;;-S^eCLHE{-|-XF^lmmVx67W_q;lFdxhDcif=hF@qlXf_t)fpf5!;5$i?ye%Sh z30{XX4}4BZ!X5m5jiK95(Ov7&ZD#2Ha^CHcSklI^_HAqXwsq*+zI=zYBlH-E zNpxX)CaEyL#8Pb$g3`r!&z_?5!c)SNY^GLObb%ig{%xip^BJ(n-@J3G75}UjKg1XR ztrbtPICF0FaO!iqR5yO3o;?8=jf4bq0Dyhii z;wszI^46cq@Lga0(^kAMrczbX2^UY06}$YdWN^v12J4_9yxoN7s#HVzz)s{zF^P|N z%`jnz0xP}pS^VjZxYJya@Ia+)2vDd8tgSeIo7uRFWp;YfpU{3iy1GW&-$@o9j=L8U zk5o#F`~Q!OrBqqw6K^WjAL%U~WoiCS)gW|;g&nSJ;dm6BafY6jZDRvc1Q{jc9h;6> z4{f^}ZHIc=x}=wKLuf15OyLFv3@I5O^bs~65Uc4RD66j@p*bHIt1C8^)!)N_t+Z`7 z0&V|Kv6}veHYZR|WQgQ^V65J3V_6-*OH$g-8-cbDht)Mu85oYjg-E4t3{8bz6ogBU zXivJ;p7h4KS%N2|G~khIpJq?GVNX{2*6lq)>-zQhe;ij{u>GsfDT_dHk6RCt9bg!U zr08R&{&Bw*4k`f=N!uLircBirGFn)m0l2<{@P2G7u=5!z5* zN-NKBeeOC;M#SD+4c+C4FuVV!tR_26=go+xYtfs`FsI1mf&;h2Hx-?Mbq&1ATI{l3 z^NsJP0icei=YlF5m@9<2+01kTPM3cXT-R)Znx_1EmS4h5`Vgs4Y+h{+u-}a6BeS|SyJv8qVzQI zir>z-Zd6rqk#r3dpbv4@fEKMl8gIdZL=JzD_>M^qF#m&(!KWc_^h1W6tHxW2rD`X7 zTM*>;6c)e@na!=ZwF1{JIN*Ny!`JnQ#FYxdtLUk)AY8t788Cw{E#OO&9f%W8%DFzX zbqjcyQ529AFYTzAORd?={m4&%2=BQCclyNIF(D*SkYzjG__hrkg+!f*8troTfjexV zjj}`O)xwnKq1AT6s+gtaEdwyNwuda~Pjf1sinW=pGEo}!&s(={-7(1?VQ@UR62CAl ze@y;BXnwdYTZ{=Gr<#`%G=LCFG3ZYcw9H09Qmjo4MYQ>!TDAiaI`O_!HUQ{MyG?o< zgyAiA24Hbs6Q886di|f2)?yR6+=ez{vn1a@kiyD{f_FhE-k*p zjf0p~v?S5EBy#CTMrc|76hOk>1?CsVNVC|EPNYT9hP1(k;3x-du4}#v`m6UVqXi+n zWj&Aa$dw-LdoRupOG)D0CvYO9kL^rAbd z0ZSf2kRyy;SHC@4)S+G1@EJa%xe(l0sMRO(Kxt0GUVDH8Ah-o3`ygLIJc%wmz5+kd zl*23g`&8TnqMn*TfanT{y_5QME%++?m{bhWflm(=!stiXVh9S;%PxJd^wcRmo!ErJ z&Z|W~>A46RfBDf&O2bWw#+xGVxydH)kZ9@v2%ZiN$6ro&>d)Ucn9Fxw+xnm=LJ%a)pYblsbNGb$oQ%t@#8Q_E*Oxo-G-(>A0N5N*{ z&Q{v6N(f>{*vw0|e^;V!47h(_i2L9VRfWfNK_PV18BR~~IFK1P!a_)W8;shpYZlnQ&m!4jQ%z02p@?aO|boSp&qm;l^N%agKwU~#^ zE8*pt#{#8g&}e}dgD1(&^S)%yCq-MARpWpr)TH20q0OB9CZBdA^x^BC9&+qn!|5p35#P%;zAD{9YuQTQ zGHF3ODa02XLbI|S`(w+m?ogXMhjiec<-48DJyAa7f-+@iQ4`lYMz7kRXXnOwamq#IksX;q>Du!hn5dgoFux3Ihc-eyBt7j`33z272(kc_$6)=UINBm zc+2sTZ*7JYvMeRG(dj>oW2sRDgia9Ghrb=B(!KFCPc*3Q0#V&G6UQX~HycaL6aW~w zts!E>8y--;F0xx>+!AZg0EYn^J0-X?FIihOFVp!+7y4MXC)r;e(9*HKOcmaE)wp&) zpynh@38TnBBpW(GSUi2m5B3^^IS2TF^LW*H=t3UzI?R`0>$lIJ3}V5nT=0`>K*@EX z8SC<=x|d7vQYUbKB9g+nLiHbE5qO2#HL*6%Q^bl*bwJ>W$wCT0p)jz*EM^DC;x)PB zXKr<$2+vSpaHo3NV^=~MX)OcoY#LYG9KX_STK*9^6#B{W95!&86gB;^J1YpKz7>F7)2Slx`0>=mA z)Bhkp)8DFs_^o>yS^w<1kmy~$whu3GR9;-<L*Eat@*nfOEI#qTIBGNBp|0mq`%34q8$v>}shq^Z zp5^t5V^|Iu6DXW@GlCr2QDtE0f$XdFQslu??kL-wspAjKP3cViKjjc%JC56q*}W9Y zG0H^18n-vXGIn8IsTcA&vAi}Ct4Dy&ohiJ&_*(!nk0{;2WOgcqbiaJC11=mcSzK(` z2Q`wIFv>ZsVM4yWmk?t9M6o;cn3~xpCs>Ng%bgvpY(C2YWyX|(#}IsfPq}q2jBj(r zM+*zVEXF5EVy-;`Hq}gSb4H)wRA`7Zb=q{ibuFb6r^N7N0|{(+%b^}lBT5XTkf=sM z^?GS;4y*-^3f6#vY;&Ri#c`CH39}-OBBQk}6E2x5A7Gl|c)$oTSkZ7Oo;vJong#|6 zTaqi;0u85JxGP!HfYHax#DFx0=4;NfJnv2t;{aiKmLCMoej|8ud5kQ+csq*cVy)7p z{$AQ4j(2fvW68(K$RfXK14i~V7c=SBVel6RBNzPr4FKYtA%R8wMVm<|uV2#0b(I44 z6CW}D_TK{$z|9R}?9;WY0H_b&+SKVBs!_`@xS`$v01rf>EtgY(5ZrL_1Hy&blM^?; zGF^BJRY&nMiMb76^52I`Au#Z9(XnmR>H9uJ)!cRIHYf?JnhQ>XvP(GIcaqf4$As%SC%nu?}D*5prm+vbmQP^>!-69)p1r|+xO@5Jchm~H|QjK??Ct#pLN zl5!hbO0IE1$BfFi*FF~Jn|G|NKLeYpLCxUAq#f)D$Re!F&_}1bagrfYelrxj|39h*c$W)ifTxh!EzEE2|0BL78Xt))s7lRw zgcX(C*KoE#d|Cgmtfm}A%j$oY_g;4kaIVvm!5&R~fYIVT&OxGYQ67U4A8PorhR+Bo zSL_ivj6(M?F?!$&75GFDYPR9hiO#=1fHm{`#uH};hk-9aWFd`eFm9nQ!8{r;2Et68 zCl;IwQzYtk<8!HWlLV=S_Pw>EScPh#OOi_Q@+G`vw0oI1EsH!>PTB_YF+}Fqvd+kZ zeTAr(nWhDku&)sO7%#OaSR+f#!0vk#iF0VK;ORXMq`~!v1g~rGeR>C2APF^903WPy z&6t9+=u>Jj-?KYEF797}%RI#>@W5Z$p#a=Bcs3xHp?7WaB-t4&CJ-jzJtQ^WPBgw9 z>X;o;vA4ZH5DQemzKfFaWd&7$jjMwubVC!mFmQH)kWNgFyBZmK-Rnmh20+Y&VSt<3 zTo_=Se8>fF?n;A$8hgpi3y!A>5DCRl+Yh%q0zC=1W*ohit5*m*!GOCzbYW2gr}Sc; zmk7#)1=s`n5@P(z;=A@xcDG1e*jEO_Ubbj8EV>Rv-P)K(8SSQYs4ZE*g(K#W1(*3g zk)%qPd6EMkz5oAcbU_j2wc!lBMay%+jWFA=R=(CZSqk60ikz~3OovFF0;YW&CZJm6 zF;$mfn&2>1lE)zB-FJQ#EAs>Uouf~w#$a_bh3 zPsG-g<^R}vA>unxtN0CrgLMYj3_}|a0IV*O*Lx$cDjf-LHONXi=pfxfym5k=4W{Ez8i~+qv z#(>vdPO2=kRbVU|0C!>gW8H3Kcs)`dgM9VM$bjIa_)R#}3*CT~wt~OFuHF+pW){1r zUnaMz-2Jah2dk+FV86lxM=!r&oLV&hlfc_UsJMfkI;A7eA{cWI^iCEwpg-jE<&8b- zFapeHB)z9;Rg+bx@M-lLtxf~klmCXZHs|o?9;^wCj4nY?S!4$xm+XZ-94~vuUBYp| z*nr6cHXoxfYHguQjA0VjsIjNE@}RRVF@;weDGcGD&G)C27-E$QJ%p zn|uxM1`u4ppTP_*qqgYlg3UQFpOINj{xF~#U_tdYhh0m17z&~By#dBWi#(trv6@P; zN;Xh+4qF7s=nrUh!DU+un+v%#e|ukiOV!ey2r?WZl*(?D>EA|;fmJ6AipkH5s{adb zFNcoe&!UOB*y}8nKZ0-YO5Sj8k*`L+xEh4PfUu(X4e&sRtTm7USY&~@J1c6$YhLI|G2@8SSQGbToB)Jm=R0D z^CWMy#2!fWu2~S^m$hIgB!>GyG@-6ArsMD>g_8ER06oaN|nHq;X>Woa!CO z)=@^E`JaqFCOW$SO=0W-6Io29=OV22hNNdxg}C5ERTm8{Y>KTmNFoh~t-1=#5&A$I zq~CIBpmtEM$O5JlutX*P78V-na0J8aA?im#Qd$>dW_)JP#c;#{*OFKyw+C2=1b=EO z8R5QBIs__%7vaI}csETV>^DVk}ikJ2^hSN3g6hKa@fCR9cpMnO)@@R`)D zj9zi8|Be^*9G^ZLb43;qPROVUXYS&c9Z=36)$==i2ij(|gZ{QG6QCA+FN{b6fCcL+ z@<+Lv5So3fMDqtMp-R5E{GKADN@TLmQle=20Nen3w)NmOS}$yS?IQ--6J89Xh;8pj zQZ9wR6dTOdFmwRzDw2*zn$RDe=#P%%oS$*22Q%7bSZ!)W6^xs)mQqJ&7%tj;p6^J`ycmetvZbt@O6$7Ny6!^jdWOs8 zcx^jjPG?a+&l{mTHoO}uBlX8Zz@<0l0fnXtzL(yx#4pzymC}nF)*X$Qu#ltMaVm%I zkbXc$Fel*T4Sb`XdWKWG;2JKViS}>VQ!|9Q*~|mV^<%#0dg@tu$^{Q>L*8vLt6R`X zz!7G(&+}@B@V6!B+yvooh42p}@SQTtqQig|T}aG&Mqd}GgCI$i7NXe#f-uv(38L9% zqS-;wT!%4}HRVzq;f2O^aRDdQg9}iyt;<$kV9{tJj9I;47E%P1S@M?5@^8tky+kIn znI2`j)f*h6$>MFS-9x`k%xT;Ls|2(2Q}YG!Db*chX}*etz_X$kVyCPlBh98am( zO@65+U0x3MxI1M2N2b{wG7S2|>~qw@bP7cp zAWpGXM5yBFZJ&?`{L^-jJ@RRvhICB9M6wSv4GjRL%NIzG;Q&Y!#?)_Ga4|Y87nw-J zFxqB{+2(FQrfKcze~s3X0~ME6TsE3zr}h5E7I{^L41uM z+TRxlUhEs3{k}lF2=as7-WP~l0D@g=V!>7K=wdx6hsIBj*H2O8u@IGNK-d4TI9n$; z0plqk$bi=HX*_jPlXsN|l{EsbuM93l$Xx=r2<$Xm%UE?gWuo3xf2C zYpMD<3fZMc2K#tep&?|01>onL?Lhh%X;9KF(E!q)V?zep z=O`u{o)HFFiXu~#V0E#pXE!gA;KsIihusSd_aFuOqxgeO3`k*Y_JgCL5n61wVA}dHu5aCP6VIXHen?Jik0x?yUMt$n zl&nvHHq8@Txj9>o(az$3;AJE~7sc?Vs~TShDT82JFW!My5>BZ_)A1Ug0L|PU&g=@) z#-iyU{3FZ(2{Yv4*na9%c}L%bNV={p*^?cdk`1PmtxP;$hEq>_lI7Rqi(u8sAkcAk z`*eId;}ZBKFb8Mzt^AH8`l81qIbmecs?>pV2;i*$#*=TvvoGV8qyxXg7iDi8e*&yr zEd%;lczIKQn{`|!A;keVK#2>t4iKi zh0X(Du22hMCuI;#-d7K2C+sdFD=4v?FC&*)>?;G8z>j|9tzClx_5w9-DM&T5yacG0 zMbp&ILYb+w@)H%}6c^|_PawvYu}=h;h8*Ss3i8)DA9J`5Ey6w3!NrLCJHU0pITSJ) zkUKKHCn@x#mI_s21aDyCi%m9K78jQCRNQ3G=CLKfiUF~oTa_FNN)ONdJ>S15YPlpQ zI`SnbVq7U^P@)gXgoR>7A8i-9u~WLw!Km^qEnF9t zcBpb&#ry-Uck8D>TH29CsVfa_$*ks2c7rK6KiKj&lCkgfw{ciN-Ab$mxGFqhk-bN# zawa?u(vsK{;cyB#-QWQnOlY5UHIJ% z`!fw@W-3??Lo%n=61?)yFa?AV z9|TF5i#;70L~oZ6B~_b{Yvgx&K^6Y?VQ`emmkkBy=#-5J8B3_&O6$VNNn={%+!>_) z+h1!U!o9LMtdz$Et`VNB<4?eYHd?d^puHr7H13UFG*{QRNM(h!pDnBF6Pa6gzl@%lFa5QCuN(T`Xx-+f?atpc6r!k6SC9N8$o-+S} zVQ@?x4M$ZG4jnrTM+sJ(qitBn3fBDR!2U#lG_Yqt|3Ba;f?=}tLcP|zWtr41fqe{x zVVG03G#P9>qoo3!f=UUNSee~Hrh>;1I`n% zzOji5*Hooy-)1{Pl$Hw}(Z$yTgBbq)AK`g21VO}2_!An;1CAOIbi0aMVQPa&r8g)*Cl+K-Eg?$R z#Y_3(cmf7U(%A=?&B@Wh2_}e!xx};sOYD*QGw`P`QvXG~9W;cr#`2uZ1!unf?mBel znEI*wcmDqD*m{gDSWRX|835s^F_g*61|qqZz-I|P_9ot!g(e-o*EeMg>1%;9v{ssk zqeQrrNBukaU|F8}TO;RExGG6B2$nIC0YK9b$a+}8%K zdk^Glb|P-~QEf&MsIL-~@(pgoBp~WRMaZn12jHW=nJy9W0b>ZL6Nca~83O7!r{^IS zxSJx;xKmd zNn%fZ^!jtfnN|nxM)anmXH__ES(Qr zz)apUVFkUyuEzZ<^`Cb;F5F=X5?{tfj-$W|#v3B}Pr4@zD?o(IswPTheut5&PHYAH zn!vsmMnFbAo8lQH+?PyhHknbqeC-+R%;fS;Sw_FFL;fsnmp^GPQ)27iJozJ?v$(5% zD((zyicYU>jZVj%Jkw9>h)zGdD?0r`+#}e~7oFZX5Rz2ciz7P|B7gx&?jhh@b1(kX zfM2<0<||go(^@=L;|Y>giITbp!34IMdl2I3G)rTDf{>TGk5C{b0uxd(8v>YY?#9cM z{43c(f{H<^ZzY!VQv_SL(y~hd+j&@%hc0;#Gye2}L;wwpEF4Q>rg;^0EQWtXw#Fo4Ysp2WYwcyrA)y1ZVVgurbOcI+K=p^}3Zu7Wb%(vKi#LT6^{#>V zgbzdGZW=UTKoTeEWp05D`JA-!5iF(u%*V1WXgRaZ!71$Sfl!5#LFxIaZbU`X(0FR9 z9N;tn?HSbh(EvD`tlJ`QUNwAKy0+Ddp&}gzC+7?#kL8g}$bJO^$&Uw@OD9XVR|v`A zdcL#s_bD@sE^+HvwB>efn)b~L(ezUJg89w}GV{WEWJc$t1ZU~*N#ver-#dz#>dBsm z3>Bc6sBD;MgtkGU_mq#(#aCHV$Y^v5PGBMy%t!F!P??N5al_vdqHw=Cy9YvO#0vFu z2Ek|;a?UYGF#W@eUJ=uRTvnh{eJf08F?-p9Z$FGFU9xeFn*~WkUFJJ5lCmMhZXl1+ zOv7?&Yz13tkRQEA69`13CX@YiyX_#0{^#*tX0N*-g`b7w)W0q?6sB6QobRWJv6TP9 zX0xB*W#a4?7QxmJ;1TX~EGWZ|L12&MIa43NIT)WpKgh`z_|aFPHq;1H(>##(dvQ({ z5Qz(4z-}vSaE!WBDF!)>yZA1?#8zpVkp-207&;6e2MU`3x0A+k6qLBbpe@v#_V8=b zUEZ7%Prm5(n$u+b(o0v|3x3Duzgw{gAMCVk9EY;Fxx3C zsBk>i$kJw*NO>bBcnA7#R1mNn7?o|sSNs8tT2QrEr$yTa%M*QNm^5%=i6%Tp$IUl^ zmL zN(BogxI<>)UohkV3=SK@h|g^n5-81|!6#Is;OgScK;rQxuBn7u1Iq&mt;kO;!Q&|7 zN1z+v*e9C|F2Z3ciXkL~o6KhjbCHW2Zm$S;(u0m(XA?M5WrOixa^mc@QxJKWQjf6c zT3ji;Ikth2%iYdGaEEU$wif?V$9qM`Ig6LWc|AZ}nEARHnfg5i5*CE;4m*JvSXF%6 zqcisQ^9L=pIj`81&qcUF!5~5*g3wN5$ z^smtiM#FJ{Sox{$$4A0Hw!n`%`+`+@bmADwEW<<%ysVdeZIK=BxZe?lUH(PZ?{lx3+Q3F7tOM!z1zrhrBL*q!w>h`<#z-iNG@RF^49=--A1&Yy2y?mm~zQqRqr{Y2`(<^ z{i&$Pa5a72KDE+tE-oCHkl(EcWTLxC6a&=CXF%?wmiGbP;6=~Ka=;>A;zQK=(tSC+ zMS#)k1|GzXj<%>doFBwvQw;%VMgg4ox3!(nKl!WfXzT@e!jK>`6TUq_bRCsNIsp~u zCTxW*U4xj^MZyPhkA`g*!*<7c4%?n1U@OShri%h>LJLd=2OM`k*yW1<_b47tFsUyE z@BM1pTywL*Fa)K^Mf}s*z9aM^>z)aB2P}aYM7ugJhp!nH$j$_R-`H%}HK2}QWDX3|9%$>no7oF#evA^uoAGNlLPQbrkOmUFkYfCNbQ7GK#8*u(De!iusx8T171tHiOEbfp+!7L*d~5#k`{GgmIO7{`}}|J-P` zYea$aGgCrd9`inBLj#<7$<5@pd!UKrxAUJ2vfkfApC}w&ehR$&Fa{PcU0mz16}hCI z3zrNp$b}X)RMg6A&!$LoU!q&{YKtS;4X;`uKW!>MZPaI&L?j!k=%oZGv`o(mNtbUg zV06=k_1A$u#c3}*CFI;Rs;U4JG2x>qXp35qc1=sPB9s_tGxFy8qCk$71Q7sMV$Q)} zW%SD5_%~d2ZHX>^NM4lEC{A{|{Iaiz;SA>{U57bEuK6-bA^u~?v9vB@Y*3RUhSr^f zx(TUUmXbfSq=}dPO8AFVR-qmfF;Q}TX>2Rv(% z)uM~5Qi|M^2PB3##(b-q#7_0g$fVNw`jF#2-U-u)Kd9{r2)v?e$T zp-WnVHFt^&R%U3zF>UJQYwR`QTh6BV6Q-2>kR$*GeXx%vt4Wcq&~%`&$?BHqU?>G{ zs<;K8TUSI;_tyjv-1{OWd2%yMH6oN<-OOIpZskDuSq_A4`lSYGmh$CduAXW8 zo}nB6?$;YqB&$XZC+XZ?)kU}+uXH~fic)_;GiFd z=DN#u__B%-GkM8aJ4!aiX9*?ID-xb?ROK13x?b=#;suoaxgKue<7eH+V_j1Pr(L*G z?Y<)T=n-8sWw@f{uBe2>&_mo*C!1c{QMX;GwoNa;!Fn+$>cC*sp%S7Z@I&bwSqN;` z(W4V}F>cAApWkf-ExI!E`oAOF4E~^e;XGO%VCUp7tY7Cv$Ul@Hh)m!~l0Rg`V@AIJ zsbf})Oh|DY#~+YURR^4{MOU!wEc6naK%pO41b|0$@{CQAnhUN45Ik#>EL;WzjHIF> zU8yZXW&G7ESbRF=N-HeTl*_+K_B|dE>uCBb&`!!f)yFb5G|_TB+6Ygrj4B zFPKru5Yga^E06IQfUQ^R+3o2(CxWhARJ-dAt@q7IqsWmZh86I zd6bUf(m|5x+c4@Km27UqpV`UuGLC_|NxKz~rT$S)PH+YfVXP#_#vW_TV~G1xC@W~+ z>{p`e63Af{-=vx)%R^78ObJ$)j-(`vz%orG9I;G%GB4{y;G#j~e*hpE8{R+}I1#Q? zx>LwkyOXj6ENJ}k6;A4sxM*eacp<+U;DmfTD-!~4M`^kj=st-^=>eNuI*>Dk`=es5 ztm|XKX03ywa<$Q|NGwDRorwp&(v_Ae+@5@A6pjosYf=UdpJi2t0&2j>r zJ&=+@znM5&}K82=q8>P(t9q`|JVnvjqHQcc-LpvV-G7Yr^UB?e4_D zj4_egF1$}xYC(FCg~j7^`9FWE1;J0@U}EJs*tan-n*0eHA!0z|DIGmaBqVypUy$5p z@(^u)1ua8?k!2#<9A^46pgz}(g&AWypGIT)bti{7)335Qgy{r|{V%y@c@U<%j_ov_(sZX3D=wdpp0fuIK>sWg;u`fI_+E z7x)VQ@yxst6m*LM=mqDXlz}bC31NU={4-0E)-L-cEPD-23jPZZDBh3@3_F@X=?pn* z@IaFMXFu41A&!6?f))O9rrn;ROCULcA41H6IbR4Ig;5 z9b^N_ddlmdJTB?_L=O^=Er7U}E8-6F+(F$WEFgkr_fn&Zp>^j?<}-&b?n%zU+G?%c z+r=O{mPdr2i0Nosa4H7&;-5&<3US0@DA^{tpV81uPG_;xB3&K{y=1-w1QwIES_p6~ zm@R9yBSb!{&!9HmVd6RiS7W;iTi=9OsUd^L zRe}j^YVL;2apjogMB_3I!eT}*L0IObaE?3!%*Q+aFIi}D_e_f-s;`&MRr3b|tyTKB zrqbs6i=NSGG46jL5nY_J$(^)v!1k{}ZX`GbWe|24kNt;*@XDu;FC1pFFP<^|K6G6l zKy+xLS|0HMK4aKgm**jAcqSXM-N2P;l5-ig%%c1bD^L|yu7+VS1W1=Ve7N7 zn@}&R#b`OT=Q{UL)ko&*BE07M*1-oi)Z>v!+FS@rZ~L*jadzD8-^$0}V7{@8XHBTz zlCA~z+>p$jx#@z^VLWI=OF}8;=}9=oV%_oU8oKmcp$93kG=pQ2KrM&hAx^sfD;`S5x@7H= zqYw}T))vsd3^My3hC5ap7;U(FgrwSVC-+EIqI3(_C+PLU1pWN8-Va>B*jASWop6d% zp5ZEATC7vL{HjptnO3?2G<0pmz=zx#cnj140Dz*)xV^rHpy-D(T5d9R;rZn}sX9rQ zq_$52=|#iWsGt4yATsq#-bWLHMN&HXxJQ{&n-63kkIim;h(;qdOBEYr!9q8Nmn&5E zrltxr@2MopLO0QCPKGXOlxQj>E!TVizRDCz_y5db;j{*5lD0qWK7rt2Vim&O03?$) zXk7eZj1Z6&nOp<=G(wprm@nfRoDvL6o?5EZ{DW)o8obmO3RZfh!1;^zd9*}vkosxIOkMp(drai>6VdQ$6g)I{+? zf5B$Dk3xwxtV&xWsZ+8b7oMhkdu7pb$Oc>rRPa`1`GHCXeeA=?jP*@s&$eL*g#|?m zL7FIPCwAb}#M9h!+0cO!olI>_Z+n8Aj;@<2S0%8s1j`5l1&I!jcu{nmhOr92ik5J4 zI4GiEn>U#k%q5&<(untP2f3Ozt^jd<(`|p->~DwsEv)Cmj^|=G=}UxuA+) zeoMVx+!lMl6fhBsHqBhqkr@pyF7GOen!ns-E|KO7&>``e0~|4=+K7<3z6_0A00LyE ztj6^*m1K%A)*@0hGq`}l^&|bkPZemSHdZcOW)@JpEywWOU0K)iv&Q4^hfkuHKNl`J zFKys$7=h(_=mKaUOx4ukP*t2#IwJWOpdMO?o1eceMATqKXu&*rtQ%*P=<>`H_iX?f zI|Cfg_&f`2=WH5A3+GN>^C6@p zBLU+)qm-g>{|y^JCy!4wq-7v?gIIOA)j_MKu}gO#0RG^)wX(%%3`_?^D|QoDfFP`R zVO^;@I?Y|WF&e6z!(AzY9O4ki7hx9Cwvma9>i9D*j%^ycl+jJ0V1$fW{@tmX3ffhO zq-552!xbWyT^UE zgP^u>&n+ioy||2fLuv)nm!KE2NT8t4>h`12;~TmzQ+)N>N0!qr7p>L!? z1g)OPEmU%=`$Dk~sk%DI1LXO=lDa!cOTm3UngeZTDi8r@jShu|h6G*AN{6azs+4}yKk z+HN53g;>y755TRBNT%oshtpHfW{xKRaNodT{K*`GQ)^^09sK)VW;)@-TxJ~XZl&W| z`cYr{U@QGFN&^qJwsJ5Y5KD}hhs*?K!)WR?Tu_MbX@4O@xo%eM1)r+|0_#xm1+D{O zRO{B!ntw1_bATJfSA<$sp_yWV>}?&b+xXCkJ%=-yg%bAgR8c0G}X&*Bdl#H4)CUjaFQS&=<6UFe01^DjnvcQ+2% zzy46SKVdsz+|b<+{frL-%&`310#?@Zk7;+v?)N=?iIcS z#w;9cho2GMhA3^0g9e_`!@p?zjBwk}yOMR&lYV%}xXAHjfgNR?o^&7>0iFx6@MaLX zj)xG7`qa7(oh3(C?`|lo`B!_8or2EMp2XP+c?Rd?o6PV=E;n0yt_HKFijpehp-sSK|_@?Fl^8VPk> zPWCC`qpQpS3Spr_RR+f?q>q_wUvvyDqEf7^Ei)9~f5g=905v!vq zV4E!h!2ZZ;&8ji;4-#91Bd%7J&x)d1o)zPeBKy)CAT3J z+Xjdz%)^NG9yY-pgb?H_fzLHW$S>-lI`k0iyKXlundVq*DHnSN?M2^Xs+E?YdY6EN zy#7R@m+-aIIKNQu#^`k4()w*cEvjUJ;t{t=FQ#$DV1ctRZ~(*^pcU+ZbB-(dIM_y| z|FW6=Xe`p@b7wetJ8P!O+etr{!4{P`j8S13EhJC*oBO0|F|?V+<@a)!u#Ey<*SdUY zF)}Dwj3nE_TO8KjRTSEC2PSa61S$%AbSeh%jFD9bSUIMlPY3=JaTrz+uSFZcYaV-B z1OTGUjm0ESPLSot6g`ABZiC33DsjW&$;*MZ*e|?hR_t}RiK9`DDxBjXy?!xy`=l^4 zn>kZ?`}RSOw>!uO-2Zx~@Rn^;L#P*=W#H)-tXR0g^D0ucY)l``*BG?8RKzw}IDW)c zT?&GDdot=YaAmg>GSS5)lL`^!^1ppbL@B&of}VtG_jEF61npiV$NBPNNXRkO0tNlZ zWGgkmUx(Kl=*-PV(czR^)TZGGd@Ut@w-E6r)0Yb6`HP0Yf|cMqaHLARR0Pz1Hwxqw zmc-#g`00?W8p)Rs!|oDE(Y+l-3H>1f-&8$ai2UkBEXn&AnL(U}vnZ^coPj@K%Y`H5 zF~wrU)C8dob36QEjy|0Uh6>TeobvVZrwbH_QC)^m3;~Aov9G5K~t^4NVc8uoNnb1Y~I~OIY8=vV>)1fCh?Cs4P<1 ztV^Y~Gi_1QhE5_Gr$eJq4Y);ciN5%fzb{CrZJ9)1BAa0mqM`&|&}W?bzIojk7wrH0 zJ?Gx%d1lK(n)3Sl(9frtXSvV4_ndpqx#ymH&bemr8&=DGa`i0l`9?wu0%(`4Ovvyc zI{<20>qZrz1TNSq-a27qOf;hF-0Ms`d*_hHEz}J+*cumwbO5Sh<_FirT_5U*LhG>O zVWdijd6F+!m|mt_K^ZA4)zO7+pt)yMbKiS0Llv0&CX-BaB&WFSL?KeGmY9uioGCxa z8vMYVSS>%v0Dg*zR=yDFB_;yUBEJ$^G%lwmg(M$`HSZr#CM}SBHNs0>tOa2<2jx{L zMRv)f-9|WEgU4+JvNKUomHqx8u#n6FHok`@T|?@7VISI7-!N6`3$uG%xSG+FALyFb zgIHkK7UF!3P>D!@-BZWD>KIxq(jJT8wote`M=VMMxW@gu~di zTO=qcHgq?mqs?|5K;0VK7P53!WJxc{1Mu-F`L2QWCUt)^ZsHweuf;Hx7`V|0JHhA6 zW+DiR!wXvz2OC{u^#EWNe#~N!T=qtBEN8^tO`jqK<2=SXs~I%N zi?lOzH#teZlk{1IZ7-|FTN!tVhQFd^EMBm5##ye&R+7Ry>9Q|`BJ)cczTMHEVy|_G zr$phL;G+}xlvz5df&fnmb(g60vtFghraV=Y)`V_<+pY0OoeeL8)+im})3iN05^ZCu zg|QkrN2jd14s^F-7C@?ke`WSk^g$+(5`CzYKA?K*#N>`=N6~ny7#HMZZh@r(*u&eP2!j(oiHR5Z2gc_DxK*GqR|o_Q`IMiUfKkpHt!XtK?%49l6e2ta`s*R7EZ_7pqRtiwYj4 z!%>+MJp`34!j*`3cK7=Df=Ul>l>shBrH@A4k<#tiL%?KmiA^Kl>w0+>Tz9AdRrDgt zSobA(2jdrZZOxKV=vp2Bu%G3TA5+!H9TTA&rj#zG10WQoO{y{2*tF!(Q#3-l>nV(= zz8S2+ve>OnIG{Q^tvx%n-Ft~COUMOO!SN}SZ=>oaw<1ofKsUL08%^vbGbzNAXQ4xg z?&5re=&rf=0ZcWR4C-*{*Cm)}!SqL0LTylWJ5e!J-Y)ao&EK?0Sb1V+`qNQn2o+M; z8;~4as${2GEV)Qq9)bdfb+$G94~uX1iExZXui8{$Y||}Ceyc>-@O#ck6m2H^Kx9wT z441p|V#UNda35u_oga$!augyi&$JWe%e}ka`lZ^IB03nfmBgndBj&SU`lN zS{Sb;p%rQtvZQFnG}9c2E}9TxUo>+=VGE1`HiN4Y9({l!(8{ssJ1-GZtTNgWLhIl}V9WDR$%K-_oM zxuLLkrpkL~`9MCw^iIz}iuF~-pAqpL>f{)E%c%u*ieb@*_yT*3aul`*s>Bv@DwqW0 zzpZ-v{p3<;f2SL_2V{LUXD>s*Md8e2`3k&Sw4s*y75a(i>_E!xOCc;Gh;Ur+5k6R! z;KMPYN5T_}2G5|U-CyMl=}V~7qGyVWo;(Vqny>##B7R(fsKe|FfC06$KyRTO*mn73$dm{ zVz>hE-N0y1=2_V1u;>k-SRi@@hLIKKty%B5!>(D`Cm0)0NUmi0%Mab}%eJzVt(dwS zpwZvBTUuiNgm_NF8^Y=OmmIr+GL*4~u`T=?pqE^?Fh*hb_h!Y(Q1hlbRORm69R5Wa zN&%&zmKhN!oZSx8B1Hf)1kj-?MI3#H#nny#P^twLWkI=1*WRXXDrfie3j>3fS>0Ll z1;IR*QBnX6{Wlsa2g$iwuNLL1R!5?tRuWINXWsz@l`n}hT-KP&Osj}aO=GXRDqq1* ztI+>U^gp*+48m&cYMVx8p^MQu{3piflt^Y03gsIH$7dl4E?wiLgDbvQ5&y*?99Nv|ZG0X%}*53e{e zih_!kD3Er@|9-oW;$nF!_S4K_XD$2O21ylW?d1$!o-%#^r1MJo-rA*E⋘`%ZIl<4u`6 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�j@C3rX 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%0ATcwHEs_`f5g6aG(Ubi#FG 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

W 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 P~=uk|u-gH5Z571j^3 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 z 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

p8j7O;GdNxsfEB8^lA%(6=-10xI$Mvb9j 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 z zF^uDS7}C-~h7o=oMJDFkkMkoyeK6Ucz%2FYya7RRmcUP8GqdI*f3xw#PU+ z&#sU1*Io?>sw5FpX2nCejbSV)yj0Q&Vt2^gIsnt4|H|$nROEBc@QUhP%nX@q6`pl-hcyJ@e&+B=<5WZ z>4v_9`uECQDTiY>b91FW2y+N{E$)`Ag+X=r(Tizc2yZ<+N9XY;Tg}Djldqb~d?k1X zscxi00=6n9LBzCGa25Aq7KB!3h``Mf`69FGMkUN+Xm1 zRkKhFsDS?^v7$Q{P`dPexrD?SPnQ%{mV#qn`4wYMcRFFh6Eu>6z7;`3t`ue!wlw85 zM$oLWc}rbxkMQ>q9sb6$)d+txB@2I23~IkWc@H0%?oUE)3{dF63=|{lvadE`eAx<} zN|@L&DZKP(eHjY7BovJ{X$ZtEzQ9EEGQ=*xyyS*!9yhp&=c;n%;8}c&k#hbL1dwJe zFzy20WoiVWO8PmxvI|o0*XY*!?UFb+y=Z?Nytadx+6OetO8sA(ssGCy2RE$PU@W;C zlV(L556P&^7ylDkQ8pec{#@cAG!7y(3L`WMql}14Fx;2^6nju?wLtpmx0h#RyAldk zggqqvgs9S4)(}z=v4SW3M`^kj!P*nNe4<=Z2|FM81GK2oJPJLACqgCSEv!XL5#`V_ z{3Ds4eJ9m1U7(2C2gD~9;v%#XJjDzAM9tV9y47HHcAjzhB##Q2dd62ftmCoEUS+-H z{6>8P$)}NDuuBMM2i}5Pkbl_(S-;nL8xoKSQ^5$61T>1n5>9j)r=>wc=T*)+bKwa@ zj9~whY}6Fb{SxTo%c0QpMP7>e0BwX${xun%+P|@ zoh|oId6p|hIPA{ma6tlx3lc=$!p%Flc^@}VbHkOsB3$k&!qvYb^df|6e0da#Xx*$$ zd$U{6_2w_j6g~#;eil(=^*lSGP167u;{xD6!j5R_isnxw&YIX23}v2}nSr);IqksG z-@OI3S8mfB&c`Z2%N(@jo8PXnaYIGL#zx`f44j=sK{BgPVLr~n^VXG(n=2@bID&}t zbj}PC0ZAyArWJC7*hZW!JPdrzia3X6raz=Z7P`NuLG8lS0~AJK{2x`z&>##G!p^JZ zDV>?T!Db7yoIbgELA%EH4YhtuEkuhRK~3O-n1(2h$H1Noov^LqN2Z}I2sc0J!2 z__B5m@l`@eUVy4eHtcZG^#_3MSX%EHeKgYAOQ-G7W7*B+&6v<3I9SC~p zuQ1#JZZK;p?)-J-6fn`>qv`53wdy4(&*Lg~PA$2hPFzxDm(@n+&mDC1#E}lwcS)@> zWa*%sZ!WEF66OVP1SVu`P|L|amdfK6iQp6*MX!s3M}R~-B@*>5f<(xR%wdiR#Tk+4 zinE#jN??dMlfB6z&;zcAz!h(_2qdy%#9fTZqL@Hk0KR?m6af5h0q|-9eAx@CfPFwE zoklso+gJ_n%LbU(MAYp9rT8s>ZGj?t0ia|e=$BXH+rL?f4L>_d10eb;f5s@|7h~gy z?3S3MfTCi{6oBG71o*NTd?R-PyKB(F@Ty0b%*$%mV-)Q`ok+NauaUw$Hig}E9gT<` z&NCkab|@%`u#BO^X%mr>Ur^5Zn^5Sl@bC2BhCMkKtLgGsO_#@Nx;$3X<*}MBkJWT}tftFjHC-O7>GD`j zm&aR9uiA|B%I{|iCQauIi@{b}pHoTOHU*Zo5Y3OJQikVh| zZc5a;26ZoV@dm_kDisNAU$1IyP>)_$Rj`(6eqPn_8dO~ynp-hL{Q`_25Z(D4P~3IZ z)SwPFsNw6J{#1oWz%1MSB9G3wt{%bf3S6~U%{d~^ujy608`P2OeE1q(+^d;l7p^?IB}-=p;y+*;m#&3C0~_ypeWf?8@&Z@sRb#5gxy zrtv=?H*TP<4Y7e6!_H?h;aYgAP13D7I3Y}kf~F6tvIe#GI_Cy8Tu}S*k=NA;aUo9N zD)jj903wfqtl24}tHf!1T_r{*iM}+@&5=ux4n5kTjy0&{b@k>3brvqjuIr2jaw<3= z&&f0m^3)5MMJ6*B+awgo0ZM0J4CJ#YkGrgvzSE8~rFLH+*q2>bdp}BNcrQ+G$uv%# z)Vmv}G0wX&&W#$1-WMr%oNGH&WlHV1uBLBLGsC49)uQkK3h8h!tl7O#REr=4^{)o} zLTC0xN9+Nfexz3or(m62S5E^CDCRR4Kg|%}VccNIYOBWa_8F>IKt#%U8}4Z3D_CD9 zFXY6p7Cl(+Mu~Z0dM~CDci^6s`c1s;W4QN^0N38@3cR%(x8K*G8u2jDYCE1X;DUD( zpVtPZg+I8giGLe(tu-!RrGFMbqu7j0`mbccM|jay#9h%NMy`9$TjA95Udr7cq8%v& zFLn4iqrtWTvd^tYr~?trRBPc8n)c_IO)7MS=|8(WR1*S5H?7pTOoUxlOhU5P z$2GMOwm}axca_?6Nuof+BWR&eGG$6?zQFa980p3L;j;t}CfMwN^@dhf1W>$(!m; zh|^zJrPUDE*U0t^!f(gm!p?6&kO`@_X?ce8Mv&=%MaM-w8MQ%&$S?;gyy2Xs$!{8( zjAk;vS;8a{>dHzwOmR9)aWT53_)=_e zI!rN^3C6^0nowIu{&p9hES{P8V;3>;Hu4ZhN~Nqj#s`%0)%{aIn6P1ye5lN*lpE0r z1e6F3tfio#MCKw&NP_b~f*WAkwf8b6CQ%~j2U4S_RAB49NyxG749(R|xEvaI-ixXQ zdzhdN-8)Y|1?DO;5bJn>(YolPH%lEzQAo~$sKgn})3sW6m8MQSU+cAUoQA($ZbY0< z>*5mKW)WKVc!wHHsV5+T-wi3cS;8F)aVM;R&A3xiaL)6f)^+?0lZC^1L#@j{sMe&^ z`s<90%=uNQ_{&{**sfb0mG3>4qWVM~8V&LdRF_V;)j8-hcPm|~`xO%*`{Qh$ z)8t*s**9G3mc7YRcfh6OlCfF<3ng=rSsh8^s-BKeawb{%uMbWENy4RPPqn2t*%le} zkx$58MY@YAEf5bn)nIXmoU@f`CgNqwG20K#Dmisx%NUzp#kZ=ZN7OxIY~rc+?;B%K zfBgRaW288JJoA2~@0pd#drtasdoRa{L6K^i-#u?5WNuW5jjv1Cl?zdR^ugZA_7No`isxT=ofur6UOPz5B`PQrtG^>LHLMQpv*0D9yPqU>B5B4B z3lgDXj*oE$q;8Nh!wa!q)NWp{Zu{4uI7k5sDp3HBw0U?vcIFdQ-zvOb&5IX(8(Q0OFLjKQ4M>~*2g zk8y6+`cUXI_&2-(3bQ;E`a}HtC7gTWYoXAhuZKeK$3L-0SAP>%YyOtKNl?+Rw1Hd6 zvQe^ZEZqRjqXC*nHqN7ult;?SBQNK1vPwQ@|KxM_PXTBD6pFj`7Miwyw-TEwWTY@ky+)c3dm8x-gG-yyCHo#UYJb@*9bjK9ZUwnWg9N2+85ON zbJnyp46y>2mPq3ib}4b6;F^*Qk)+f*Nqup~F;Z(gIYdq|6aUCGdj@kns|ky9PCjZB z!+qHZt7sy^vV`O(E)tbXOdGxgOlV?~y-5(0#03Ln@b9DrNrq10d|=Y5_(^&6lk!QX z`6SbPl4(B4G@oReADB}`GA-aFwSu>8#J5SHQeyb`_DunmK|I141(nT2{%9Gdpp_u2 zk>DyDb_&|(4aN|_&8*5Zih^IhM~eWmKgEE>9I-_aK`}tAG0jbJAfjMN^FLVZ$)1Nj z=D3yTni7;!(-0NCk)E=P60%X+?a_~OLk~1cpvKAjaq@oNw{a~0Pgmf*UZ$K5ZUIF> z!TeR&P!trB#f!+|(;mUl?jnm%6X1XR#1sIZ$vc>9VDV2+$l}P*t=^ZJ#}~tl=Dn_2 zdQQ~}kE09*#$|!ClDISzzFxubnO0bhzZtEvyD~AKMP(*wZlyg@=oYW8e%VE%>`jbD zVY5lj-SlCQY>_X{nPmAmoj!=r-ytrU7ZfS4QA;Ubu$}m&$5+Szqf&GfFBMwz!{L;JK$XeT;}Udy>bnQJ*%IJfFa08PtX+4E5D zE*luk!ha6fiTwIou?Yy@ZeZuXmGIpN+A5%$(DXqGEHc4N?_e(fCqq*}A37EVaqs|o zfy{I1gN3%B4;}ND5<1X_?r*ohLmkG#kVu=1F<8J^e-J1PL2;(T8z78t!TzT_ z0O@Cz2Qfcklz|{9zFD~L5(lf23S!^1$d^3|>oEPYFz0aPk`@%nAQBL`Zw4<$MVkJ` z$P|F@Z(!-Kd|qvNj1@a*<(;!lrezEo>c5=rBlCkF$q%H^`!GJs>MMTm?*C=YDgbyeLlqGh=iL0K8uar?psX3@kr}cIW7Pblw(z+`pF-NLQnG9Ju0~Sh3e1`azEbRAfV3;gQ%b4m|RJedNXl_KLzIp|hfh zw8$gx#SQ;c9$By*@)qTzbe=vO6Po%WdXgM!f)Z_g;b0M zLZHQuO%Z|42Z7!nE2U#@b)S}dbj*vjtK-yTc~>mHd`8Ez+H^J$t7(v|s#Qg!+IdDT zYg0S+GUFqv^b9)4E{jyDl6Ua7rRW^aYj3EHDCI*OvO&iHP|$(5aB93kVUZCU+wh?o zEv1YqGl;@R|92>~{#)rn#6{zVcAV8{l)V5NJ!5xbT+#&64Tk8d#93?+XR$?`#TIcE zTf|vx5ofVQoW&M#7F)zwY!PR%MV!SJaTZ&|S!@w!u|*z>Ei{#|*doIDT|wiDR8qiV z5N%iGp%kNlR8sg2d~gw|q=-~fC{&VvdJ3q7k=hiLH(}KtN|GhSqTT|A3#S zp`jh~t^U*~1_QFmAo5EF$5;3lGwLVEsaufb+1A9$Fs@K`0JsYvQtn!>kZ$v zsFuCSqT0j2B+AJJNdf$HzH18bL+^iv+WZ|Ke#kR30o@*vXl7bTf#g|rxmuMmNT($LiALrW3nJMI%^c$;2!~fnMQY&& zSOnieFyv{RJ{7&<9v{4xYrlmvH_X%VNvTIn-KKsMVL$bP;bAoB@PoC1dR} z&K_M_9?4K!ZmPjBXPLF-tR6#ItEn$%mAub#1vj~MZd9bj>_=353g2trVoJ;Rr@0T*KeF2)30jK$qC7I(*3fgWRpcZ?O@F;;lT7@~`@ z!aGL4B#;&iW(B6WG(Ad3DN3agV=m-)JUIuwJaMK(HExw-QmutpbL2s_>{r>J z6^7SrXDJxxihAUndJ}ioU~U{{$QkZAr?$zX_wrN)QsBQQ4{|nWo?Gkk++~Gw$)mh# zkOjY}fy0E~=fNbJna@e(H~Whh=}JjV=jW4ngQ-EA6OJQc#REPjF}%HDhN{86#cC(d zqaa5rebC^_+8d^D`m(C-z#@MZEUdUL#{5p)^H2k#5SWJwHRv=WQ|heL(+bZ?#W$vG zJWjrnVhpp`Iqxz>-|NB!H`JW(yQ3oeo&-z1A&vN!`DyRNhW4`8KKoTwCTD=^iXOJ8rd1zz zX;1b7w3pQxxRcsvucle%n0h5o=~^y0h3PLi(sbS7v!j0F;uH{IMms7i0d5v5nMYg( zbjQR5xDEsuyNUUAs|y7 z65J|gwtqt?sN^L~?Rbop%w?=(-aMw(vK~p59bpCXI=g@Gn8LgWecZqAzsA3hisf>9 zz4y#Y<=tE9dzW{dW5q)5?7@TzIj`G*%}hu?0v*A@yeq6ahI07l*?9yN!&up!KhNL7 zPYDBE;QO#hEfN>!HPPO1Ga+V~4NM?FC36YDf?#DYlW>3~CZttVtJw&5$?LH)HO_AB zWhxJ6kyyi|fmsNgVXgu0^_|5jbS*F!oK3qx2GpxN82E48-^)cv^kZi^ZCDN5_tf;0 zXxi^TnKmHP#p)z$hM9CZsN2xK8>TTMKcBpF7Mk<7E(^(CfQ2%7v{}WmC$Y0u35&1} z1O;k-cMAAO2W_^pK-dOt^rBSZ*d-Ea#F+1#M~%AHH1mBuY=APX``aUHv^}q$%ITrA z$o@FG1!a#h+A*0KPV<@*K*xI_^~_YbQlom`mkk?}i!Btt;fGe3$=)O|5h&mG;4j5q znZgPmyfQ_wzNTgq)-+>sA~&!NLrb`;FswqOP8Sb%i`v?L856=F(4=GD+&<eUDtJYPj#w+SUuBKv+VP^U&-dp9ncMb2YzoJ^X(5ZXx zno8AhMIC|%V~_Q!X)7@<9icmifT5_WLsV4~ZZ!qK$oI=)c$lflufqy*{9{@?Vc9C$ zfRQXGb#1?0(gkG|!l(b4jWY5lGRkO>`Qrv+m@WUVOC_>52`cfXax;e&l@vFf`WRE7 zF{VIcOo7Ij0*x^R8e~E8w&F2ewVG4i>FFdEp!#(I+b&0)p${$in^S(sBZpw7W680?Q zs!cgJm9nu~w{&b>2iogf`LSC>1+>Eo=w{3qo9^6KjMLBaX2^R=}zhi192$&oxZX`sR(85 zc|p3aC}M!Bhyki1%Bmt(h!;^;6baSL{Z|x?Zo)=t#ix^grq}bB#P;9#`*%)1YB>Bg zM2|6h^*fj*SflFEZG#pmO4qR}RtD^_zZv+$?X*)xaz zVRn!tbdN*G36rh6iGyLpB=oGq3Y4DlDz{ANoJsFRFRO<;)T5VWna)i}f4u~2S_d@F z85(1ZM{<6qfhUqc3{K=YJvvPJSz`|D%tl~EnFx#oLqf8I*`!?2(zXvIFV@14R zkeI!CJQEVFl%Ry5q_D{JO9>*9Qo@xGOB!xkgvy?WP>~RxbMM8Sf6mq8Wcs2cMkRAY zg#GUlrk{6p3b4TH@fqn@7!VF`M$a5fHFkECsuie;(CXl_u)j`?UvN|7ZeDM{@INOY zBLL`ZM&XOjFuiQ>w+4SRO8IFM;V+w%Ps7`qZ@YMty@~O5C*pmQ<>Cbax$l;X7qDEs z;Cl%2>!vZnWg_E$icb($lfqzK;!^&?6d?H<<5&%dk|N-UX^rb@`R)q!*co*gl?f2H zSbN#c2-S*8cf~E%ai+*lskH+@^{;!py3T0y2X6`2uP1P+(Osdt@oiXJN()`t^KhM2 z=>AWv6ZZ&%GFZxaqU^lp7=?s#F@Ks{AJ2%Wwbf zY~P+A{C0jf_1e?;TEc-w3MlY@m%6KX6vGX9A}gOMpBaEQwl%+4=}TjH%WhbsV`VbPYuniM9%RWYG}OPs`eM zLJd!G?`Y~_C*M21gNqYmjma}=((hnluxLXr@$js{diKh0Kh}wHQa!OwOU9a_7W>xE zaVcQlM_#j>AbXy4^n9F&tARP6P%m@Js?Et%cGD9nAQu%%diqZ=0$thN_-9kVN_1>f zYRyaPKy16Z7n7gR?7?Xjx~RIqXw*_lEVo10In@Dh7ponZ3jhZdK@aDkT)e!)PEx4E zv{$ciTXY}<8L@=Om2;#-RyO+=gQx7}+mpdl%fCT(|H$LkG^=I9eK@N%nd}8{!Iw_=9kiMcrS8GEK zRGk&9pV8&|+6P6{O=DeKs!NUaQVZN?)J9n?RqI`3;?cAiM0qt-p2oTC1#m7=<^RS7 z+H_xoRoY^`o>+s=a=k^L3H$GVF$IW6ni1ZjAMr$+SSmgfR-|&d-%=^ZWfIq7fEDcl zYn=N^)FI6Mz)xgYik)9UrLSkXaD@q2E@KQ_`6ooOnbtIP|JGFUb;xAu?PmPVNF}+g zz;W!yUR9~sDAE5Q=4C@s>=4xsQJurEq10>Y70SNwB@MCJFduX@K84TFH6+Mx$|qIv z+Yv&E@vpQu&p)Xa*+p!z4*?fLG6(C0!rt(&RVKFnSJfP6b%{bBGjvHsr8*aRN81(Z zr#G3etI76tVgJ|3m#N>ux8}&k`!a@5b2y2_sr!ndfY~_;`8*_y5ZXpM56iA$wwB(P zr2*vqKQ(fuI|f^{0H7C8?u$-Sw9|_8LZ7=h*nCea;dd)Wt49*qx2SS z71pjO7NFndKfiUjHlrj7AnSk7~Cm+9ouX%(NTRT>pgJooAtGi>tBcU zFWgyvnmQC8v>k`iTHgz;RDw6cx){T@)LVpDI9f*C^$?b7_z}~KbKNyUy`>stQ3X%Q zu6NgPkP}#qOkB0eMGB0kX+N0fKx+ z>VwE%_&1tFZyou!KnN)XJBFAOoZk-SKtd8w%|JryiA$4^IivOy*}7^Bgm8ktHmKwL zb4C}TZ6HPFKf_QBs0fX%rhkAY?X(KD5+?FBmSz{ARpDM)@)=8fm<4{s>&JrGVYIHp zeL}EpW|o}Vre7-VpBX~66t3Z=vQkJjUo@V^bo5~ng3h3^ywjww3}iH`AH?^#5{@R9458tn>`!YMhQ+MZF)WYh?E z0lg>HVoh^))o5?kM_I1xJLoE1!Ua7={EvzF-EGA0t$y3|pWqW@6ZkgaIf_}W+%|mm zwv^ikxM(`r2lxP`V%6H%*VGb>Jf-lZh0pLy&HgE_4&v^wwEI$za4I|q-CpCa*yaI5 z&nTMyB-LU8wCpx2rg`X*gcpEl(X+dqkkb20+PPAL-beQgtSJ!&IpuV)7y*u6OoNi~ zYT5~{+2mMeJ0bQcK`9##l%h?AR+yFa(aOiZe#h`UNqCkvahg#(oFw?0a=**k7@$bhBfa60OG8)BHwVEH1*d6gls%O2n?8%^}*mH z|8U1J5Sp46Yf_*WpdUb(c1o@D?XR`_8X)1ch5tM<($it4Kaoa85F%*StjT3LN_*@+ z0ti?*Mr9zK`37PS-x}%UZTe3DMm8SA0;D6bV?x^pz<>GXL;$26gM~P(9Yg->QH9S* znYC}FM8K@@lxpVJKtC1mFINAxi>`)2Lpe2i-PN=;boAP})4!k!u+*z?-5i*tPI)nz z4$HU*cw!eUYh98VKj%%Q+{tY4$!;VT{T5*)n@lDG)BiaFGGR0iT1@{3&4%)CX}Ha` z84L$c{Q|%zc9`;-yl>RzbvM?Q-P8~bfxdm zBfFutfvNF4JlLt?^!A5!vYq)o>btgEXUf=3GNo(+OvzfCAlu#jvSz#N*7`&)*-W+E zin9F2Zp(@V%?fpJflt>{V#~Y|we&SHVvkHkeehj5kE;80!IK*3hd9juCRVs0fGQ0)$D8J1r{TVeDE~C){@h| zjwZOPOBs5ewO!4)s!9;F2D9bGu*S2Au(TTg_I8xl&-mT431Hs)cu`rN1`}1!VznS$ zy!6A@qYJ-ptM}1y8M4y9Pfp8+NPHc2BrdO%kD2!Mm`o3FL&P88pEK%F{@I`g^{;*U z7e&0>dHc#ic)Y~IMxTNB;=hig6-+)Wo0UEAh3?TJWubSqCcPOH zl`Bq)sO(TVTb)t}#Q%MpqTu&AeA#%z7j9s`M&CCqe#L%6x>x5pxrA(=N1=bKC@Ms(GeVVG71n z>}gek6n2lqx5ZJ)1Ax(|M29ZM=;h?0QraF7@8+b6h2zXQpvAt1rYIL5Ss@b+;{r3& z=_GBXAxUq``jK!W;IkS(a+ZD+K$+L5a?T2Xka)N)E&C#YAsY`sY_lnD~mmFDyh@@E^H2=q}S=@rjeUuCpNEg^Ow(IGkhGIoJX*s z0E;j$6k+@#m3I{|^KCJ6T1QX;nzGm(&u(`BlS1#6!e66I=xK zGLA=_${F|*!*T&NtQ2x0(o>q79JCq0gk)Vmbe%m1?uvYaHNXqqcr8?2F&v$SUS5t> zc1YHDk zEXlw{v~RZNFaC525Y18wWIbQPYFSWv4Z2IxMG53dwj4`=Nh<1wLLWzz*?5@ZRD#*6 zI0hH`va(5a!4}%t$MMVqJYiP@T?|}?@g`6IC`SE$a*suznc z$r3VXn89V0s-<5Hd|RQ1Hf=p&CAoL0;&<^C^DkKQ$hH&5J2cJ3=(Xw~3EsVq$GJQW z7E{6i3w-hWCxA$5;Y-=*BmR0K;Wbc(&`9yOTpE$RTM`SxJJF;%fOw=~lRI0UOnws=Ad~2mb=NWAcaf!-ygNNVAhKIatc=$YE zLp~)QToGOJaU9kBDtott3pz}ia~b!G3PMiyff?V9G^8WAAXegnkWu)GQ-YA-suJNZ zP6jzBl>>4RX`cVz{=h&0al2dKB+7t=lqy3{TVj_ zBL8x9ieNgMME=xd;JDTj`68?e8=zmTaae^k2p?l;d{HIWL=hi0K+*8Qkn$l#_Zpyo zIvYhtgA`3FrUr7Uc-{|OV9TBdY?5d$!I?`E%_U58>D_1VjvP=unx1K12o{901r8TfhzRm^FeZ)gkUbCV&%>(IAm*`1 zRwIw-i9Ws+(EUYjew`Z;BX6z7-cPv6;btcdKd}N1pU2)QYW%v7VMF7=$%-Q)J|>eB ziT&WbM3}oi4CYc^N!f1ho^2St2(1GJcaBR{7ybw6kzC~JMbRQ-NDfKKLYN5VgvT_l z+EiYz%6(czuc*lL%K5H}lvA1HHtKMEo67H1i-y&zHdV;`xq@aqr4R<1QLg5~j<_hc zUU*c$1#ailDvtHa+STq#L>|#mOR~9$*%dUayRN_o?}$Bwxoa?CRZONZMs9evzzk{2 zh2pT(cSI_PogiLr#QEjSM(!qUp3K~_qtk+N@x{}q)a0l7mwdtp$&d9L2>`lU^7a=PA zI*nHQdGaf>5+WvjQ!-j+-Z_7TF>e4QQTmcv^BO~fr4Q5TU39@Va~Ow$n;w~?!WBPC zR5g~RbxmHAY`8VZ5o+V~()2wM3pv^I1SFFVucL!Jr{^reCkjSXjYy_fN~a8R^lb!k z#R9nryoEH#8D|0U1r51Asa;^2=78}frvoOFQ!_yoL;7?5h1DN+p(uME6iJEMCuoT4 z=-H^$15p|PQL$EO1wEOPMI5NNrv6`#PZsg|^K#C0f4HQd?vsbff7Y1*tvX zO6@~dl4HJGY!e+!G-z1C5!_H)-~;=d+tnRm zS~n@P)}N-#DmY>z`rikQ@H4ZA-b(Nhe~y2%V(Fo`bNKCA#0y?h){kUfjQ;(Liy^(5Q8Z0LgTZ})B^M2EyI^z6EjtO|c z5!_*SS$VQj7GtyF8DuFoTv1%2;Ia<5!3JS|0ueXVb9Csgh`8vUtQRf0V2TmYz1Oz6 z(m^*iF-02G`Lx&U#ZLYFN>-xwW81HtiES~$7dFN%QOn+BwX-1Ei>`?w{Vkf*qOm6q zws}DqM(cFHJ{R3S_5S&#Q@|-2-AnxwSm1Q%=h zuzWdRZZ>SDZvq3rr18zy;tANlAXIb>+we`1swHk~@|$YrN10?qUiAK0 zxoW|bZ}et@g@{$eVV0Smd_A%%A3LKJriJBDFB~pH)zKMc)0}vF@f9|q5qtH9s@%$< zU~wU?){BLPK{8L;X%vG-Iy{?=UgCawDZ|xPvs&r!DNu{hP02@Gx{;%ITD z)*Mxxm(&*T*U%-k&HFWQNp01?_*jXyF+XgV;j`JtE7>5Pmkr`)@^V6iIw9`L<(Y#x zs^e|4cS}(BY?+DUZ3Mjzy5+B%BG9Aptd9Xf)slF!V)i{23n=BJbc1KJwoz@o0%5mz z4ro_9xc>>TmX$jBP~W|GM8|0mEvd=35S6`Kf~e4Ora&8wHc1)9#D)_({^o`$17>+T zFtv$hbe0QDtiVHHA_hxihM}q`RqNUhyiFV$HT@cHj2Vzp>&nLWgm9P8?H}Av;_@ z3{mEyYOWhMeJ?thy(I7Kky%38?vbKg)5rof3S6EWHCg_5+?3YnC0yx6Dw2KgsKPty zWtEr=?_h*)qIcMtbFi%nuCU%%N+WKlRai0xN@J*&}VQD`=-b}EoGL?RXL2GS5G5kYZ`t`J6cZ!bW-Bvn^GvqykE|%l37y72 zYp;DC`!1W5F2JuG*^n;+v*vS2{<&fs2eQtzh;l_Bi9l!gIai>?ik4gkQ*v3rmdo0; zT=qj1>sKNF5_cZlslkI52d@ZCp_EY+G%6>f&brc99>V`Nn*Z^FD*9U!mKz7=r{&`* zdQK50quDt?XI1`(M5l9xTH3){ARco zIJ%3dg-z^I-?$>up1msP$Lg-|<47^1p0{1){7Bc?tR*4N6kq8*29}-KU^$NOZUBNV z-^Krm@RUt}@7P>2x4#&mq{D8C0LeB#YqIe4m$1h=V#hJx;5h9$Q~G)qbJQ#-z*o8` z4mHQZV=^ToZN5wN1E!lJj9OBSOoEA?)Jx<76W&1-CgUyaxP)x~0{%H8Eds)Aa&h>K zO!x=QHsBn7ZRQ_5u3gWUA%*?6}2)-|!z~ z4|Cr^m51LXbWDce0?J=D$oWh*6 zHv0yy*6}Xc^Bh|8gX+W^bF=!gj4ehMHDQy-O$9f_+^{3CE<7U}Maf1{vXRcUQLX5% zYkzTI3N#8@{DS&523YXt=$4-Fo4&}R^#!pi%yAMZzg}g;5mZJLW2&gW_yNS!WDjk) zoE61t!O_V+=MC~4te+_pKOH;>1%@Ig-D1H@g3z}cguXW$LgSebO5_otmx?t)B{DJN z7w}6XQuaJVX6+y;UWqL7GgbzCkwc^8P+4|eyZ=O$cK`DZPXVeJ*17AfT6&Wm-wPoF z313R#2Z_OAU zD#(Nca+T#MBIBVF%~jvVUqQ&81K86Dkv$I~9%o5-lg7vtty&!5R9X0qwOH}9+_1_c z&V+cJxzaexvg0h#kEe&D;%zya{ol8?{2k+WetmRh~gUy^W zzE(F`{D6@sR{?^I@+2b$Db$IIj%p0bo`=D#!d-f@llP_oL+fD0zR%*M~@#CEi4ls4GjNlqFHhk|j=L2NH3P70CEqfoI^4Un%uYnmgfY)mJi?Qc8H z!2V7@?@dF{=PLvESEfCmTp%>cv?OsEtx4yWx>Z%2*nG4**0dqYaO@P>Egl;j?du&(b}zjzR}gUPME$-yKBjh~$(UB|bcx@YtH&6^gl@0I_LrnXE-el#`0p|9`RUtf#u$-dFPV}1R7snc7J_jPwC2XU-(^z>lY zaa=YuHoAUn@G*>UeX0{*Iy%ww^ozRsJ4cV(*YyqYaa}#W-##$#>k0RfjTMC!!9!9O}sR*V`!*9 zUig1H@Ii4KhZVD&p9?48thGw z$p(kIF|^aeNq$x`)RhD$b)`;W+&x2~{X1*-dk?Iu!mC>k;fQ`8z%$%M*!VGH;F1j0 z?`y5wUDuLms;j~11fJx`#6V})$WS6Fa0EMBUQTok4R(hPwzfCcB{sRA$q#C&+uz*M z8cL3(Mmo(m9<1G0d2a%EKU|U6*}ku)slHZ^++ayCG?0Y6;%CUC6JyCl|7a=!q6Y2+ zFBE-(nowP1T@wNPV14VJ#NIl{wZ@v=iCr~~2l4uveTn@wEj3Mbt@xVO_WgCC(Z?ac z9;Ngg0!smi!<{3Y10jf}?xB&yVCO(mgG8epunqNh9!vJ?!Eg|%5Gd3N9}EX*hhPn* zk~k-D7&$czg6-~396ODJ^0eWheh_sRWC-u|uGJzLkL>Ck?(Bka5enZ?zmFr|jlrfm z@tqhNUQZ0C<8&f=0=0DZ_KqaLJDP3$thQ%o3)qUI97%#)hEJqMh2BLpSb`@N;|fE_ zyP8{?YFZN;6E#|A?5x{WzptKLNai_sXopa-k^Ei4!QdWZDHQ_4)a~1m*m)=c4y)H( z9~u$129)IO24-W!259c<9AdsM_0I0!{L|1Pg-rb!X z?HcJDrdSEdr;KzZAZ7d_CK(zXJyt*1ojesfh6nWYr$X}lV|{}pkX~>X2~p#|e)M3f z6EEyLnLJSMlLJCq1Xv)g88$jE0MmcX+N#K>S+4THU_HZjiW-zAa?DksHb5 zDwR+-KJn49By2Kj>+ZhJURRh+D90d>wJbAqLlK^4TY&+feL8y+eLaa0$c-wo89jh( z%D}}|dH?-j_N1vz5MhP#l!yE7Le}F5W#w| zwWbxaVq>BLB7h=@$Qm0pLg?`XL}g#sX^q}d^13DfNYw~6**6s0)7%8z4+L7~;?QB#Bzk-p-ifhO!%^&}6B7)TuHzgJ z<-hQJHUUD4wj4eT=HA(uXgSz&ASg&^4ij*$=@3-k-Usnp0F5!ow3Ms2)AgQK;D&rH zQ{0__2B0)Vr0Bm@+IkbE;97=!vKS|aE2#{uY2&w78aF0-`ubDg!<48qU>pLB<{pGO z&6vTlfdt%(M?>JWOivJ6Ihfc7aX(JP)->&CaoJMj5>>W&lbC;kLQ>k{szgPlH99oa zf#$StqB&YV4eUJ7lNe6HzsW$iQjp=1=HSra?x7u&O9N!qCKzn> zqG`l1_S;_Cn-YNeqzGt0K-+vsASi;<+AL5@w2`{KZ-RF~v-kKg;30xK6CJpuZ4K<< zZYV4sHLinSa=ARP5s&o4&H~Gt42c0CmJSDnu^gm-l9cZ;{5hU)NE7Kpgju0QTfpcd z-=sZRtT3<}ytICLM&p$`IkyUkqaDKHin<9(U7)21Tz zc&X7(#>Vq3QK-rB{=0SED>w7;Cr6=5Q)H;YUSRXl+WLXcQ-XQ1las>M^f=(o)2KIQ zR=PPsrChh~P*YtC+?<_0;bvV4?JdE!fbte4F!Y!){74c$uVWxN&^3G-b}*f=OnPp2 ztIx^tOChQieyp*1#*-Qm6l&gA2d6F3)Z9`B6RWj$4~7fp(LmXAt$4dJV=ZFMz=yQv zB8FYFz;G6TI5L>(3-zWnf03C|0e8aSXf)Y7fZ#|HUX*qO8+RKQN!%qX#hMcP4%5(1 z)Yj~;sjY8q7k%5`4_5}Z6XakQ1Plobt}hHfC>Rdhuh=9sWc)bul^{lremr!Ic|mh- zoM^0a9b^(*N;p8iPYkkg28l*sUn)tbm@?c)6oQEQ(b~>bQ}Ae#szSn6q&K?MFyAdR znBgTu`L(o6^b6Vq!7mwap2I!s>xR*8KiY~}+VS0EuKK1QP7^|dS{z4+KykXL z*}=7LWQzhOW&Gofnfzm8^oaS83W-1t+-!t^5z&xoj19V;jiyEIZvy+Cf)&+Ewd)6- zE7Uh(RIc>b)JcWYcrej|K!yRgE6l|fA~BHX(zKHH<@D?QhUdsA{73geF5aw7fW{#%bL(t_CgTQCwA&9hyJ4bf&?6X5+N$n3(&^S7<19ykzaxI5NH7Cqq%mlG8 zvD|y+Shh z!eAH#BF6{mMY&4b2X1|?wl37t+}PNB81a?~uhQN{=She}03?wbN~BK7+dQYx{c6OV zx`xIO!_+|w3j|TEE_sqQLQqy(f1NlsJW5t|LuLS^_*x<&`|)VfRxoHtul1B`3yh;3 z09eA5VkC%iz>w^7lU<18L;UjDjhj3ZnB+vyH{C;n9~>f^it7N>z|Q2$A9r7l=XUjv zCWb~C#W>anRhvq-Fd8BB=@PA0HMDaW@?ZcENHaPFkv@E+_NhmbT|;mMwddp67v>{; z_5(;k-e4nKcruNEzQGXDDcqB8`cGsL3!v<)VZyoQ#R!4|V#mX2wEyR(e3mMF-U%sm~vv1k>D+3Y#PV0`&tsUyX)~*!~=XWK@C2}PK`N3 z>kFJ_Lc;(hHMt8IBa4|b7y^US(CkhQAXqCNgAIuhtKA4~(0FqRYJ3olJ_x*Y4|`Tl zC7SnIMdXezQ5EVw4UN;+6+-+JmJ7qQ@+Cd}@cQ8mrb}H{;NOiwOswnapSi|^M<+fT z!4=~kIw-R&mh=nkMUZSkt!?dc?fUMwF0k;vwV|yyf#DiH@IJ^TeLjd9%v_N&Pa3BJ zG?|F`%Aio%&oNEw{=}4o2Cb-L2;0VxJ!xTIN#=B2U5U+FtXoTfMuIC;Tv)g=OZ!Le z8zf5YdI6&ROo9XilYnoO#t$BBnLfmqfxiKF@sm7kj3x$0M?3p$9M_`59Rlo<^wdL8 z2M_lTK<##GF%tBQ@KzsBBZek73a_&YFcUMVErQg?v)4>!0*`_YIE zo1~#|98C809zQlTHUej_9|^8w{W=)#XA=U9(rABkBY;&~e~{5>Ywzil52f2^*B}xQ zF1O+n5`bH3GifPO=(4Mqp1I z8|z67b#)oXtF;AT2Tvc0G{H9|I_eO2b}7_%g{$w4n+*t{ENQ6rOc zqm2G{BbB|ge%G$Lmb!hJ`5P%f=pG@b@yxGJx<;4vFDxO324HzGA~DuC0=-V7*oHvw z-ksR7Q|mMdg?q_PS_5gR!XOi78H?wb!}NffghQZH8$P>)0RqAa5pFNG${}e35>NI$ zg+cfPa#k+n5Bm%NPd~VlO?(3~6V(&RAyBx0r=;ZqhfSs#g!l$KyK32}SQZ7sCj2tX=7Z3KUa8DrCELoB? zmkh`d)7UZ@)zyF)AT<8nM>1;WkH9|IQr}uCxP6Xksio{AF6FVgs4e6 zc13ImQtS8aYIY+ZI(Fs8M>z;Bw%I z5z3uL-RVq1Ri;?LxBw7B*GQ3zZJX#7^y#_m)HjT=0I>nj{l-=IPP^8hL*wHuGW_Q{{M0BEaWTf*nPG``S zybHz`q_~bVxN#rNn^Z8aFxJ6qFNfj@CQW3TL|x%u*U|#rYDQm~DGV__(lZeibPd5o zw1EegtucP^o0VxcS;29S_omWxsf^QB5Fv=jpl>( zVhM;m)A0v@(al(DP(VzWz%0drR!c6s0Pb4%8vTL~q1XdLQCgsbn_wlePYof9(11pB zqn}z>iki@*VqR;?E&vx6^C0q@?CkFE8>D6#IRWcKK9nyz?Zy?fToV0nb7F23Q@=(J z$yBMh!Z}Uv^ohJMb&2Ky^(*sR5UA}BaH zf#h^MOdILi3xV-#cC_6X&;=ZZE>NJo?dAfc$|~K$*5YblgoQY^w!#IViD&Z;AvS(6 zof#3z8A-#MidtIBjka#q8p{w^EVS5psHv%)qLLKC>IOX$1`|gaWep{}5o$7NF%k!? zRl1`@BYo09!aL}RxG_8TV{CHDhfqpE5n}MSTy7GxnP18L%GhuZ0Zm24@6D2?mGc@O zqySKmF1W+6t-AFPjt~)K_#|A(Jkr`yLud5npk9-uGb!HTCkF7aCdM>^EcEXHZ(5mL79%P zp{g2%6qE8pTW~YT11z#3*#Z0HlHHBXJ4{&-i_6jr4gI{uSNo!|0(4(3p_Y)DWDd1D z@X*<^pBZIMfJjIT!DT%T_g&n7w;E(3^8obVmNs!Tux5ptgQfv-gQ81M0np19D@+SI zfX6MCJy1KY(gf@@so|K&6-wF(OE1z9)Q)1Oi(CZQX>Q|c6oJFt9(n|EEtJJEG2~%} zmmnbJ>6$?PnMDQxtkGm)O|dalQI!k;^dl`o7Lz#!3^R`;d(G=z%OEriN}x^{IcZ}S zZry^%ev~?9|p< zp1xB^*>jwh*AjgL!^{9_lJta=gzJtEA=725r^$4l2sPrGN#e|owHp;C`v)oAidvU9 zZHzrFM#FC;0ESS+Cw7i&8QJ0TeiMtJ1#M9&zOKu4op5~wD@hEN^>Dhpl7;QQitjWG zWUqUdNAIY23+u-TMZ}86zZtZ^M4OFVyRK^8&Ru>BItBhZog8U8lxV7NYGx9Rm2zV2 z3w68GYy6}$Jl16_E0VkG=ZSg-N%J6Z4c^TVDwgPn^}P%ri7y|ZMG00SKEvuH{b!^8 zvq}HiECF?^pBY@&`(tw4)`^3{c*^}bX3H2bHOPFYSA^CL5rqN!1)5)?duE}v{6QqJ z2t+Sf>($bNj+M(apR{UBfvR=D0U$^+Mx!BAB#JMJSW1*p*k}reOtMmJ8QEu*wJnpl zJ5y-HFQw9mFTEU-tYsC5k0d>5WaD&VR(d=tb<-j32fBplF{uvxX7__O89xYc)}d9H z=FJceh}TJiSIn2*F{JJAl}0;Qj@3oX-cUqb(D~>eddxGxvkppErTc~|lvsDDwWTJI z=`=LW_gX6(Hb%xN6d9}(c`RKn6o)0qO}fsyc6=$kB;u39kT-p!$J4@$0fSN`0CidV z^Z={8IZ&Em)ZK>tMEG#UL>423#^MqQ;sz;c){aju>Dexj9vS@sz(N?zjk3GC z#P}9ANGH)gv5zdR`M(n!Z$b}1CTr}ZjtRn>5gXWnx)jOAIFrcOH(@?a{Xry#G7zH& zKpO%ONucNAq{Weg8Vh5Bi2@5oO6CP^HfmXW(PIeC=^OFl;MZ&s^5(agwMnG2|zbHnI=<3?f!q%y$gJ1*Hs^SLX@WiabmbQ zFK0pm4W#4oXvR(`mtu`ZGZC55$kNEM!>|3U$0v=jq)+IR#^aRU8k*9_E!5CbAZ=PV zl$M43I%a&@tHDh0&ur9IAJL(V%=cw{k+t?dZ{g!k5xUSnpCHA)zE2t z^pOx;vCR)bc@k%~X5+CIb|7VMwmKFz@L;=Fs#f3wrdt0S*(m{C7H;5M)Zn{vzMMr+-A!gDp^<#)pgIc15>a>{}OZ|veiunCM{8Gdm)f zn@OYedLtp*I(VKwb{uroM!JYCYx^l|&~4H%uQ_9h_V4`EkTvpqGrqY5akgOD(%+W}arr6wsR-+kzG$&Zg7u`luM$Ra4st1_#&K)3jpKFAqY(#-hBSsaWaIp-99D-aa`i{h2`#v^ z{L+AndwNG81q+5K3=9WyR!{pwL1>>H;#ndDLPm10W@yyOQ_quEk6=oUb7MO=&gVxY zIZsNIHR-7b6xoV({zC8cU3c}k^wdMG!xV_tc4=~ui&FR!vk~emA!2YdjBAqi1~qJ{ z5@L7h#$WDN!qOIZNW!PN`KxtC76&MIP;p38CJl&%d3v4^VoA7PehEPo^K4iKR|(;x zvf(f1nV6lzxD7k0C8AEwP0z|*0~apM;hg%m%phPIeaY_RkbI^j(~3+hP)0GgF<_yR zicXiM;x>8W7Y!h%S)$S>7ba)9CWgUKwYT=nfgUtB)qzvO~DEY zl^`3=Oklejnv5Q9BACD!Y~iwMn07!G^9LL@nmI=;wv0it# zT!H#wv8limSy*MW1}~qx%!X{jK#UiY3k$O`V!*#ZnRHySq7Ay9SA%?aiH=B#%w{2b zu*?V~wp4ymeX?-|=#AtrDRRv>JO*5s{&=Z5XWZPqpDieJU+y;2Bi#+gWOX9fnTzb` z%;0hnUFebg$zf8RQ-x*i<=P7l2ypT@4|cO^XsD@ta;~*%p>M{{i0%zx1Va!(07G+I zEn`In|1y~IpdEHyEDyFHjp#^CUAI7GacMY#ttYFi6{xRn3~(a?K<8vkhdn4gEKgqp z)#ZofV9Pq32uB^*mF@2=a>)%}+CkE9cCVR@^D7k=EPBKBw??c@Oj#$|tGI9nF~G72 z=H}v~i0im|hMVs_Wea2U3dedWCfpdE(9`@&53Z3LgmzrL{(`^^Ti=4*j-V?>mvJ%2 z{PbI<5?IBD?$9t%g*8F@+=xNZhSzXh{o$OAV#;2ezydo)F&QH(LYVl&9K;URhWLvz z$Z7YiEcqNhvWVCY=N;havg2gU6C{yYf?`5YHvO%Q#T{%v!7vp~2&5dUbVAq^BfmM0 z#XuaFOo=SJ@EFTOSmukpW%&bo0R3%f~iIs-b=p@j5thVf&!x*a% zhUyVbL<5Th&gf{$-DB%Fu->rJ-`>53`6T-55EpP@`GkH~k@E1Z2r2Lx@xijZV#g9B zHY`D6NN8bexV?d?Au!v(R2{gHbBU^Tw1lakHAJZusCn{lVPMr6=9FqVP!uc9X)Lc` z;ub?>XqqMvDqPUPm2Ior9IHEq@iu%GL^Bvb7h8lx0TC~(mNqfw(>oLbLrYT$7_Ti& zbP3kuR5F56$Rq-!|NRs8~-U(AB~95Vz-{5x@FITmx1uOkoyob}o~d;z6RT zkQciFLRLNd7XX2lbSy%WihsK5BQVSOxrg;U8FydsLDUQfx)bvkF840XO-=O{W^f7G zr3WccuQ#L-Q1)UbVC0n zb_R20I4Oz1WIl%#IF8M9)eH+YoW_{Mmb6RkLS4oO4#Dt70qTqX601zTG^4E-ot;v1 z{jWE`@Iy{uqhmlIrd2j^+(;_sYUy~W)0^k^R%<$7;7TUU!d%862m^wi+G5-)u(0%7 zyl`WL@~lJA)58syY$P!b(A#h%B5(G0uMJiNEKWE?^T7@iyb#-6V3y>JaA4inlss{% z>A5(zijh>s8;m9=Fof1~MYv9id^rHdYy;RYa!;{Uy4G6bMGe))K~`^Yl8#&!W+pC8 zUxeKQKg>@^8io_+uUy8-IgWq1B2itovmpyp>npSUo%y{bx&tI(CDe+At)pHn13(qz zbPiNOkqx!fN^=_e)il>P)Wdt?UmV7IeXeHFPn1iU=JigdtWuTC?)&7dgH-d=Qq7a7 zeR^#nZ1CRmdYNTmy6iep-1GUKHV@8wSzO8`3)URNal~4SH{&Wut}9-DKwuyD z+{b32X&CPIx9jx~jRm@GgGZn|oMc3Yc^@uX2{@(bHJe5i?V~f4nu2+r%aZnB&@^kX zItFQrYn)J;n8$su7@kkQsmC+=7cO0Sa}O5@ppTe@84=6QLYCn+E-_qPU&fV!JebjjcHdR2V@Fan~VsnP$aD2b z)0`}tTCHHEl}cb&cUjHFuv+SKYZA_y4EyLWb8O4}5{NMJW3U*;GSQjbWY8R|SbJ5b zeW?Jkt?w>bkZPeIq-*s~O=+&VH_%N)hX@0>8&D2}(xG$Yc5Y&xXJS#kz3Jc-1AmK9 z(=*swhTqYvrv11shho$oj+)HzxB}&Dw9NCeaexhtXas~-2Af=8=EY2mv8KHX-}hQTT}URXpfgZ99CD#Ejes^J7*+>J6CLUQo);CjkoPVBI~F&$~|nuLj73HWo`tzV3(&TsI)jUS9ta8d}a{!7P_Z7wzHEP zl_er4WsS%_s&^YZYl%jqjLMw7EAD-|C^sWVXL;&OIV#-ikF(oeM3K-Wdpzy!05^wM z{mj)K6*Kk0w9b3Tvfi#ltu)W8(MY;vGVV@=&c-`vc!F+L417Z23ex6QHxwa+&pd$cIXNx+hIBA)i13D~^V0#Ph+_{>a zDi_YH5Za{JA(M`;va7faGCG*Gu1QnH#z_vA?NVP|5lU#8rzteMFx$S~V0G08Hk3pw z9DBjume!$m=K`Cp-Q1k#cCCf8mv(f(a4(}iF~hyp$E9*Izgf0J%B3AcyIbsb(l0nc z%E33as3n&1Cq!lg%d5weoQ*SxszJGaUS=SDT)@dxT%^s)!fuT9U;?3f?;z6Q(X<@W@XGnsnp*P0b5(gHsVH^>MaIxiN@6Eigui|zS=n}i2#W|cR_x+qCHscR zSIh-vmo~==#U9Z=;GB{NByN%RZWP^pZxeGCTIn$Img_|6HL(=h9$F1VDK1P*U#ceN zE=*s--4t(<(S980KLC?l$iqHKpL*cx)Xe-ePWz$9r}qGOAG1FqU3JED0P>$}}8qb$#AyJYBcd1#w^sLAt2 z$lH_dNDOEqw2Vi+GmtFg=E*ZeL+tY6}F?m&m@M;V#tu>Y^xU5nCi4RV_gi z={V)O?8YK(3-=O>S2aGnfT@ttIYRefWc$7+Dqq~jiPt>M!1q6}UvjlbgE!g&n-1<# zy(Jw%n)jbns~cFLvy(nyLM6}aXvaK;4HP?d>sKWrs%Nk{gbHX;wIlqnrbMe?QHxtk zXfje|(lt-rX^6&`qxab>JT%!lu_8m+P+J?f-f5I#2hj_#qkCm#k|e3W(KTvzuUBO* znRC9PK3RvrdRY$-f9&EA8w0pol}2Yv&Ld0S=I3uL6R4dSq@^n3@s%*EhiNI!)_E+` zeU37uqB16C9@LqP;8}9|0%jjExp7viokbT$8pL%*erY63Ey&FUOD^2;W(eun40H*D zHDC1;ttQtPhFc8fkVP8(!3&aZQGfIMGUuoA4g=X~;xePU$mV{|{%U_O5vW(eedj2} zI5HCVX%03xQdBb!ifX!;f7A5rY|+T2Xn$a55hE~&T+VZ=eom*>NNPf!9As@sQ!`ob ztS*>8#6--@`H49!B3#1k?p1{M>MC|8&GVA1qEcM}nWUSiKo7k9lgh{MS5+1S>aj|b%Kn=S5kTlTP52E!(@f* z_x5j@kCe#NE(uhEm2*9$*}_ueQpjp$O)FC^Nfc0m(CRW3ddIEwLhg0rR^_uzuq{!z^Nh<_I~}?WO<1 z@|uddf5<=Cm*f7Qwr}(n@?JB{{5xK*TMof!UeA?)16DnioNtSAcIi8?gF<-^1+EUv zBFN@Qm9QZ}QIvq#QZl9{^g8c`aw%kZ9Vr|N!!}X$kI@yxq$RO%HUd}K76^N1#$`i2 z?+_f|I==jZKovij$VH`l&hE#J#K5LFU&)-M6yX$dGqE@z&g8u}kfh&NSf`aK6IZcv zNqw_BsHkKO>N$T~t}-=Wkt%YXzNZT3tA_Cdb6YK12r^(>n3WAIhP}NlPU>d~3JaA& z#Eb9>G^3tx;S~mIuaTKb+GXA{Et1G;EpdVqW|U^B%S`QrEmteWGJk26GOgxmBr4S^ z*3BlQ_s9;$INb6bg9tVT>LpmdLdiwtE!n~t@o2>2yOt{v=R(+3>_MKqgiSxaiP>4) zQYNp;;d=TDvjeuuaUx5i^A+rD-CM%_JL}wvR8l~1Uixo@UcnyH`?1QVqlS4b<3~@$ z8X6{D3ib;#(-@l0PhdWvfa3)5{XE3SAw`&D!(xv1ZX2RvM-ImboMPe&gF3dmG8_2Y zB!#XM5tp+fnU0n-zIB(@{A?n#*jbVj^R|XeOs8i~OQBRJ3#heOLm1bDFX)JKsZqU_ z34Sx=11&+^Il07wE2^9U0*lKR5-5+3B}dd&4ad}FD)7X}XTHCinFRgd zpfE7{cGH*fc-sBC6_|5yEl|}cjAz#eMX`7!Z|33z(vk)!oMDc$-i|=0%rSS6+;Ur2 zB&w~MfturZ(mE^{U}L)^7k9D6cL$p-Ib^l@72+xKHcokBp>J*X8m88D&aAijDE4Gv zbOwCx7LmT0=0JHEPHeWRN5Z(-5pP}S_5a|->2ur+jERi?U{gq_II_=PS*~Hm!&q{O z5d={uD7XoSRx8gFi^|B;rm1!oA7N$zvldfGdb~dTAUl}B{t<3k7Ev%8n&$vS9?~w0 zMH*nb%PxK@C#A&A;n;dD@}cBZRfb3=?j`OG7qJz(sDidg5jBp5Vv*S%KSg&j%ovrq zWf~#73MynloZ(th;f4^ROf+S4S^64ACYXo1j!QX;{HA3J#JLURklEPb!|T{boNQr) zyf0hXti5gr&Bm%5&Nd=vOIp6=CCvNgY}ot2?dbF)r@VvRhiLa<2T8L{XFXi1-a}@* znmfQ8E;HbU0g#sj?3b3`$OJyvuf9fV^W3Q}%rJRo)AO(!`lW`7>@>Zpaw*Y>s^)_FX#xMbaBJc7z&2l6U1=iw16upJpT9g%{Ku6~ui z&*Tx?Zr4vBqS4LOQLE)8S#PAj?(tm>6&tm-?uwF0=PT(mB!mBP%);fAr3;&?Wh#R> zisLh_@~uR}9mY)D++j_uG0=rkfa5*2QM!6qjOQC9h| zc`@MIa$fbo?78#3b8}bTge_*cL>#9DvH4bkSI$2u`!(_U)p-TsDta9Hnd@C*n_oSG zt|R6!cKSjW;dc|;maju)gB5JVPkUr>!VmRqi1RtD64y|{b0!OyDX(|wXdw{-`;&X; zCHg8luv831KLt7~Y?EMlkidfX&NGdw4exx0POB z#OY78s@I2G$h)<>T486s^?y=wRUF>Cv29np>_Ehh#F^c2G&D>=xe%8&C1D9xZG{Sm?K58>blh5eWZ^Nw1zN` z2_QVZsaCzbVv8$%5*$a8A1Wt!4^i`oMmZKSH~8o# z>WV-G#NL-7MFezS6^D=TyZXVjQ0E;fEeLF2Bf} zlzq#;xk3rknH7?tL6^7#m>tB2EA*Xk0q)LF$m>Zv@}hFFCym&$43F~RfglucV{a3} zx?D;KUrMFI`7La-WTq^w;^@r5cxEFyGkI8`T^)uc&f?cSoY7gi8@>h2$m0>*E!W=~ zK$>gVHKAPa%&Ja|MJ=p~sx&8h2vTyicQ^~I@<>~w;z)1ny5fii6yv%+plUXWw6iDd zvR0Eeb!o!^bO2}2@xG2dfI44|J%Q7lkcf7a#A666eqtR!V-Anzpd6D%^sfFveT-rnH+We_6!a5*MNYX^<;d8~bk zMQafLhEQ*eMGGXx^M=^E;_>CEb^%rAA>&fFo|W9H4`sgU4r{%M!OD` zEanw$X^k>_1~n$LM5mBc+89?LTo~cG$=6&Hbt3dbbKY1w0msrs$InF7tt5Z%fy@Qwb*D zc!l@t)hbG+HOGY@5<;;Qw<+Uwnq3e%PDXD44I5Ti@e0HaTB?S5Rf1Gmx${CB@_OMV zrwN#qG_2szn33o4(=x|i?HlS7(Gc$1NzvgO;)*;LTkCG4qMxoXo@7%JrUZQmkFytS zYRe?wIL1!yCXTs-1x`O&q4Uj|*1tz5uN;9}XOW%1B^-v`(O!v!)ts3XtjuQQIF!?E zkT{0ARH7JVK?#L{J7Cu&cCoF36`RA5W_=|&L=(W$$rCM?awX1-GPAU9XLe+C$G!-b z{n*J^VnaLJ!=XEzy1+H$oIJJ@7DSOz1p92j<0Aan zn>IWmY-^*s0spK>5iR&GW{7$J8@X@Et^0ez%HO?#9qk32s7!|y+B3XDJqF9VK_#-x zSyhH4_H)X?6C9MCpq@3!=sxIKq#p`DrGn`^x_cUouvOJ@dg4`i;8Hz+tEjQD>rFkh zvh#0}PaP$dBs2ksDCGwa>d5#BnUQzq%9}BRJ}XK7dG1Q_IeAGwCokb~1`ND-J$8t8 z_PHgG{H5}y%PCjv2_v^99dcf@mp zu4T}TlIs}x(%&)cx9v){jg8e9HOR@SFXXV#Ta>U1Sg`EJcxACS`niE(b&%nU?w{jb zdpeT~jUWN3iNH0C&M^s@`lp!V zU_om6+~K9LIL)-Yh?_QT;y&bMufRLE_~{xKsDBLF{8%Z?c5%)=)hvABWK@xFZx zJeWG!!lOJ`8}VbG(m2s?=C4fgxGut~A1=pLG((r#bu`2XpOcuJe&Irkst)t2*qMoG z1Pl!kS9w>8l3O4-E0~k8!^SnJ64q=yGGNKkG?)$MsupM6So0Pwi!tr-ymXl>XkEKo zoa)9Tcd`}-yzFFJHupyBi!0P{B(Pzrm=}8JbcXhrdPcdo%9D9ZK_)D1Xca>J*jZF; z<*7E?|M@$TG1KsbSRM+aR)W$N^wyOV`W3Nc}jA)i!liH0_oKD^;Pj_ zocP&YVGF<+=JQkM7B1TEYfY{gy`kvSxCeFX`Z{ha;?CZl-z|VB+Pf(CKFV=mT0}Y) zm=T_w1;~3BlMVa&ICQywDr!dB-O&jEfj)VSO_cUL+ukiwhH%wdbQ>AUYk{_4>1u`CB)^ zOOMTkPTe+=r#Ojp*zCrIWz`uvd*|(3tL^CYkzo7W!G6fvf20BT!*ZLtQuo14qVIzf z=Ow7J5vXZW;Q*XSjt;dCIqNSczu6D9aHV}TG&~ zHlVk(iW;J+f>9V+@mfOpRQtVUwPKGVYn4Rl7WN!LH}{&71CPLrp`xfDBk)ZWWl*^9 zhzu4TMY*Y4)2BLGG;IBjtiVx-*%W&65F?>keYDw4AaA?Mo(Tpc-+3ue-Yz&4ff`^Q zPsez-PE_TRTc>i#>quhEo41iE25!kcH<3q>h@1EZE@#R!mX3M+QlADObhg_!Uds4>H1$PO80%%Bzk&gxU&i0nauWRrL z&?4?&_7%IlIEl5mfy?-?9}3lKr9Up!9b>iq)6X7|e)j?CT2z;u+iA35Htm9Afl-g? zkZq~KeC*nXn0LoAa|$7Zo3@_bNCSK2Yte#k$Z1uOCbjBDlUmiHN%vr?o);dqa6B_Q z>FnsFyGJKw?TaTiT%@N%y%d|9_A!se`$25r8m=7Lw*Bi~aOcngi%r!?6KU64_>Yn= zh~cgl%6c~WSeYn(bGOu>Sj&UA-QCC;G;mKNXH&d035V)gPx0C>%~@>=*hTZ3)#7aL;eGw9H%!&@!J^ z$@G-Gf*MYl1Z$BWa~WP;#O<1dFzk|)rYubrNJLU8JnrPe1k#D8^rnb{v&avO!vmw$ zDVUovq=&9RGgVwAAZKONQ>J`ZdMJj-)g;=>xPdv<+9rQSPX0ThBndO=Lo{vo0%lcY z8yUae#c1j-c8y_C#UFYE=ZYt7^=2JGGhzWyz6J;xjos|v*BltS^(D}?= z*l6V--~Qs?wt)(=V^3|S89XQ#0yFD4j=RSuXIgzVq$fvG zM+qq&f^v_fpxf%#IhYH`)a78^aXJ?DKa;&CTC-k#Fvy59f-W* z%}H6`2|Y+A6(c2}VyL2Kal2Pd7%4+e4K#A~&&kaSpVG2G3c~gI0%DPh-Pfhe1Q`!-73dsZhrqoE+||duR%l`u@vJoVWMP+od%E5!B*&&P^_?u{ zw35MEa|?bFDzP=iX-`CSZsbz&uAKYIDQxbZK93Egykd4Pr6eM`JC{J}BAIMMbK+4h zdUPo9LQl|cPqRQHG&F=JH~Ky7?ZPU%?-`1viWZ_TCVB5}qljSbf@I9FrOQo_sF(>t zv6sFY2b8@dF7KGxFx?4jSV)H9)nymwvHREEFSIc^t;#>^Udqyj)V6xVr9baisu_05EZ1gwSnc;a&KNMrEX@?6X4OI#~ zp_Y(*+=EgrJvvfTwp0fW5YGB$Qi?k>g%UdMomLYr-@(PYc;=9j>pW6IFlH`qOwB#}oNm3KLLZr_rPf&n`9*iqlaf;A;#k(GuB_+G z)%Bct&a9<=xx0qyEhW@xdV)xhi{{|6{mVjldd)lz_5ibG^3=*F?}*M|WhHDDO<6-^ zG1>X*sA2D$xK|}2;U}`l*g*zFj!?Cc&e@Si1}h0&VqEgrD8Je_MXn~@qEQYS4y8&S zWVQwSHT8=oIZZ1>W$(2t9mNgC%B)(;Z4Yrpn%Q8GV{)N4Im;o^)M*_e)t!i9*O96= zU zB@Ge}on(l!eQkYtUhj7xQ0#FqR4i2_DmzG$_Dm_UQaN((1I zbsQcYfN~D4aAQnrR2sj03*Z#!?hkGNOl!;zm|Hjq9 z+=cmB^T1G7W1^KWf?DE8NKPa6IN4AevC9dvJ#1JI74H=j8x)>b!Q68;&2pd7y`(1SS*C zPvCWRc_(@W?;G=)xEr`(49om_=Gl*UJLPq!Oj&u;G!=_&5z;3=ovycWETR`BRDl2$ zkyr|WsDx3txw$KIJ@hs%PDw%NIwnIU_cc}Pa)eS3-!JJ)!O1Gzg(W*h#<>To+Q3ed zjs6rClyKf!R3F8{O>4L-7~s<^6Gg-SiBxZ84+o}_pf|A3N?sxBXAGSXiVTS!B-S_* zN{0tJ5=E*=l))Wjy+vGYg@2ZCX*0t3l^x#r9x_fnFw3#8yjT*!qAhuR4euw(g`#|k z6vx%^Y%s5WfkLW2wIWB9uJ+DPU!2CJ@q7mK(){eiKKmhZR+kEo`ljvQ@F>^_U9>u$t#zx%#hlfC#J6|Io=SZIChUh z;DwttGUIW6EX=3^a2b~KhX3)DU*wNnLUJN2q43mJxDhM!#1-Y#3@Dp4*Qulm#b1O+ z13ZNnlQ^Zv-@NO8ein*Iym4^{P8P~}yG#?3=v)Y6*)%VH#jEP=y^32a(tEQIClfu7ssEll4StooqtH6-lh(6x^`##p6{=n+9Oq{*#|wo? zeXYC_G;{8_mF^$J5t`429CA;&%Td?o16!`on!h2TP)mxdoBDpZ=-tYo8&C?l zdbX7f&3hD%IFW#+X?_ey>r0l+wkyyLZp$)9vXZ1!>q!}!DoLg({0axjX{9=d6glQj zwVju(Bjj$NBSe!2Hreh;K+$z5Zn8y*QM7J_8<0S=HVWBjoucHD8j5ArF1m@Jgn}sw zZJge=k%flHgL0Lrph zD01VZbs@H-h9XPW!5Z&k!?-SLN|cdOS}k!YB{!@fOg&lDkqhGB=GZ?I3?h=0KO{ei z9h+j)$!mjkyKa?&Hq!GpC@Qv*oL!}nRM$ZR0iu;Y(T3pxuTA*sDy}eNN%WmjbYzAz z^YataRfCY0l5T-?2!u^qk$JBtG7(WBVAZAYc=HR(B?` zHJqzK$i1sx4I79mdyHERln}pyV>QqU&x)QOF&hePXsv;LQ`!@{Ay7va0h+2~Gu7S~C%MO;tU8X6h`PYAuw+&gETB8y0C+oIIv1Qq>1EG3jJ$urOs<-gVUB6eDSo-NK^g zXJKPj%|km!sB$!Vh!h3AEahdtn4(m1^jS(1DTluCnVT{Z^%@8yiv^g*ftx2c*y>-| zxs0o{k_K;965(DiQWy=~qzpPcS~Pj32F+yFOw>Yk@W7GP2yeDU2W~XN%FeM4B9n?; zGq~q3#BGTgju#!H9jy_9TfzsZ37SfsDnwJZgCjLm>D1~`4+8+ZDs9q-Sy6m3UJFj{ zsz_LA!3d0`a4jficlLE!QVVU5;G!0zUk7p zYLP4b0dO zqE_r9pwzzL`}>eRsG>#@h}#z0>p1o4(qPjDYEgw-6j^iT8mUHgXi*Ar+5#y>apy^v zEE|c4GP33p=_VB3P;{zTwK56fL241C>{qfG1DkiS{YnGKxE^OnJhJPooj#UyW?VOJuE6bZVeEqjUtGuKzFPDokfFTAvyR(Y20U$Yad578?!qS4 z2lWm6eW(nFOR|5>shm+wF=nt}AztB8UFp=8?ji-G{?%8SstTf0TP&-E<1nhpZLZRb z(LULe^0;wonvTBUN>&@QaxkMwPZ8(=CW7Lyu(WL`Y;YJ#tPNcjShh0Bs-q!_zPLNc z95OU1=9Uzs;Sl3Wl4)cE>wEJfimZ7-Z~U&-^aqk!zO?WjhX)fR?-}hSy|c=NMcNvu zJ)&krgx1oCmO_!JXw_V=ub9$mM5)an zU_H2PGGika^v-HHiB=9oRPVL6NqwOfhFaw7o!0Wu3a%rwKyn_I6Lm*y-Y#J%CA}@Q z7%Ap>6UKCBWRhF_3PlR>i*+riKR8Xp@+Noo!}*>rmWkxG=ov(&UK_*$Pq>j#XlZ(C zRA}Ci(ag&tGp>oRgrYUnq`kXl-Kue+J%X}A{JU>3yntsd_11On_Y)h^wt<%pAiN#a zid2q;4I{VTjohO;EtXSDDx_1B(4gUAMcGhDw4AaZIXd9z{L(q{Mn;PPLpIoWA1X9@ z&POu`?WT}hV%Y2??dS>P*wn4UHZGiORkui?<+94$rgFi`YM)uSbg5LLGcCekl*{59 zo2Hr^9A}h9FSRA<$eufrZK@ZnQt4$u1R)n8n1d;Ltiof#CBBs zK?p5UQrs?`0*5d3(T0>IjdneywL*upA;DfvC@Yw7q@$9VpD3}2C~4xYNW&4Y`P#ua zW(nY+VDEvYVl>dKC+?g>dMHvewZaoCZ(heOs<@vP8z!pycCYjnN{;r1Gi_lZUnxQA z_53wtoM~?1AxFUbe6}WOuTDoaSR>|_TPUu}6K=sM6XJ0wJgq`ol?Wz=oP|*Y9@{8! z;u#uuo)UxVu4M}4<2HN{otwh_1_EJ88X$NPe_ z7f5nYhkOSQkZ*OL$cnGXnEU}8trx&SwJcId$#NBiYq*C2t+f{^3QK2$usjLx4T6QfTX znzz`RrW?CQ#h)fl2tF~uTN8Xc zsUW5{Zs2$cvLiK6%wVH2aDJkZspcjepUuqK#Xq@o;h@w8I-uyPB|U7-#hZgn17cGr zUD#BDPDo+LOY~XN2|ZxVQQQ+`rLVnYSu0+yti|(_r3ISArP!t#UJ#^;u>!T;7zF(g z-msd2gQb)z@AB9~G<=k|iPXB+^Ef0akG#z637e&8$;3OG$Ec zgv3@-$|aDX!})9LtGln?zWd>8Z*8-bWR3+>FDr>dv042!gg`$I@?%|2#3F7m);<)& zfWFl@kzy=Gn|UH`VW5EA%lBi&W@=90CNk<;#!79aTA3*o2dG6CO#rb!CV*Ax-V(Rg zD<+7%l#q9@=o36*!O?Hzz>aWakiLsf5NS5vAvTg%+OAGwdFlx&RlH!zi^+qid%LS| zDDM==-I|s}DYU`s7hlP%w06bR;?8o_xETxYO79HO0p7vOh&$+bNw2EHC(o!hu|vlX z2-|iJ9B0SZcU!PS5Fe~#X$DWB`^%nJabJ`lEM{6eyIgLHga;<|v5K8#%uDYq-tC>i%HA^WC2N+mtu)aM$P|qaAA$5{yM+i{&klq0 zRCNwd-4&J!1KBi#KHmlkmT9@K^#gWVy24aG=J(s;OYafsU&O^?9D`y(qHm*Fj4 z+M^{4t3y1Oy?l*N35&DL_j>2htzN*CZBdahDHQo-Y;xC}vFV0Y3B zn0;-?vb~3gDS|AhVM&(BH%`ozIkx(b;7!=U<~cm3nmjAYCGo6a;tC~`N`-n9Tk1=? zrgW`-xo~v;NS!IXtD%R3`HQH1xO!j%5ArPfv-GTId$?`_qffrP0b%Si&7EqCmb|vO zu{wg{()t#!ilDCap$NPgjzRwlS;{M_B0sv1%h%u$yGzcplrKbtGQkDMOG-QaRe7cr zUG)|5MS8P8d0Uf?4yJ;jFnmpRs8?M;k1{jBzqSXQwJ6!u^I^t~o+-B?u!wSJ1!SpZ zvsa{Jk%yHy_)dShkEtLO9gopcxtIaof^Zs;amB>uqcW+G-y5#aU7$@%iSlMGx$tHX z`cZ0{4~@dP!0ex+)p`5P_Csb|ZlWzvHbY%c3*tkRh= zf(#1Oh9`BSo;hb6nU8PH;EJBWqtsbW3#uZ}G}KrZ8BM9af?}AFZUe=*u+;3!-Zz-X z*faNF^&3dqwbV~++_lSq^n0&zc=}Q7m`?toXsR@0!hXDhy=UmO_V7X4ueL@Rww88Q*sZKJ{CkVC@BW%tIkyDxzI#t zBb7OiCoiO)8-!HlB+-yQietKKeMKi%5e%*yRFaOe&%y)^v?eoC*2o2cyb{_dl#X{s zKHalV&g5~f*oL54v!)ZJc90QR9i3*~?ny=Y1)5faBs}A+n{*g78X5@It4*?~?fl}7 zQIeNPL6QHCtn_7isOp|z63FzlHE=Q=E`S*3>B{Si8=O47Uk=Pg3^YmArZhksxYTO9 zT0(E@LdBQf^=)=Ew|J#0cVMddx%+##2zg;1_a(P9s_HwJ@;(J`$qy=DF?uH^2$Woquq)V%V+@r>RC23?oW z;l3hA@G~1|^0umVVXn>ZTl+f$&3AcvCJLwz$FtUBG_cy=VV&et@)v+C>`%&JdB+e!VmGuGk+C{I{XTs}88`aXLV1hLJZ<4H? zcRDIsUP7u(rtPthi*6GXUkwL)7)dF*4?nec%>rVzx3{ymRjr_d&Q}k22P?96piRE; zXpA&r}}lK3!1ZQiZzO^ryxg=vt8VtDBqNJY6kUXW#0PgoZoiO^VM zt&y;>Raz#n@Q_h5&h18v<+wl|^&py_n_R}#Zo}TCs~E*Wzja{DAUX*dn$uLG+KACm zX-sU)Nl{xeT|XDmnyTi7OBqvK5M%nGjAo*nv_IZXe*ml4lRUfEEFObtC6U*(Cu=CjPFx~E!DpSgF?$%U#LDOql(KCIb0#; z(xr?!Zc)nl_Ul*yPL@u;i%HD+^VNKR=Q@^@TQXL6&%|+>o^;Fl-NYj#Y-LO)QTteM?*vqRG z^kmzR$e>vB;rXkQRiv@3Q3~-#gNI5grZ+G&fjK{ws&sRJL!Rub2|e_9ao`V~o%N;u z&g{;B&q_|8UmfhsU6>W|M2t}w&SWHsx^P*NktCCnz5Qrgvnv}CI!VjIUQx#8x}OW5 zJU5Wws*W!oiW-98EtCqGRbv?|(uzFA@FRHcTfTyz1q6E9C|V8IOILbKh09dX#SRK< zK`Hgnf{^P6RZ8b?Luzg2+YlSgJr&l5Sg$n+hi$t^%}V3Yu+~URIk9U6gBS#m3ht<0)YD%;E!rB$;feTx^kZ)iqRGx3aIww}QQK@&a^v=SJ}Tm*xQ z*a{^d7R;DPVG|kAnGwla6KPM^T!u8Y$8iAkD%&=RSX+hIg+GO zn=COVXm!i(QdUEhzihhcO`;U@qa-U0>cz^uOKK)Wa()t1SmDg`nC6Kto2?>hR{gTj zRAsbnX%kX6iGtHpPOHVN1N@Zo7>s0vOQ5R^Q%RxE`cWRq3xTwa@d}|^qzhb3IwCtM%TI+_Eisj;QV2J?=6SUU<8<7YPv^VD?q@T} zPP&LY#f#e6;3VB7XIDg{&_=+=v9%md8oQWP*Vb^ z9b}NeYoDj2VBbS7n3(P6elc{3*|0lQy4Crt}v@C z#Jn^`VP$_@nUfcRBU??AyH)Mj!frKjnNoT|F@n}hshJV?2#}1*?62(XI;@nyY67$+ zMNUqmH(Dz-5SN}Gngc<~4W$|4oLfOmJcET{%23z65J|OJvZgxRwq3eu4ZXO%+ANN6 zxMraYFKgO+$?7MJywb58$#N{6xpPrI!gDzDGFnrbgMUg$N)sxsN>a)FaK;+TAP?;{ zbSTg$O{U{XWmiF{nVn%%e*mgkcDo}ws(_6{lqSoWG)BrXOVXOLZ0I`CBtpANn37o~ zl@MIhhtY`R#u4S^azN>x%&TF=brO~g3MpH#3c>4T4r;EixfFV9p&M1X53X#;jkx=W zP~W^;R;{KM1V7cd!sACE*D68Aa^s5@qPT@kRSgAht@ezdc#shy$!7R$VH@gItCpN# zQ$=7ul1R3u8sAADs8WT1!bIE ziDZkMNDEpiYMUnIAXfMa5?jg$XqGU^V6AJEAU^4F zTb_Dh>ohgp$<~X8)L1%A4(CI4opMC&YsXWKs2J@S!EXDYX{;4b8cC*Z;|8MCvwbD1 z2e%O_5VWYOP`%r^rT{qmz~xK5$t&ns&0veDZiK{IzMMsiX?*ji+Ug?-3aTXK>cqtg z+ZtsHAU2Cu+c!3G29R5;WhY*5cknRYJYQbI?u(6$rN!lkaY?~0*8i^qzG5Ws+EyYa zEU@#CM?|qBRQ6#^4s<)8@sVu+!Y9#ORc6ae*iXBrGN??lsM@c%dvm3~w1-Piut{=p zQ_*D~!}#j9eBn6DI!0UAbkgtjs|Tj;gCvtz&QFc^E?~blUR9EfkB+n}7;{$^@CXvN z&EoA_MFRP~<=xZV)PR4-m5*-Nz*$Q^rzOP#Ln7hgkvvhCr(iOaMHvdL>KZ+Rl zEnMMy^12bYf+Z=ZUhnnChnp1-M)Woom+(y2IuFY%<8`m%i&(ww zjm<4)Ej|LCP$}qw&coZAaud7~M($l);+?q22WrrYj@(JT4ic(4Q~oToZQ?#4A&Xtn zn`jWgI)DZ{xEvMRE$|$d7B-|6=&S&gLek~K39%YUeg|M9?qCP|hCvb;@}+G!XNubH zG*UJZ98xS_!$L2I$txo18De4slI#>8>`-p1Yy@H(SP2)!7ouem&p2glujYPB+*-BL zUnO(0D}h3>3HE~1MNIaDiRl*{7Fgq$6`Kp!2dWq3&;;=^D_T2hqLjrJ9qoxZ0u__E ziRPM1nh5z>n0@#L@~X{-na+wswSZ~oc5p+1y+`m)D72#<$Yd!**1godSiV|PEMYB4 z#NJt7#+!mXONhArDE6qYbA+QwFihI*y2Y(oAV&QDb!{kLyZ)(Jf-kLzOSUFKYaxiIu zk9(D>;h)I{uBC_+WVrJTlZuaC!tBn<+Jt)bJ$|wU)RY z|8CM*<~A66DQ$g)vNa*3S-KL#nRr=ylJRTgDNQ^oIT7YdnAcWzESC{2fsmIXltARU ztHNK>GI{dma7_p$?J2v24tFOh&yw0U#vxscRbT5v?D zf~?3{7p%oN=Gi$Xn^79&;>^N{$&y7W(avjMe&Y@IoV@4kiIZ#c-*9*3%h$H{km=+q%Cr3NNgT!9 z#i_T!&Pk|qum!Z$+389Ay*PYy3kL!RTZ6sf$-S*d5U@_lLA~LAd6g(fOV^w@Ywe+DzsK}~9iXY`Y|GU-J?%>)6VhB$8@r(ctld^W` z%#GAG4=W2(!2p_W>c7O{+cY}r_DvVSSiK&L9qiRVhf7Z>gK@&6??{Oy+z+K1k$ysu zy2zoJ#>!2Gw`x;^U&c-$O=_*hAcPU=tkXRcJ&hS;i^IUn)|qFn%N~WHMJufEW(Hz= zK_tL5f|;i(`Pit{X-n+1YqYDN_DRIHe9^pC8ya7<6YyG$kwuc?r&!0Jx=H*xA1j~a zlR_p&UXJX53}cyrVuP+D){-cIgsG)PMkotbC3dDQifyQ@X$sXD78tkH7Nj6~z0Sy3 zT{Kld#KbKXE+S$3g;hKf8E+85?tzegrjdTONLMe7Iqq)cxVyTxJJ{N&uv{ojEjoWP z6|#mixBYc?Q{$(k;x+TNttAYN*{!u6ZC?RE4**rB43n7=C;>vsB}BSwHJD%tyO33g z)q$3~4kqp%&g}M<*R(Y0Lo=i~ApK0KxoB3_T(ky&uXWcIG6KfIAa7Bk_A*5v#{}|j zQB4g-ti8oeDOD|rlqE?D#l+>k-d=s)BExwF1*go6aRWJ#SpgZD6cKc?0qqiKG#Wrt zNSVC}6ju9#OVSX+&`UVvXoV9W2dd!>#L!>LRDa`gLUnnW89X;R`NrNk{EwaaJO(m( zX#!KVf{(KlGOU3m&tK}DCt`uL&;|tNWCK$u=~)?0IQ23Za0%^$?=_BNMjy~9Yw=hV@cG_SVEHoElSzg?HAJKa-at*G8W}G zZJjQy$uVXhEBA=YY{vK*A}jAO0o3*^V1I%C)V&dcjwP41On_ggt2J5#8Lhy<>J4#? zr$G!g0}-{NXh35eOQLaMRky87&s?3lI5juEC->fC$K^WiZo%f01n2#GveJVD5haVB z<5WQy>)KqDP1yP!^#u}GIW$PxFTth8%HkFYk{ipH849W|cB*E>GN$Z-(y&*{o|&;X z*EAQQfsK3?MoKd3nticf0~7M6wVx4)2m1x6S&Pz@P&bZ1b9q;daE-k$#ajmr|-Db=OD^^Fy$&0Z>4=s&&dca7h9*O||lgUVf<+8gei+E`yYwYI!`3ggY*J!emD z;MSfSCo$AKb^6RHEahzMt@IVqKAMPfp^wmVe2iAn>Av$ChTbdC;>kVywZ+eb)=%+pKvT}#s?zejd3kKHB1MXVzp zWd{_)#3QATsRJJubnw@AAH}!K`tsk6!4fV$vx$K9l>)?pv&}&R9;Wy4wS>!4v43`Fk<^HYZM=)N z*^%oq4QK;^X#s9~1w3y6j7WWq#C2n211igMT-1@p>6qN>Z*2A+?mvoaQ8$MDB1IN~ zaI1!_R~9MQ(v^kDWzpem{^t5T@@YYB`;_8{YS4Cu6%_2Zj68B- zUcn8_E@DUD?$QPlNK&Sg*o-q17kpq-N}G67DCezcMYdkOG$~_UF)870P|P;O9Vf;i z8iZ8IhcGqANIv%%Y9<*`mv?tIdbo;^+jz1f1xnXNie(oNSoL6c1G_#-8l$PZw-p;9 zjeP$)_FQrn7uz%&0y87`>g)X45+)-acBvB7sv=7%bA70W*bgTgIh|hG2?{`gr#08m zOwHj7Le+Bs2{jAQEEXoXsYA<`H0YLc{Ymi(?o6>77BA<;!8oTu?~LFC-IC2vjC$t5 z>+BOGf4PRGCS0ov&|$bJwR9?Us1Ii>a%j32@v>@9Y0(c)$Y{1hQ)iGDEvvkRcSyB7 zssqj@9YEdd+TW2cjEXv7B|Sp2Iz%5drL~rd+jmhc7FT{A30o`*Voeoo9JdZbo%+4A zgq~f$e>6(1vE;fWL~xd!lyV8H<=ZHo{(CqqF^jA%oUeiEGV&?3zXI`g_;9N6C~lZ3 z<>rFJsMVubut13a9dR` zF`ttSf3g#6Yf&aku%k%2-?Fm^9Uks5;~O!{?3|;wfZ=2B>VvaWmV|T0m`jz(JwR}a za&LFzy1l6v0JxR{U%aUSlt7JLDY>^nUc2TO`CzA$#9DkRC*IgbzD&f_&dOjD_k3

JC$H+Xb0R(=GqCMs>uG`D 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 z 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 aeQNm6-463!nLlpV@H<}`{$>Fvg3bRWgEo%< literal 0 HcmV?d00001 diff --git a/flexric/lib/flexric/libpdcp_sm.so b/flexric/lib/flexric/libpdcp_sm.so new file mode 100644 index 0000000000000000000000000000000000000000..3ccb77047255b846e1320062f0c7e61b24614307 GIT binary patch literal 178120 zcmeFa33!uL_6Pi?X=zfHmPM_~XhhVq7eoaGEkZOXU=^ykmeQtB>1G!wD2f)87^G+Iqn=OA}6XEN`8_jkwY^LHf(f=EH zxr%P!o5DE0Eagcu46`)lT?o2VrOSU(nA3cblGoKdWU6tSr7|zulX#Fi=T6HrfYwC1 zIF)d!_fu_xhc9^b)a&~U_`c(kSv!u~?HSVZQpekbO6>dMAMvGm^%yzS%Ah`?KjN$( zk51Xv+~v>;fzt>7;y-iIk*J?^_QWHjy$k;m;jinWzp{(|ye|6Ny6Atbi~hH|=)bXx zezKnj|KdN((UE9eTf69Q>7u{2i~gbL_uyar=Nxnli0?%E8T{*G^fKNVA;8l}aeQva zKk9$=cy1cEt9~Ote@S{mu6&IA08pTz9zfoONRbE(D2@oU|&dV>HJ5Qj6 z1*N4Gg+`gbtgv#SQC2X|pI=#2Seaj4mS0s;NOYzCawezJ1}~W7FRwwDW&r+Ge%&mq znC~aP)&3fzvbLt$C@q;?IJ~-I_^DJ=LeMtOoih3CiTR_3j~uP5vD&ENr;x>x@IMFt zb@E?oQ%|yL5zN8=a~gYrPjNt>yH?$euuC?QVQ=c|S5nfGqBvc(YmX{%6Za$4e3*IK z%#WLtbG55HZ1z{G{z}NCkM39Py14!?A7V6PNnHQgk4k@YR7%PbW{#nr7E6B*LE*ox zmj2$B{x(a$JuXB~N;GPyOyyE7>Ze>~eljfmW)3D;rlnt{Y9{em`e_X2C(F`LGR#l5 zrQft2!NyqnF){j+W9d)Ry@uhl^mn)P=UVzP;rcVv(%(z>8pbS3f4ZfAuBG4fDGT^e z^^gS)S>TWb4q4!k1rAx@Kd`{Q^kaVV)$eurf+?YYVEJCwRFf2K@zuZXdX1^0r~C=s z(G#}eZ~C!02I0>o$nGzr(P;5XZqo*4_Xn{yZCrM5jkRe5wfpz6Hf>;bKOAe*#%1>% zu{Ld3cHb0h(?(@?Rjf@Ll-;vqZQ7XZz9`nF4ax4aV{O`q>>d?s)5dN0fLNP0B)j|Q zcJzcd|85w?>Bmkoc)ZaQw#T*Kjcdo_zY+(3A+EhKuKj3S`+>Oj-Er;P;@UUIwXctB z&yQ=D>$b09{EcY%8d4TfF}9=TFu1}-+;)&FxT9_9$1Z_&jMr2N{0kZXl=G+<^ouXh z?hF0>EMKU@>2tj4d;gc3eqgXzF>poO#EvFC`OA{=Jwa>K4$1S?kG~9jK1R?8>)-3tJuN*?Ioycxd0;tpT^;tr!W3j-N+8!-$u z{)QMgq#V^i%{Ngi=2|#X2LZmIx5F3A$)r(wRl|b@bJ)d*p+=8zXkB4)7^yg%YUL0J zN4Ed0Il8PIGfa-F6~_~V1Hfe4LxE7A|GPwm4&%s|=YN&CtFsRTB_9_PN{M$4-d^j8_ z*G*W^Vlh1j4T|A3VR(#YxKS~@&dLEC`=fo(F6+oqTnk6mSQx@cojiYoVlZ16 z{A;;6qz@H?ODqgvqmF<5ibe|*!>fhi2F-9k*TRu*7KVv5I#n?!69#iMg9%EbH=u%P zS7=mh*J!%$#=Rhci!5I53)C+01yeD-A`^dzY`U#TQ&gl6(z4=P>EJ-0gFhHZ-_WUO-f`A>A>VycEr{g@}f*kl`rse zCe3N!`3x>ryAt`*%LEj6a;Y(2PTDo3e0K}+Aa|O?qYp@w?t4IbPRTpeM+vK%4~to8|ycInKO7Mm;BK$`f1 z_p=ytLK>JZct6wWX=xC``)4v!p9hnbDIu`7^CSu%U4S88tH8TE}Urj zI_cfOz2r(OH*wYOO-KGf!(JvvusRgBX3%Caq}FbNi*G;z+9dP;PoD5lwwiZquz$#iueK!U*JL)ngHi@_yQ9$(9T4A zb1bf4hFq8d3e+}Hq+?u7GOlzrt|c9!V>KP6!^fv(Vlo3Bi|*_87DgIJA)Xa?tjn-6BcAOfbBX z%z_#s%@*f3?v?yu+kM2g8+B~UP~*d577;P&ihQGxkJ02ot|4EL7%ZL=_lSMW z<~RTNmEGW{>_nl)M&bJ95_7o2l~Ntx0Og-mcrOWFqsH5-q_+UCYvu1y$|bKl@F%jJSkS!bOA!gRP$RDKD3wpu++I`M z3W-FfwvrQQaUg@+4Trr%r`SMlwrVEPF+PxsoB;t1hg~Mk9Mx=>X0B?U#v7$vBos!z zUYavibGtN=Rv0>zmLM&*i1rl$N_`A`ls1t8+O~1?fevnFs^)uiVv(&bDOEs+(m*cq zT*@7PCIg2fv-hH_zT?>W6aj~^`-K`)VJ+rR(?3g?DYhn!gTf{u)fgI>|YgtB5#VCSo8fLYw7jw;{FyAmR^tlmMzN`{M#`N}y#*fc8J($Us~@*w=0!**NfWGtc$&OtCiJ$!tBqlM-q?UuNrEof_7vDEoOgP43GydV^DOsiK%7 z6o+YwprUx2D1ytxY|A=8DD$|?11qEt;^PmB9JFw3T$YRSP5$c$FWW(4m@!vrdiGvP z7FcGm3Hf9P<}PkAN3;aShb|>*7T}OkiGq%%OJa7O1zvnk(sMQmJ$z)+Jz41C^HPx2 zhl9e#GR1*#^n?0#$I_-64+N8ku*BX*dheVJ{PRFuPWEnD-c7H7e}*HU$T%`&9GO(8 zBS?Tvf|kJK)E8z0u`R;Oe8A?ah{O@g!}RxOF*oJ^_T!v+ppHAwd1N!Sn#>MI%*Mdv zXoxkaJ1pHGwt&L7NRy3)W*s%LI9re9ExULW94K9em|A%+wuKp8f}1fI%z;wj$mr?T zIJ6?vH)RvT-)smI@+Qo{KD(W~CBP?jod3xax(-qvbA3}K0*0#pgCJFuGZIto>@w6E z*s-zs7?YUt?Jg;;mk&pBiVro`a%7}Dd@T)`GVqUF3rA*tjd`elnMN;Ry^EKJP~&`| zsMHkO6~&1}q2q|%DC}JVY|OF56W>Y@M?RBDUjAnR}hX`7y%9Laj zaHf=hE;4m}N=bg|Z!B3j^PPqglBtq^-`ON&X-vorgd@dE(7Uw0V=zleRZ@_PNXkkr z94^0KwsufT?2C z0OyJ+!AEjHI3@Trr3;AkFt>2zl~fvyTz5d$Gt$gZ&~<1|3A9ZKe1ip*qPqv*z~a*F z`>(*}#@8vrCR{P$$}4TpcFf?dZyHSlSaD(3!OJ6Dg^hS@l|^)81{Sws+t4Z=o5$t~ zH9jbIe5H;ILO$MiA=Sc>8q&tR0SS9aE4o?i;4+xv|G+R_p;xU4cP`=5$^FK~5lWoA zgWAVa6Kdqkp@x(zw1l6Oggzo60?H0ZLiwg_PR9znc67Fz$Kgj5x2H&rhLm2K+cw4R z^)E@^p6|R5xZJjYA>Y>h#1OfFv6;kUKkQX05n&PWL$YUk*uqtEncpsfN zOVx1bLTTs9o`XRPHQlw&|RXC^QNrQxto=(0}l@;|We@=IkOZ|?Gk z2^){%+FcG8nX0*sp)1$Kc)u+@>ta2!?OdrfZaC}>!P%s6;(Z3={mvKo6YoR=xZ>*+ z9N3{EDDG@iMx{G;gu0BMJ#sFA=!Pr6H;-`iJj>aDCdU_uNomLVfC7Xaq2Y78Bh>E| z_X^ZP2x90AZ(D8ZW}5yz#fbKZQIm?I%|Ivmj6D}J7GJ%A0MGNc)+oqnHi*w9O}e&X zo$rF+fcHTC;sanQyi!<>%{*~g=BzsMn^5CfWJ=t(X&r$QqjaQ2Fm$CVg zh<7XLg)ALc+-mssir^|CSfL3r6u~GWP%Flse6cx{cC@6wV5Gm&!V^HFa5PiL3r%CU zE>7JX#b?jr{{;DZ<>tA*dT)orPJ#168Z6Y}8|&?;T4vVZx6J;SPRNQ?3j4Lqav6LD z#6Dw0TdXM-aD^tUwVdjZ~AcW-fL-R)?L|$9x9?)r0w$}*TRuL zJ7}56@J2uqVUkvcgc`B+Cz6qxWQ`*E{Lj(o_aMPLGsC@YOtW6m^bne#Ys`6@uV|hG z4Qqx!J#Ry2`iu}M-p;bAb78D=zrwu28zD%(3AwBZBlLDKl+<0+M_0{svzSk282zF& zvsQDwszr2Pq$V8$NI4vgi_=GVJLVsa=F~RoOG4lG>6eDMKmDGjJwr$a)ST(v4?Z94 zOTVmz2H#Pfp4I$)`c;3T1}rfm1mvzJ4f(4f7v4cZpOcAnSG!Oexw1ykB}n=+TYU}v z4C#m0^PR}f&nVdF$|A`GD92xb z34H9COkH=f-9BIX;HEuC(#_KQ-_*A`eD1Ag_f{CjB%s$jnBXlUXzoP7-G2hXTl>-C9lhZs(~9HI5U)XEfmoMYv28^6eNgAjcr-sDl@jkaYW` z#)JjJkprJVV#8EMZ1fO&L0by6l_2Y|9^li_Y4g+^I6*T#_ltLg`o+!aeotxc70rLR z#vJl49z!_tHYpJHilxn00OT28+h(3zvaM*?Y}N2fBBbY@K?FuHuSwbcCF+L1@e|Pu zLC_EU#LoJCq&Zp}ztUzrs5=ci6P>nUdwAzfdzN~0{h7fN5o*M>1?9e;nsbzEIJ0eM zb>Sjf8;_z9#Z}bCH z(9q6!MvST*d#sLHAOHwez~WyY$M4Cq_4sUS>=HIos4-v0I7sVzw9+>UPQn=8H`d$A zhwt+gRe@0Lt1w6DR8()7RM6$0@*)vbV?Rcz5}bL+3i1Ux`dtu&#W#ckL*{$x7LyT0 zkEBi5EZ1d8T+^{T9fQ%e!f6o^dCf4dI#M>WI-$lg(er$*=Xf4xIC31ZuV36EjvZ9! z<$~T{qfb!i-+#pDDh{qt=s+X?_!eZcKRI5Zzeecfv}br*IOYc{3c!itB~4){id90< z0*dDJrTYj?ZAIUsyf?(#9PsX}U%b;;NGascOr>vyN;SLJS@Uq@Y_JP@ck)Y5`~=&~ z7>}Ep4e_=h7Rhnez`t63!OJolA=U8xxG&({Tfca(aRW)ZMoGd!krYqTlFF5&)(?58 zV36wydiR3cAc|Ma3AH1>LO^6s-Hiqk`0N#y0{7TxQlegolhhsuf;IlK9@A^tmWscH z8t)cuYB0TG_+r*8963*Tc2Lf>L^&~$eUts%$=n0AcywsKkNXf0E7bUqNa&*_Br6G# z57-mh#c`?>`on_$RjH{^fkJ1qiAD;m6iks%Qj1|FKSel>2 zjD!}ASN0AL%ex42cw5{NsNEmT+b>!mn8t49W%Co(Fx0qFRL|0)o>iiLdEZps6(dh5 zrHd4^Oi6f7B)mD#G|pz06^^VW349_XSG!oDKQHL_X!LBQP??~s5I;$wzaZ#yHF~;2 zKU4H?Merc0P;Nn{sm$;Nnw6vf6S-HMi-T%A(}fyo;|+C=&VDfI2C+?`NBa;R z$+REctC*3ugV~?(@{oAyEsEJXVphLc`O(!1{Z&D~Poozq^lJoN9gxmY=uLuNs?jGX z^s_}(S|1|mcO}}9+PwbXOcrXyd5yH~ule<2BjA1N_pI7(Q|Rh;>e~`?aPt)UYlPmA zvR>i6C2Zf&c#kQ(`+!I9#RnE22-J22aiotpj`wz0f>cFrP`1dyoc@Eh23jm|`ZkCe z-N+-a<4}ckhIqIr!&m=}!#B1ie)#09bkrFL_U^?kzoc@$=VeHoM{)1L{cJhk(m2-W~iN41|PKAae=_dE^cb-~@Nrv&}@K z!xQg9TEjX0p}!gcvgZ2{JQyHeiBCn?TWspQA>J(M|81U-P~&uBi1*F4gAeBj3P&16 zflW~RRS%FNyQzboV(U%yEs95;@K~;S9N=0wGGFuH14uK-Td~P*BYt6T1bteJuG;thebLz|i(z|Z-2_K@2>KGHeJ=R(74!n7*I(bpQ0yDpv}8hxZD`#o-2Rmf z?a?tR+lE%=@Fa+_Z2GXMb+O!Br4&Z|(PW&Z6@G|o;m9yjSZ$Tf4ecOBgpebmG)=Tt z5q11Y5y72q#)}%b&cT;|u#}+vD80CWy2K$?7Zq%S(2x5`k(D7uC9T&$aUsiD0;^3q z^cPADSgFmf-5opcbvZ`q-G|P>{x~+?*4Wspd&a>`TqU3Y%l3 zsYs_x858;tFoSt8gqe9IpTT-suj>^v=%p;1LE{KPAxE>;ku{G?{`Yo7qhq~$dHQo% zHT|6PNUifNY&5+8&SK*B;U3#A2j5duTvo}*Kg3Hr;zY9*mrywN+=+Y_>3DHFbi+UP z*>+i=&{Q$=!;i{m(M2l{`)vGifYgXfQ|nk@sPO^OWU|)eNv?$>r$G}=Q|u|%D0G@h zxb{&R{WUg3IN~C7j#PHsHto1@XCxlIcSL;Cex=F5jszf`lDF2^?nv!$H>vU6gFP#Arz^rhhN`xy?MjC)oRa}k%DGg}wA4Lj zJ)Dz;tjs93Ro%W&a*m~=5MODIp`Eh6h!L@}dxHC#P52~vv)S~J5;h6sfR^+5eJZ*x zC2bl~hO&du+Ok@?$`h}dgJsV--)zE*&O+V3&XaA$__m|f#YzY~g@lB)$DFQ&c!{e% zTAifO0VMP_8a-2?A8smvyffC@UW45pMH9YgLo-5Vm%Z<7K3gydhOmLoB@kU7)*LOw zywwB~%n6>6vBjgbNVZeFY{6dqU?UAorMt)u*#Tff znVV%FU!0qKq+(vZD3qd=tm9fZQXe9%V0n7|j!S+9Y|cb0D@|bbq7-1;<&o#atrK-h z=FK#=hLlyaOgknkndu}`Uq;9}5^B6f&}VD(pSTu|v^BAcZBP;3Kv6Y`R4Ir{7wEfiR$ zh|5?!o2SGF9@N7)nR^sh-9saTCUg(Y+5np|Gk(?FM{;oKWqClpY3|ir!}qCQwVL}I z_D=l@(7vlo^q@k&hR|im7PGLqH!9k7V(vd`+G`cVRM0tuOb!lTXG$NVq^kt8efuzS{+98;eRo~&L z9ghAsOXSC@k=Wvbi1N1VrXNEiZ1DBap`!LAZt>GG90zCvD=SQ?e4pH^oJB;8FK1Gd z=uw3B?uTNE(uB2xDfWx3=nxVX)2=;MjWNWSq0OGFST7lU*IJ$;x#WMQi zzO)8eokx$&pVG=6B{}A_@uP~sRQisy+5|agk-vu;|4svMNZEfmHEG?P!!HMhBUM{8 zb254}Z{^=%|JGlVctumKS5)JKs+ks@l~gBN>V@$(MgF}Ay+M<&R^&gwL_+acg}+}# zYZu=EoyuJCj4n8pW(G6I-Al27hX6#99AWUf1Bd5HO3aTUCR2-I?N36xPSa*6+GAq0o;ccSMf-1|Ez-2@ ztUcZjYUKIokjZCxXDONkLX)j&K2h z4qoMG&lY{3LY1;Q1s_F5+a9`$pq9tVk&^pHA=N9km8l1 zJKruJKEVFAi`^>JI8x}EG~J_$?o10^%w91?>9k3ylI=9SkLyeb+{7tw|mNleC$sZn*Z=Ih972QmstJ8Eh zE4qI^ul4rC>=jd#Q-JA53-&WICP!^FV@6kcj!^RQMV?E`+r}2dEo)33#hd9zKXPD( z8fOVz%M5dTFDSZcLdT-jxgnt+@dU&M0(@J>7#QbAFueE_vT98_T5W9e^rdG@wljAl z_Qf8F#&UL5SsbTJUJ}Qtp;X9FQuI(xQA4SJj;#ofS36EQTu|zU_MFpQyrHL<$RLNJ zlPhoNRS)CHhThua0SC0r0q5E_@}|ZXZSnhBya1VRr0}W(2FxT=0qNWWD|*{)HzTx5 zg;xM#d+$W@MyN3$V|U}xiMZ)?T*G&d{(!OTbJ%h0grUY;1^u&&O!UhX`bBi|o9#wLZ|-BeL-7h6j)eL43r!nL zW-<8wCo$JAvvgCs(C^tmwZ}BN!yLcp(#o49Y~bnP`}-V*c<(`osx(oXvUB^hJb)HB zKi&^X@S=GO+=@Ix*lw*IiaPpuXF?uBaJb)8l(b>sJ>%O4u-m6!+=;A~Md zA0+yBBwZ^0dNXnYZR}&&hm>Yt)(UlS?9d5@;tC>ohm*1A48db%fuof@2Ex!Z<#b?Y zL*YbhPzteZh-^CUk~BeNP#@@WlqNE?O)_swEM?KlH?@5MgHC!lFX0BabBd7wt2bX< z8^SXjYJ5Xx@u>?;PdPT1YT?M>P3$Rn2cIVW0M9KR`wRMFjs6->e>n2*XL!iSe{}zX zlQ3@>c*H556y{LJBnZ|=)b zuF|5mDQoW7NanGvf1_k5;N#2U+14-U!2Ys{=WdMd`EN|)#zuFm68TpVxo4_5x`&m> z5*EoTl5KQ}TtmCX48?V)a9yprx_C(8NMFrWy{RXg!Y}#zE#%>G*S;~+X&D(x#$J&z zQp;G+HGKE%X$)8A^)Gm~LXF=FdXh$eiybB$SwiSCF%$-BV#LSRE8=z`eq)MhrTY}| zL?H&D?N;YFFpBlb7=NLSuR3Au;j;z2ErZ-)4}Kex!j-cR`6loKN}yr1rO z4|+S^Q>mRo8&iC5Osjn(*YHicr+Cb;xBdHa5Ue&6`dau4_6NM4FTA#%Z<^o~#VaSq zOULF@il~PWHE5#WDL?2gL>TO^DzHTH>MOh^XkO28EgX6ENj(^?!1;=(pAhxXL<5up zw+azi+xq@qr}yLrC`LyMqu0(ehkvS)K0!;*$+Sg%wzg$}_DECl?MH9xlmJq;w(Zku zo(9N%V!KXZr?FJLr~3qkrteX%SLnwJ`ePcspF)3L(AB$J7bx_Bf?lW5zf;b8v!JUS z?NR6_2>N7={;op5M9|eu(s#-*Ckpye8eP_ca3qV+$**}OrIcRVPPw*xm+Eh*%LXh1 zd99UwJ@97aBEakkMm$nmEU2Uo7pd!cHtGHerM)KyAqm2kBml@2mZXDf#Dg<)UKu!d`R z|6hiog1E0jUm)l^C!4dnQlWppUX3c4ldGrcFRWasu}-KqYO08$+G0}4e2}-%e2k$x z5_78f`4V5j9Vgx9G*O&(IZd;b=<7xF$y#(jC3=D>I+!!l{KnESMYKeSe({;3%u_@? zh=_jou#34}`#WU?%-&3rGCISbzEV z5k;_G2u{=lA1X~oGl6-0V`m$2aD}wtOBC-Xh4-J%F-*tLEFqgAO&mD9kf8^M#7}iNai)?*&EBCb!9_@U^KP3Eb(flJ^3rAK9fAE$R+1S-+GxXk7@4Ut; z=FP%opwMq2 zbo{Q3!aGgyR%yKbTnk6?6KSUHe2pTXDC8Gu^2ZgqN5o84c$igM?+0tV28Gvgzs&$~ zNpP?thZ%`{4?73h?`%c>BFNE9Qg~Qr3GX$HccQ{uWfzmc&YvrfRC~N7n#Ipt!|z{` zD7{nOq|k9AMiMX7=$|U|V?{Grk3x;OG$p)28gGTd`*E$!0HT?;bBPjDDCFPhIGLcx zH-p@Mc8W9Mxr#%HaM++ZM7f6Nf8Y@I9st;&O;!-UkVLPZCWtcIcY31iPa*myBn>QA z^ZiQkJqCPZ_G(e+Rf2w;M&F{)|8XDCv3v!w6Q|$jjQv_ig5TcA2y`s>9#}#@&@mc+ z9W}%8SANfPffBS#1Z{?gN(Q(}3A!1AI{%u7=9tF6=p)ghUt_M)T*fIbIf=P&2Gz6I zUFo7W0z)+yLvi`(w`$NZeO^cKqGa`g`UmW``iqGB&Nc_}DYFPiLMCAj9+(qlhN4`f zDDRQszo;q0it-jsi5qmH%vO|FDatiMd4r~WUQtfflz7*RD90$ufr|28q4a9X-zmzz zpzLh&I7j`3Jz9S?GCIK=^9_p2JNJ^K?uQWk6g{~rT^rL^2Lmm>P+Kxyf<}>q-`G%_ zvySu^a&quv8`w>Teil2UXM*%NevuwJN1TCry?4Ju+#g?Hs(PriwsC=3skD7Y#(bgH z_I)0GIC3nsC4VLXsZssCt}%*klh7Th=^jyZyVjU4fd`rt3)t?^wRJeM7!Z4?XT#lE z`65H#Ms%=e>2Ddnm}6QogC*hlm+)^PjujZV`F%4*r?xdWYPu|?*9D;CpST!AKSZ$| zr>oeDVO*!g(S{pUsJ#G1py?H`MC{@D5s8$SPa@`FLGlG2C5Rn*Yk8k2JwLyP?a%_Y z_y&k=hS9;Y)erZ`Sw716^zAM>q|)76?B~eor^xZB!E`k@HE0Hue1qy+zQcY4q(1y}t|rGZc;_!vmG6{fh2=p*vjDIn>a1-Yr8Xx@`wE z-7rP>fzbW+OjEtDa>UM02^~zp4`5sPP8knRdGMT9VNK*!dN$L>(;8~rF3eVGW}6hV zd@xhr``XQq3+P!92i)Gc>EM847dQwUUW}|xnsks3>C3KiKxyjBP3F!Vg}(4g>4RUVEQut8aVUi`}JT>Kk99#VRCP#aLAycg$^={O?^y>-09Z0>LESYbX^kz}-O&S+DmT-q+7AjH2XtSjUS+|znAVd$c zDdCJp&lKqyBR$BkwWmwZIO!RO9um}^{z7gJby12NvlU;Xl0JUvgp%ACndP5y$TLEX z-;qKMDNo`SQ5M5L@qC9P3-6-Tx0h01E9M2i>2p(GB!#ZhA0(1f?z=NS6UF87JCt00 znX@6~WG(kru7x8G$UXR`!}ds`muw`|Zx@Y0t3&_H*I*NdF&cYhBFP~V>>+F~5X2?) zD2HLFaR7N$L&_4|&x)x&;TraTzc%Q!!=kIDqx@3U9FB9j@^fD!iR{ zB-j$#HeYur7V71!4{`S_%H}E-4}pc)g@V0Mp>rJJd(#?ys6wwIbo>sN!W%7`U8nIr z=Nf+hAdzNzyVGBhPZ09)n*1(B-W}xj6)etgeqy%_HF6qgNclIe;ALDFDGqO~`Zd2X zpXJ!>wN>$*E}H#L^Ytjc*MM)#UUw?=8G>G;(GPG9zkfmK6t&vX%%6tp@^`hgl`dAy z3x)X+nt8cm{Eh4;nn2{h9t ztXAYV3He7j#EP2?QREMUoaTq#96-LBh~`24?rg7F54d zAa(41W|3t5<9C-3rTLIo{kGGGoCR^Z5OMvqxQo=pe0dw`ub%w#&)2l8M;4hfvUY;n z_A?&7(WIL`+8JAZ8}VXHPp98Tq=fU=XqM)(J5fz9~d_vcp;orR3!$BmPGZ z;&N$UHNGcge4|e_=Vy(YpClfiEGE%*HCO7_oR`zpAA$)oQj&c#?a zW8Z80=Dhq@z0`kH=UGl{p~gRm2EWI3h@S z%T2gTP2I6jA1NfDbNE*(Q=UtV;v)fV8ZY!~*N%S`A*vUo%w7KzYFY*g_aU15LyG(E zAdQ`_6K%gDIzY5XcS5hn?+7Wz!-Vk%97l9fRHhiO1Y=98gJ)Uq|1VPHa1I*votoUI z$S)+l{*zJ9CB(QJgY4L8s5wx%8L_OM6uzpM zP7;;dbP9c2<&X!6X}rpKa+csgRo~PDIwK!Ytm9Wsh%#;rxk?(c;X7U<8kW z9Gph;QSKz}@>m`=qHm1yd`&!~3A1H}zzk(3swBO83LXEcz_e(YR z_qZ009FbW2T<-4D_6;dFD8_4qv7s5?rx?F-a{`aU`XBdeGU~>RLWOQ<@Dn}zDOB)N zjs47DV=n@yF7^`m{EV7 zSN)VH-)|w+v?jxpSmMt`VY%e0AapZ{s(s$uJfKkH1d*{#&*2?P#!pM>IP(#}#-39s zGJ|;^B}$B~Dlzhp6CFm9pa&A;M-C`4*85S_Ynb0w-tEGr0Hbuc8GTKX4&~Ps$0>C# z5Ov0Db%rQ)=0P23(M-+)2FMffcWAbs_#DdU-UL4hhrL-ljJk*U>W3fO@C-|KVJ9jd z{;B>y^5K~8yr?){AwyWCIj&J0H-ICnZugxp(4D|{u12@bcO>(5j-r?bmnaR2M1zi# z%`u&-G&nJ_2Kk9Kh}`r)&_Let`hf+78cRik`C5Yy*&5-J7g)6?&PVpR3WI zQs}jUuA=iEg|6O}$<*k#DfDxIu9FJALShTjIzST`+JSDHq2qfNZOpwrza@Y%>tdd^K}0{R_LhSDqFbV6b98vj%h(mH9=JGPI8%q zgH36sI?X+Vr}Q=f2_7}-j6f+sO!PY2B-Du4=xFJP`RiZ?gd@k_U=6_43SDiT=WF*) zRp|S#=LsZ>Vh2iFTCyx}XWNyg593m{i};Dzm+g2TDW#oftdf1XsQS}LGqmnep;bq+ z_5DqnLRabaZH->7&?k$k@sm)kes2CaGH~1kng2Nk)3)@>+sFI_$8-7WUw6GmN+0?h zvcMq=9J0V63mme*AqyO`z#$79vcMq=9J0Xwu?0rVttj)4C@+{-P*GYid)S^=e^qfoq5qUQX4j|@-cb{BM--R(>#9l$M^sngUm5Hpu%(9c+a%FDO0#H?95Vsd453=b!J<7{5A1CbBn6tyUMEP#CH`|mMC8Sx=I3C zBo)?FmF8E}GG|LSDNt3csI--ftNi|aXkT4ByPCDv5UH5(lJcTNV1wb%A-@X*lb)5T z@?R6r#0H1bUBUgM@~izdwUu$|iG1jfxd1}d{E|X{er+Y}?-$IKM((7Ex%ty)Oqek} z|Ke==PkQplJ8JsWVFXfbv&bH9|KG^rBS()Oo^67en`gQR@=UEL^3#nRKK~^@sw^t3 z#B_{MB@GMiB;(W5JTkwm+89NDPN6>|v*oW@aGA{><2ms}&w$D0^9xGhE+yrawKW6m zzDd)f`C5Gjb@5s1JQF_49i3c@=R`Ar9g3=JhLiHci)+gf4*ufdvule@1XgFNzpSEa zp~qi0x1hGV#$PnR$Ubg-HiM>B)ObqDDog!kegqyM%JbQatBT0ShMifnu+r}tKi*@X z*Xpug5yG=%7}XfCzdURD)Oy!m5(Wni4cD6UA8Qf|ezP1qq={UszFIQ&mx#Ptg!hPW(+cES?ZwrWdxTxp;S-1xb%z$#2IUcbtq5@lH#*IW5|i3+50P&OZtA zlL@uuCo>DgfI{CX`fLa+CHCjw`KL1_$E!uMEgsoLh4+7By{)Y&DIGC;VU0h(psK21 zA%#H=Z@zfF5jnJ;{rUOp&U0X5Of z?J_|S=znLQ!Q(C{os(ZwJp#oDL?yhlfbu{|b$LN~R>2?&tKx#vYQO54-Koc4S#9Yv zOdc|-*k3#(x*^swYZ-VDFCUjG|7-K9#)Y9(Q?|icTToK&ui_S_8Dr!Rs>9B-Zqs>| zs{93|r4@x3dPylVPfdk~+t^Kd%6Qu{z+l3{f=W#G)XCHGr%%2N-|jJtnzG8_o!E7r zbf%`=hG*P(57B@VvDA6DlU@cn7G{G1{?eL!qK2auRMq5{#P;}p?9V$l&m8YDF%6`| zd~){c{4yjYs6T8Z_xVf9^8HoV^sxI{cvMzYBbRB7jpCB3>X^XN0!xEXnIW*vd{$DE zKT;11jZwPMRi>j9R#vcZwm%;Sih^=_+OFEwb8!@#M{Sx^2*D<+q{iB&ykTuu)>h9& zBo#7|g^ovRX|pvg?Q(yejW6s)MV8>r?EilJ zCiHU{YYrSe=2gYDrKMR_gUCzskGca-NLF9q}!1M(RJwrTY z1=aJi(1!aCMS&PA9W>ZN7+a8WLh{o>UF|8ZswndirhSi3Rxym}l@(;<~U{|kAwpt>429kI+R z?W)=NHHbmDUAcBB>d=jH9F}!so_}FJwn}uctjo7Sq&e)&s**W#Iol8uq-xqNDqK34 z7I%WlfCXEz$MmWior9-S+AS-oE-R=hoGZH}!yq`;cvG=1*6KxCN2@l{JM7HLf-3By z$(a&CS=TOLGRQ7~g%zcU1OO*YpMJr_{ENJkj0LO*!p;sld&-1~=gas_43gRjk65UW ziN_iwM669XX4uQLl~Gkl+GQIt>%=xru|A7U*t#>S79t)vw(DxDuwVBS&@6fA>`+`$ zOYvTglY^g)IlvHdUNRBn*+@uQJ`Kh6op>qJDsT{~!)b)PuDYbW5d6TC(g_y%!nt(2 zKp$ZjPfch#7gfT+{YBWjGd|WGHVn7ad6Ms?teKn7c?0)T1yzM;sHKWg zY{6qD36mJ59Udd4`OUAYs4*|Pcpj}Ciie%Pq#So-yk2oy_Cv8cxl_YTlvh`l7L=>? zt(ea635I0n2Mf~-S`3m9e$eq?+j0;%_HPzwygkg-I))M_U#;FHuuEZ@#9DJ#In5ldR^=9kh~^`eoSP^CVK>xE^Nu}v5nu}v7TVh}W9 z5FQLhuEAX-MX;0jCj#JW4qf1rL`wR|8Bkr4_;69ys9uE4A^<2yQBKBfZw!%r+r(s2 z>7i)gTRKZdpq)ARh|OqvvQCUq3=+-M5@6<;qUr%i1H+ByI=+zcq? z0;=B(3<^^Pks=Px10jg&@8nRbAA6_rn*2`OG#v#(BCyGqb~#LKqK=HFa{Zk zL9nvin$!JBY}8$3hQvI|vb*A5Hgb?*eZWzM9e#Dm>{7qF1cS{I-P_0!-umPLi(K4< z5FJ0sljm#tuCwz|UM7Zf5Z_n%=VKFyJ#fjKIexynVFcXg90Y36y~#o0G5&J1Nmqu- zC2af?Lu35Ag2(vNf{%MyD|6IM{Y|K;ui3Ut9eKai9mwsi?b+D=SS%xgEc7wYvglyf z4<2WMQPrv+L9O~x+k_Y5B1az4LMh89#-DF_I~hRJ(?iux(3pJ_Q>**`kN;z>`t)CDC!~D zC)u|$6~*!ZrnF!oCUJmax}z3sdY`=mxVl5;MUoMWV>61FiZC=hmQZ3qD_o7-28@Se zj9tR50c%0h^34%iU^e~C0Us2O@plC`9XbyFU*4Y-`ISGSB9h>bUAvRIp`%?Q9#^w1 zOgq=eddrU?u#XmuTD-t=@~HfpLAbJ0UFbP=GY)9jNj%$eeZeE z=u;>UpnM(W>M7Ca4wTnUjYbcke0Ewi+P|A&L{W}I`PBu{=oKhybEDDgP^MlOjjly` zFUl<_Kc60rwxP_vI2uh(HH_0PiAD#aTre{lorE&;vS@TJ%2!b?MS0le(dc@V*P(1e zdE6DCN4Wr{fro1Ep!A>|eI?|hybt9}l!NmjALZe*qS19IPe$2QSLxF3FReoARlEN%H=5UMY#dxQ*)!yW|VzPK##HnWpbKfEJ8UDWdP+Qlxt8H zp?n|Dp*-SRd{+wPvnY#DzKL=v%CAwbM|t>lkdHD4Uy|C1atOW#mfXuQT2T%}`5wMG zHwoqW^^lJ;h;k{)4^Xa0`CELS_H~qBpxlWv8(;Kwr5nb%_~LUGO8TDm6qJrzqR~>6 zg(z2`Y(n`I$|vxR_-!a}4Md}RQT~LoM+U}$-=r9dvK7CgF$LxAw}BqzJ@^%)6)3O6 zPrW^ba@0MbM|nNUy(phw1Nz?ZNBn%_P?X261wG22P?n;+?0(Rryzv3hqkIeHHk9W* z2zr#;Q1<8p|HSwBhN8S2zl=Nu<%=jwQI1^?dX#OCfF9+R$3Tzrf(@WYxgKSYzJ_ti zL33`;vQ0_&!7p1Eoo+mv8`=k68We&<2Pb2Q1&|ve!VFg9gQ;Q*=TeI%DWz&KH6p`7;;{0QY1ltn0S+X6pA+5Khs5z2)qUq|`oE1*ZY^i|Mj0v}}- z%8DlVG0L4G_#w*vTS1TVGnDI5u6YggC@+2;eu(lml*vZ|j&dN%!{2}%QLaHbi`wC6 zbP39fP_9M!5y~wnFK$L2L^=K~&vIJ!rwYb9RV9Ejfj_*Xe88f_(eUjYi?l`a}Kn4aaQsx9MKtS%dbo=R~7lQ@^)Y#?46+dv;6u3nUBtE_5$A zHyS;aULG~!&S4HhAf2AWzXo44It`_UCwVpfN9cYD@SQgJpEZ0x`fr#Vjox8{2Q<7N zW?{Y4$PT=Om$F>hG&}I!{Sr8H7^@oFAt|qZh{G5)yNX(n) z*8pD81)OAW2K*7g<7E@+|At^=&$vS{=;E038m8EXN* z?I7?ifZyH)oOIX$_)5U9MX7Z-zrnd8**S-_AUhk$hOrBKm9BK~03JCAJO}XIfcLf8 zGmY&@b1@6>9anXpCz5>~U?=5w?E`B8zY=gPb20sWG5c%*d>Y_bhGXz^V(=Y+k3R_f z0N_Ip!oNTEmA?V}G~3vQ^VmrLae$A;ey-RmKj}(VfZ$gEz6kK4R(N4dzv}?M8Sn-x zJZY(>Ukms?z~8mO-_Y>SAF1^}0C-U$)(b2B^q7AA zu|J*#_`NoINgh0Yz&|bOI>xU6{3?Gmnu3_q{^vZP6ff7kl21^lSF zYW*;CQ_?3Iz6J2l0RM}P{v(Ea(6#gvlMVfvV;2!~=Z-ZZ~;Y$GTTiUe`tpog0zz^8y_i6ej zz)!)R{r5Kb;|!0Ol=MW1FDX;anzVcz>+NrvvV>(q0mS(>aIW z*;e?R82k#r&jWn0ZT^qr1&-v?xo8H?6??4oNnbJpg0BVq*lVKEr)}`{8omYarvOjn zKOWHR0KC2m=P#>#n*VZHW63@S&QO1@!5T_+)5lOH`TYTl0(Pzy2Kt52fZ*c*KN07h zW-C1DRjuC@fWHfPB6<&Kt^@oBz%Q`U(>gPOS+EZ4fa94TjUHk3VP`LPVB*mP_z?>d zuS3L^^rUlQ25|1D4s(o2cWFJ7aSm(%e6|gKsfG^({5`<0w83SuBmR>Br*qynHux8s zz6kK`fVbJ;u{d4|_}w@MR@&g=NW_0V;9mny|E*=Mzq6QaK=9WAKLzK*uHZWXUwU0M z`bQi6MopiLbL`RA;~ZmMC!JC4rvm|Z-WZMk4eLcr{*GiCAdesL=4H|73jk|)($lf= z1AY~qD{b(J8om_pTLJHCommg~L$`DtN3R3^WFYZAko4OL_}jNeqkp!^|3J&9^ZU&~ zoD*zt33Q^TbN!oupJcP&G1`7~-hXEW&O2868<;)YrwH&L0Kd@+PpZ@MmjZrqLo|Am z4Ng%e{<|LV&45p~!DBi4b-=#>{2ZHp8okO!pQrgJ;~v2a_!4WLa4v|= zKj4q7!XDYi{{hW^65vDc#5v3c|4_q=0FT@ijoxXKf2)?i6!6PeN27T*xGXYc|Mh^c z1bmeZzFgD44!8sN3tiFg1pHLMFSXH2q9^{zxCf~Ke6#J84blwT@*ax7XdpPbsgv8rtIfa0RJ=KGpz8W^R*pH0Z;!ee#OZKzeU4W z0Dc4D7un#GHT)^Sp9TCJD||77S^hS_KLLE0%?}4^KimuW&ikU#jaK@kM>PK)IKWro z-svtIJgDJA0sk8CaW?)ZYyMLJPkJC49bwC(C-Q-Z>{kkSZ`@1K?}uso$ySKD>g_Lx3gHdk0|0uE!mNO|BfENbduH z=K}tL4gQv%@BV!-pN~YNi*4{44Ic-1KH%jxxS!zz(RT&lPXj*H>MykaI*VO~sCeF& z0`G%IqtREWL+?+XXFfcBz$+h%M#os;ina-`6@dN03QKxI>(~bPu^Xb%TB{DuQdWR? z8@R))c|0-i{(!9nERkMhzj1&Mf1+#n6@YI$2>d$0uYR(#Y*LH(uLb-jz^71$wu|f= zNcSy(Zv=dt6^=U5wgUDpU|p@L9e`&)6^&kRvrDbEOJ+aZJv^P5_h`U20@fAp8GvVO z>>6GJcopEgta_`<71I4Kz{lb~IDTA&?!mw30M7&bbLuedLi^_TdD>fkPHJWPw8#IAnoC7C2;qLl*e|i3K*{+)bZKRnjq&K6|Awa0SPof2tB! zwDQ3n4SjHB$)CA*N{Lrn_!IeA%A-{qSH%3mt1SF!S8zJR(Z_`U@QWM;>G?l>@aU62 z8LEOuh5X5+yDNO~$bdg5D8+Gw&L6%rKuw_;k*M@5wnnbP$MKRssj9>)Lj0*y9cKSJ zCD_!nRpH^04S!5|zo?_^EQKFcZPTvps(q@0|4X&aaVmEIXGMpahj@Y6YJPK6nXAfK zs;pGy5>>8LAx*hed+_vYPu9a zfGG@@V;%mENF!&=a5Pph{ z2IIOMwd3%55cg*|HbUl^+@I;7*9mgC-{YV(J%RhP4CgW6mNL;v5>v+b?jqg?z+-X- zi7^~C_;X$=b!WpuNiUE&w%||7`OX?5@l5}mNb2zzbB0BoJf+Ur=)c6G&P-9qaGp*) zE_Kq=$CQjKyQAfpd_0~gD;`tPkuN+9=ShS+%ejYfi%uilPoS1x;r;{lViR{LAg+RaLwrUHXw6{sC;+_`sw3X8$q{=OgZ-~swS#E}U8YOVE{cPG^SRQ9W+ z`qpF6KlI4qRR1^ChaEiu^}()`HWXk^sj)ZJle$4@a%P_eR6iQ*l8@^9IMrVVS@O|+ zLsajBKPj1gY3=BpfgvZI1szh3>~}wP97i3LR8x-X_dJTEpFx>&bia2{PtNXkqWfVW zIPNH4<5=7ajZxgl zGEN{q-9H5{BZb%}oeH1nHrja}>dDEeQ{5k<;y7X=Qo^(z^k2mW%8jr_>IKeM2xU|1 z2eIQ7aNXA+8|9~Q6FtGMBEJdB-CYCbnI9Ca{K>MU+#I@Utr)B|jUsN0aTH)-O)X}bIgW3zj-<}v#w5o^7$&uZ8$O3)AR1S5 zV~S(f05nQ_+yOmv9r-7rQO1oT$K{CL)N&qJont0UmRiA$>l`0rs#34v#uCS6Ji82umT==S#{q)e z$c+uDm2)BIrnJ47k;jd`$;tcA$2SmY4j+agc+ZfGy})QtEOmZ??&QqWzBXwIJ!iK1; z2<08;7(&TN{mgv>q2ODOM(Uq=je`T#78|KMdJw>c1rcf{or;wz^)JrLi6|@eXZKbj zqA#VV{?ePg8{UAgyr)Kc@1zmB{&)_0FaSdH7%4Ow_c6z#&*5;cM59MWYNo^eEo!du z>F7Sv(Pt?dz`E)zG>>vPKN2jD!`&B~Nx?c!W8rsGjMM=R=TE>&PIgUpp9Unhm+L&! zUiisv!*#yX14!~3*GzXm>|h+n!-%fS)ui+tg2olxNOqiyEa1A58!kr~g35IjH+nb@ zM|yGPb0fp?DcsXFiyQqMyGcX=H?UcPv%6+d^OLx9{nm`Tqk)jL5=qu|pYtTt;YSyOUQ;i-nRL*lLuUhMj-Gbnt%aEDRhxO08khp@rt148-AxrtCR zTA3R*G$hRXb6mM4%sm`eZVB^&j;rAB zZVB@&4hy%0`A`lEw}g2%JHK1Pd^9`1Tf%${ySRG}D>{x{+&#Ak<=7l{ad!zfCKyS# zz`xvAJDnJIa;Ce~eKJCj^R!zs0Xw?8{21yN-*#7+zD>4Dx)mYku68;B$28pFz8n>q zh9zP^Bk4DUa-(ySpxo}hUZY41g3FWrfU?Rt2aw(b2CMtsj}l%hT+97{$*LHk?|#sE zJK+(SW5xYs&lc33by)EX=ZlyktWkf58@V?*{|Eq>{ocI~6^<46ANmqDg#PvnpgiZ? zK`0sSko$O~ERGfTR!gk7Uo&H+Z4&TbcSZ&OGxvpr@2@f3f6gQ?1p6Yxz2k7|#)?M% zm^6t<{^A^guH-EDPWPQeLeb^^TOaa9S<&2I_Zb7GE(fh>?p>LLh0r2XCeaI$?uc_X zad5f!xtleI{e8(&io*|m$sE`v&^gNePba;@+?#NFWVj87`+L+l9Ni8_Um7Qn=96wo z4(F$W<#M<)u_qBMx5k=-ZIe6A;fw++IXP_#qDxJ1+EjB^?t>$wO>>?A2v11bl|2`t z?%V-qH=ITI>u^rZ!C;)JebM2({}k|Y{sXR;VkGs)f|TZW`aqEEN}KC`ocJ$AI!r4O z{*K3npm8-fFu)5CPHFR;%e1iaULTRL`>?bc&fh?Q!+Gf-2s4uCi>7JS&gTK{m7I2C z_axeBcX-fwQ%|lx0S`#4?@rrd<8TU(KEUApmeWY0@0z78O=%PAo4e-_^?O*_({2&! zbflrQfs|u_gkh}cUXDs??MNf7p&v0teUy=QTR&ogLHMv#rrn-01ubZ{ zzWZ8Kx_vMTosS6b)i{=#rG))L%8zKt_kQ7&N_lr0wh;LZ#bNI98~B-vp;l(ve)l;DGskyuo3tO4*Ih9ljem;QIlN=h_>mhf$9q`E{vXQTJie;p{2!k= zbGLKXbC=wEliVaX3n37eut`9Hum=zg$PxrJgdGV)KyhOeT&tCQs+G2&sI+QDrPiud zTeYsW+G?w{YOA*HR_oH%s7A$d73@OJO}R1X-V{e?xX$5tjB_%9_hR1y;QUF1kh z|8?YO371KGgaaC!3KcGw5&c{k8i$q_u8^i|p^1blrTrpgg+ikT0xBAu6FQnuwZY{> z7gYhOl_}a#6!wScz`}J>Jxf)^aJ{s5LsjS?YpB1GAVP--HOPcUg&sveA0FsQ{}rJ| zSGw@iQ2Ttgqd`)&0D%2Nq%w!*!o z2tLFNc@InVLyC05w@5pR;yQdj3*Ra;WW(bCMvL$f&mVhPu|IM{K-lk7!fjGLXC&M& z?G{J~UB%Kq;2IUWme4O`R-=TUp=fup0X-;HDafO1!heL{{K17I#eEk#aI z&1CjPPO~o|GwVBIRvl1>52JI6oS{x5NmFE({W20kwBX3Oy2*;*`Vk_sJ3%u;!Sy3W zWKV)7tzmlemW?8EbBa>XkwERbcc;t3_5GbTzA%9{_OS}hB7(BjLi@dwF1eD=kn~%|w zr^`fs=9OV8T1VvPimxq$LL%@?<1jW`4K}A?80AU zL>`aQ3}0}?rfv~=GD=m^VArNoMC7R`g*x(^cvgt~RxhAsFIk>}KQNHFp6LadlY#aHMl+dT9KQeCG|U#bMri+C81;V|-w`ZtX! zM*d>=fLh)Uy2z`>TsZ@e@{!jx;FGVR*+pL0KuF#~&%U97s42)>x*(3+N~v$Vw#SA8 zctrS z{Xa%3^%<)zD6@2N>dH|7LNdqVSFc>YK}2j>PSfzb9#PVM8A*=rC`HVS?mGDWGQs-v zC3XRiA#Ob#9g&I(^av73$aoYbR!b;L9o@_tzW_eR2-3xv&q&3CR-(R0PTIpksRyM< zzIRYM5#bTRB3dbMQ72N6_9{~S1;bqgA1fMT-XK&Z_528C{D?7MlxhMfE1k$-sjft- znvHviOj1kgEpiN%X$lk3d}N@s??6(|c_NK6N~1+$GqZ$!!blJgH&UXR$WhYv!>8IG zIG4kipqoUqfqWs|TE-YCj+geQNI9FA zuMv?p8D*)3)4d$NnIof=op+Y6-z*|?Wt6>KLudut)jT7;#o4?DJ;Zz&qmWjod;M|| zSs-Iy0BUni6_JH9!AGC_zDmk#r@@9{H~>;&dQkrSoj;Ww!JHPRjeE>rhw zjVe(61^OfFq$&Z4HXnfc0I?&~VK`D^)uRq%N~xoPfv}*m&qm6@F>i=zl_UdI zqR)wdtdmquq*U3rl8UQVkuqf-#T~94N6MA^EN&-k@D!A~kUVEZCM)|x@^GdRnW7vH z@@A$HnW~%+P-fvOGEHSk*e(1lmOTvRj!akTUI0*iva$>4?(|F}(xH;H15}S-dKFoQ zasUUa)ykgKOSPt#YTO7=tyO9Oa6VsjhP?_2J+wu8XBtg#NOYDuo+Qy|yM2TtoQ+1! zqLPr8!n0AcsN@Ttx@xV6F44=8Avqg^T6C!fY+)7006Ix+qNIGZ!+wvFPUD1aS(a%t z5=YG%l3BTluFzc_9#g2+dVy+Js(UHH7u{r+V1P8{nq^y%)Qknm7WD;4^e7QM6dMaF zwG9KHP(Oq7u+lQ35kxnrY9JuzCVMFodZi_LI7Mp6*+-I_)ijbg(Yx(SNy7S#-qUBL zC3>&7(((s1>*&waTGBT}|7br>de&|9Pf2>!EP6y=N`3^uESp4M&XKAp{49rcUJE3= zqP{~3PV`^)M@UHS0z^MaQ9BC<-sq?5MMLtXovmAxdJS0eJT%34e_i2VzNH0FgX#v4u8Tw>{s_|QAVv$JUK*$C;$?JG ziIln-h=d3s*0<2V zC(0B>_`*)yUgB{%$%Ns$8-kszHZV|k4&3RKad#}-Qm4@Vok{|MnZjys22`ag`RLOP zb;(%#Ql!;L@C$X`WEh}^B5kPyDP#!i68t1HY7L0uzGS)oQ6vIs+>(VfJN3Y+m`Wxq zOghf|N}WbsCv*0P$k4@0=KB;cS(c^JP~tndqj(9#cCuPgv^FXgjTcZqYN0mjMl>(X zh?`)Pu$K5RE+rK=dE=Q>&OZ}$ff=AnWz#ljd6knQEPeXEUYtEO2UTGi7}|n<=DeMkw(n?(pn4P=ZW9MK%661x#>C zD(#(s^us|(m2Tyx{g)JV`$yRx!9sjVwORtQBx$W72#c&;a{%d@3Z#p<(#MgOZ1TCH z+rBv0$<^fdz%jnWUfe^_mW|kdBjtP`vvH$M=FvN6lO;spQKSby){}5pHmxYt=j0=H zEs8F0gsOb34^!T8nXKdlppw>?px5p%^~>YHqTYvIJVPA~Zgn073+o+p#HA566lhIa zX2*I+`VMYFFGx!D2HJ?FR$zXIk);JTsP9dL$JCt|)dN^HhrF_+IuRUkr!1@9M8@z!fC~8@0l>1|WmmsmMEVUDs1jg4PU2f8~qmeG?G+u|$?@QI( z1ts~iOtuEL)0br>rLH%O;j?d7B%9B|i}e>(qXrTM(fH)G5$A6TTdfvYf= zDQ~Q!T0Gg6*V_Tqn0yz}UEYwVRFQZTcW8ZpqK0S*zI-8UPbT32ES?q;rDPJa31$~E zWkiJJbSNp5CR+huRHf(qj0o%U7UY+!6!LYMbFmmhRHcuC2xY@zQhfx6S!%Z*8R{go zP-x$esL5s3B9O-Oxmxu&60!C81GZdU+24yO$l=K#=vQE%1|BR&y1txM7q}Kxm+N0O z8}!20UJCVqC2 ze)fpgX9~#ix?Db|GGbprGoOQ*l~KI_EVP!&Sb|y*exJefl?in!AfHneu^%S=*%^Fd z-j}|rm%cC&sD!$P^v%V%JpeOH?S^6^Zr*pH8;VZMxIrTLiX{oI-R5 zA$n?3x^oxuUuax4xdag*`5!#3)f7tD!#cW3YN}mZSS2<6qG;8yg;i2huQ8@dY8th( znuVVuhV?IaxMr|gO05mGak~=Em9HHmL~Yvj@Xyfe)#lS2czoKP+6rxgqwT4!(Li3? zQ(K=Ue-W=#!YpeDgw{5w-{D6`bzM5P9%wZ%4AazRJz_SXkF-=R8n04Q&>Q(we_;Kp z9ytMZ1-iVTux>!lRadEof;rw^SEGK9L~QkjPEl9utb|Jghfu|Jbs2_7;99hqx_&uU zeIPRs>H0Jc2|No;b^UYneBeK%X~;Z^bZjN%3~*{8Q}{aSiuT`;(w$pfwL^PF;tbpo zT@S&fb&XoP&*>MoYtgyN5{wi5`u6|*di#Gi?S3isCAj^hwLV6X>qEsBq-)+l+UcL_ zpbYk;{j&w`EsKQJKxMvSec0|HlH*r>+#SE(LCDmnot;I7z^@T8_4y2`@_4BhKh{-I zWR)pO^*Ie8o1>&Wy%CWRc893@`~V{5L||O|Hkp2k?ye!Gu0>kcQ-3>7Q`8fPT%p?O z4s{4IkFN7 zX>B_^I|;+XfD$zTWIku0ZEqv}E{tmfqpsO|2*h$kz--F1rsI)rU_vcodM-M;#$fGt zk*=SBwDo=XzA;pD6EiMHS{JM_9D5NNDuf|MsEr6Tsg|IDTk1BLqtv(x=oi)jL`|ck zj({{CYAjLJ2tVC@Hs%UC&F-@?Z_?~y8%s;;L8BefSe9!8k0|=PpctGt&k%AQ8qeT@_A}lw zB2bYbl_M`AS#|DT01#j_%1<>`6*}<2wd!`HeGNlwdk`EZ(}0G=)1Tu<1iLq`7emsO zLlFQ)Or1-BDp4Vj`JAD)y@d23JbDcE8cu{jJVgYmm-4KPgDgiiYo?}%-9x57fUYTN zd=lhezcgjN72Df1JdV5MD01Ge+6|r zBI(+|BauerJR1n)^Q6LTfWoamVvrwEriw`W@*`vRwJTM_jtbfTp@gT0;f$(SjAIW-K0}2bl~8YxWXE=E@7QYf{^+yy6 zuUaBzq?(Ghisn8N6Or+0bq3IJXM90Dj6~olbPMAv;xsGJ9~CpcGD(#PvGFr}L{DOpvDw9lD}$p8{M z?5BD%8TM0q!~RLgnVM8xq|Z+a*$u z(;c_7I#dh5>oBh;P?8}PItmK6Zo}X;J*S@35?aG{3qnxlQP5Vmhn78c08B#*NkqPA z&E`o335xOh56q}q^Hu_BpEDzDpGFBQF#DY0m64s*j6{|QRI?89!LV^gQq3V#ac0>5 zA(>dvnO?7a9VljIN;v`}j>jXkW@gp-q<3aT>=#IXK^p%-;5BJFW|osB0!k>)x(?o- zl~Q+*DSxbO^Q|K}d8`n}#$E4lR&}hY#E_(fV>4QUBdi&ir5&49f7Q&hqxK>)_c%h# z_8fB_0%msB?Zq!7kjs$@j-e*&Wb~1XCnLnI3k*%ZcmUf?7=8h7-c} zGcNB5-pEhh6I@=|gGxN1RJ%SeAB00rD9e(gDEw4v{T7y-P*7KDY4gH%4LU7aGOw?+ zd7iX4(NWFwq`gC+EK3eZd!EuRgKOp$)a#ViHa}|L2b}ydoHF05$U6u)IU+)G6r3{O zG&x5$6DY~ixV&76%AIc%73CTNWu*^+r$I*I04jBXnq-_nl`c@T%tZjyYD|kPBT%O? zt-{aB@$kEu{^Xt_Jqu7CEWy9EvnXq%F7b;OpE%vWv7lsjk<(I ztz*%k6ooa&5Qf1;#t}{O3)I1)X3aBFZbk>ZXuJlRg`Z`xxTChHMLkV>oD;+Ld9>&8 zQvA(`=Zz-`#8X7T8}spF4MDp+F`ZE4K@CdPX0u%%|QVjQ+K z?>a09E0&r^i@XdIs3;*zQE$#Ht7bLnuB!r7e{w-vm6w;)04Taue;I*lZB>JOjRN{^)KRNuBJFdQh3&^FdjleGndgBsssY4Hh=2#aOC~;qEOXQkkk(Vo&bML#%q!LV z$QSA&^srK8u>@qPm8eFg+RdZ#6NtUe57dibix)dTQ^$fQFaxFP{Jd~3rU!vGrvFn| zHW2BBOn*^$jp>t^{<81_#rqmMKeA8fES66Vo#wSkAvr*Fep;XzzC;RlY=LvdrW5;X z)RQEO-c0UH$k;6aRC9*bo{EtcD;6lRr5;8&C}Br{=)?yH7lNvCO()($cn_(RwzOPU z@)0McO)bbuKFp-FtrfD84<{np-H8u_q8O$_+Ne@lpgNJb2X|OC2Jz5YWn>zI8f9z_ z$kmH^8Z@$n2Aw;e1|7zHRH+BDD3rPn(<8rn!-7AA{W5B@a}-{W0AFRX^JuB=M9P?W zjI88Sk1=tatc;>lF(!_emES^rr?fvO$jW8tgj2tTzRng|$th!MB|SY+R-T2~2;Kqd z#M_F$?=8t#S)iRFaT2qHJq_)x6YnTcb=2267w<+A@goFp=fd9I{IgK`or|P;6*yDg zgEBT0Lx$Rla$$3^$nyRhOqTK?G*AHp$?|=?`3R~?r}IXsHXvn6_d{8H8OE8?-6V_O z!VJQc?yxL!5`)(|IQ~8nnR9$7LD!2UOLBr}HG->;DZ3X%EA5XXlGjeHRj=2Rkjxo(B&7ib1EcN*F%o6@yNn zRl+Lh)-N-5`mHnx_X$6JZA%ObodHW#ArED#x9lBAg~lY1-QUWl0Dx)~lyo*&YL%fH zV%gs(6?d#VhxStSL%-44gv}Pm^FZKOoU_ z81C@KC`5wj0AhT3UOfkd&smYMt1wFsZa!h2SmEgHMgUX9ilqDEj&&7kWJOAS?#Wzf z$EScpRl`X_jY60}<`)=#SH@K%5Zby`vG_(3tu7%^8#-mFrs0m~+U;n^tJ10sXy5&- za`w;00r>|ATb0i-ZJxJRmF9l|AS4%}!&+6Ae-wZthoH5rDlg-ggZbd8(`JZO1q~E~ zr%nb?p@9Z@1)R02QUgu$AEf}QG|((d5QD3V8fcN9wE(ErK&xCvvYH&1McU-kh=*0R z8fXu0oaT$_WB^^(ss_!oQ+|%dyJ|q$ z6A*A=uxsrGv1*{k>y9<_ATSIvZ(S8LMOA&mmOZUe3z61)Y~vm%}Ct z3k$2`{t9FS*uYjNvO9}d{KRx(wUgqe0-l&wNA0oX;L7&uSo5DCNF*o@&tv~UpSU`$ zD8SdSCTVXa;ZtaiYtjia33(qvZjE_t#C8*+b4|8{R7K%`8ZxYIcyLW#O+%J8b!|L! zJ<{rJ#3%*7BilE^9fl77JC<#==;ryQ65_dpuBP* zFfi;Kl-kcVAV00KevVo^rBXdcm3!AG?UO;J*JjqIQf9^QGfRl|>B3MTI)t?WMP6T` zjs~QwxGNsNA8GY8!c(Yc&~Zq`G1yXXA+6Lk1qfEFV2@vQpmzzV;bYK2sR<}*NPQa( z7$-lVD+wz+!Jzp(ESA{yIS3y<$bt-tIiVBW^6dlw{B&t(S^b*y_YY;)I7QPi~ zFp^UJ6M@eT%h8#7i|Y7h#g)Nd4- zPk}frjvyUSKSiuy`|Gb5V!FnwAz+S&x+bY-k% z;*M4^A8o5^mUt#g3#=JTa8NOn-W50`pZspxcEk@{8-kqu0a-{sq>ZUa4+-Vc-d4?Ez@IRs7U4znu(=;9VYYhD2Y!!kDdk0C!^=va+G=-%<;Wj zj`6PtXW&z2jw=prK{^2`TgDgJ1qQxk{)FO(@KV5!THDf6Tn@%Sgz1Sz7FqcAZ5eA{ z1Sz^PZaKP?wunSM?r4lnh@dS~6tyHt z{WM1dj{HeK07(tx_D?TpF0Es$6F&Sr`O@#!`YOhZkmXd| zzd%Gt-h_U7tC_1IUMbbOAMJJPK=mg|XgDKoucL&EZ2XyI!j

CbnldRYZhzQ#>Q3 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⪚k!P-OU-8j&orLG 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�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 Q?6>ut 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 z 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 Y%Xl8}GF$sn#3ukxL~ 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%0A7TD#JwlEThG3pt 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�(ZMa;aePBIy|{UOXQW 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%0A!uOEY4Npun zZ5obiIt_LZbP?Ff?sxE%=C%x$ftcWqxE{n-0UMqynohY$(}|C0h6i$HjiwW4PX6xe z^g!oJ$=@PxSWTzkTNmTv^2M=O-U!gjm;D=2Km)COCCO3OnojvPA$0iae0%zRS}bM?Y$dWWCehtZ4bA{D**%SHBZTO85}Vy$n!6f&j%ulrnhn10RW+T0XQ9GWD3!m9YzI-yO8BJN%I^p=g#0}8pPaO*OOZs?|wWd>8qv;ebRjkK<0#gcc6DlYYvfMcQRbNsO z(4yLs+Yhy{a9NpY$Nf4!(c!hC!W6iD7$le}P=%@Bet<_1QW?7KEW1lMAUB8#Q;&P` znILWy6{g6$C!?pW@22p9hg60tOcUID&jE3ZC@M{M51tL;R#8;ycV9an#BCKSD(&n( zfJZI7y+TDLt?3m0s6xj(TGJ`KLvpmHQ+TK3XicZ^uJUxS9pgUp94ffGya$MRj^aI6 zys3&eO}&AEQ!ZbDSswU$R~Pf}LmnKmWO%rp583NHEXCwfyo0_0S{zHo_k|jPEZ+bv z&Ox;|+Ejl=6U_p&JeS{W3$vymy&9O+V__C=dIYmL4GI=!9f%qtm~|M42xf7i5W%dI zkQ2eI^Fd5b89gU{@}TE*7P$Q>XQ^ z�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

?o=NB|r`x&mvUokY^ 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 z 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

Dl}11f3KL1;DBj(Rt=|CpK%RP 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 literal 0 HcmV?d00001 diff --git a/flexric/lib/flexric/librlc_sm.so b/flexric/lib/flexric/librlc_sm.so new file mode 100644 index 0000000000000000000000000000000000000000..11665b3517c61086628aaf9485f60a5019dbf4eb GIT binary patch literal 170976 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 literal 0 HcmV?d00001 diff --git a/flexric/lib/flexric/libslice_sm.so b/flexric/lib/flexric/libslice_sm.so new file mode 100644 index 0000000000000000000000000000000000000000..42ad6b4702bff049182c9aeab47d85665da3c98f GIT binary patch literal 226528 zcmeEv34B!5+4ls9Mnx0X#-%!15W)na*~}t~MuQ=iaT<#Y z3U0-($^v7D`QJbw6|YV4Qm+Rqyt%S1P&f0# zy7~VTeg%p*e^*f?^}2j}VQ8*~-|4{9CY5OLi|OVwl;1%8kgIx|tD2YXNi@i3PCm6T z0I*h)#dsy%@1bRCzkY6ASNShL`~B&m+t=#>_il?O^|B2)zqB^MNxEWAK! zYwKz%%OjP6lH&66s**rOq@tvHNuZ+mf=E$qd07emttcv95UH#S5M)V3bxK=JSqZfe zi3*nlz-l74SW$IRgeccW>H^jEb+v)=vUw%rYOBT_O?QS3Dgq}@pLXKZq9eu~ex$v1 z?~WLE6pS_m|I6Tihw{HvrkY_|qP7hDPyRA6Qa{11i38VNeLaTXu)y{Jpt?QE%66vF zyqlKm_bJ z_0(s3>KA(IjZb-+|8x5v3;f3d|FOV-Ebt!-{Ko==Sm2B7{l5t}ZOID9G7~3c1j5a& zbwm2v!%fd+JU!8MQ{vKfyIZD&rRy5EOo>X@U?Jhw~_^>vCJ4Lf{FN#AvTKpU?(XY-GsYRzDqib!5|z9^@d~lxuj)C3qcf^!ao== z5f2K5j+YFD!d}$;V2LPy*wHz!%s75HnRPfzv$%AkpXBtd69XwHld_ z3BJO3iTII1@ODe^2qhS>1oP2lNoTD~kxXwXjxrQ4Q;HLXVu7V7!X}*uyc9t3RFns` z+6<+bFBHFf(J1{G(@r|)7>b~F8G3n8Y6mE_p9!_~mfAMHOFFw4YO(Zv@E&tb#HR^? zDobFC68O` zFBpsED#b%xitPJS#nDJ%c8h(Kz-%E9vjk360$tzubi5M9K_%*9yhOZ62p($*Ucq-s zXSIhQjMUHb*C~N{Lg4E*qtaiLz?mKbuu<>V-y_i?C3wCNTxSVx<-4S_orhpLiH=qR z6+&QvB``%LdLAMeyF#K;zeF<;1Q#L*t_w%&*M_5os}T&>2H5(;)kQV%6fz?UhWGJZ z(z$>rH#Mvl@V{zjjTAvfTJWnB{5TN=TtXWl(CTok0J#HQX-vOCPI}(Gfj~6$XleGR zt=XeP^j~{v_8XzjSm@DcEAZL?zb+h`(Hf4O+ZK*yg`=}UVn#cP9pPvgWM=1vV}+gJ zXhBdw&+YX`!s*fW>CwVoVzw$42)Kxd8J?xT$qvxMdaHCVAE`P38}q_WN+l&XkBiuI*5<%d|M8%1P$u zPV4(%Ww_~SA}^MEmJO7MA1#*KV!LpvcHxj-V)8Ue5~!nj>T1O}Mlj+QBTq5fzXC?A zSyX6tfG9R?&VfYPqnm?r$-@N(ZqCPb6aSl$Y<`#6Fk!wWQ@ztdsyFwbB^+JVOP5$P zU!qJAC24EU1tqn`1|st0&O|Bt<`?06z1E60cM1^_4UPb%#FWuw->XeM8OvMi(1Fd= z6D;_yf$UxLbMRjf;^yad-7WH|_2CCe=bp7JV}JyRuF9fIteG!SRu?5{YaT^y$!1a> zWCFOIU~YwFIMzaT3de3?b&Q|2kkwGh_}1H6hz@Fr2KqV(9~>2qts;}6!}FjRs|pDl za=|PXa^{D}h&R<`IzRd+A+w@z;sKnssWp!@yy9Z7!%iujG7CPtQhb(t13tSRmw}y? z_0Ob!K^IXW#a$akg*5KkBDTxCc|hE;$kfxjy5crDjp@1WF(9V8JV&Evl0-idKa(ZM zOg>LVibnfyqPwK?*R8NX)6--D8CJhijM;**%3^%WcS+}7!iY7C>6$x?uZ?EUz#yS6 z#D$y;m$`DOCL6_?iA^|`WAF0egB-$MdIBMgDWK=c#$hpdR0gBLEO1f7xqzHY^rA15 zBHNyXLNTd{ty;(p?4fXL5(#b9%zf2jX$w8|DJe`BR%#< zzDqh|zUb?dA==bClu_Q|o=LVI-8rI8Fkw2kp~icgf{`f(8TT1L7O-s1HSoOvLLRDYk0y zMii$zKrYHFQ8M6bc4Hg{&CWr_>H z8ZK;10YDElbYL#-5Z8Z*&6S8RCWTuvZ`?=*p~3PQ-zA;(pFx@!X0T|eGn@?dusM>U zUNKANn!Jp_GxSh_q`8w|G4F+!H8^Cqi>Px?Qw~kSVS?pwD)$-kY2n}ivlpCj-Sh_# z3^?^dt_PJpCeh1KYQGd}A0>>uL8TU8YCC5@F2;)$f*_BUqlFL175bxR5GHY85=Tt( zGE+*#Zx2n2&-9s^nwvvaI)q2A|VZd*@y(v**);}*kQ-LmI9JRS-PxR}hR z5G)uidtUw}iIby%4K)8nIT7p)x9oWU<%md=v~{eI32JdCS_MW6km5nCWaCPria%|A znt@lYc59xJnze4tlTx$Jt$AE(j7sjXql9n;!9^QZAUSSCgr(S+I=ePCG>U_LtVZ3B za9T2dW=F(L>Nv-J0_kHR5JJwI46OwmqEU!s2_!?;?TAY(bX~Zl9f4Y+@Xl3?%6|UL z$9;WR77Yg?b${1`X~07+_^(~?Aq3u-27F%^e6E1+0P2~5?4Fe>$yW9ZmgGnmmSpax zaL~h~67eypKyeyFQ^47%9(4kc!Y!e-6i{xy96-^~dMr0K^h@<&p$%wk!xaFbc37wb zM9ET}e2s-x{QD~aU#@lU7VZF`dlN0{=h-l6IQn!hPl%7@$h?@9uD742M(ObVROcxq zT}x(RE72gGf5&%8=jX()>1m=P7!ubK@e>8(AdB(1V(iTlN=hA-Jhf$29`I>22cG;9 z{*hc-ve%(h)+9H^83pkQ$2g;q`J8)f&HW6=HL+Fe*yB07flr4SL-QG4?1ZD5im#e2 zZ9KWfR>_pdc@XdxN*hm$u~i*HvzjVpoItKlszi9;HuC#Im|hnOp_RC0d&m3a{3kzm*?H0*yZefw4Va$cICm@AqVPdx|W|_kIs13t$8qxb0&*1`; zXQK=`&Rvc2T$C50ycXqkC|9Gr80GaS!y#8Rvh7!MM6e6c6%>NP(bW=j%sO<%TB(&N zL#xr18eV+vxZg))9yNm3BN4wLO#`^rV+a(kL*QO74)nqwh+QIHKnz+k-^Y<3ak*oj zp}VBh(FK}K4eJE_Mg>1zz#p;TGZp+E0+)f-iYxTyf!9W)KD~}eVgN=9J9LzGVnN;m zxDM2J;)*s*>@napwPq2{L2Fzbg(G-^>xC^h;2wB1cV~p7r)QyvW?GF;$w4_6rz%2@3^CfU$RX*lR{=#i(fgD6NXITriecjNyt=MHnras})WY*V8TB;R<&$;0DeXql*4(VY8b>U%+=sCyUsm zWQ%ux=+74WJ;j)*RBsZhPd#bWeO#$N3O&|<3Qb#xx&INRJcn4=dlHx+WX+BK+zu;Hq$Kn zR7F2Q*o;y*Y!Z+-2U@rch5ON;6qeHM|qCiKwU?&E?{<8tW%7f=BuQ2ldfYEXkjSieW>!30Uhq)=!5 z_8X9<2f9lU*quP#_PBHgP)R~|pUzM`Ndo~-qc_%R%Q3!|aF!Hl8t2^j4KR*|wly_u z14*Hk$$Wf*=RuebFf{us?2(E131E!@^0sx=k?N`ipbADEq}suBPl-~0gF-*Hl=BQ} zM!;tZu-HX&imA%&XTq)Aa(hvG|NZ}BZqy~{jv3&}s_iR6^zR0k*6jxL7Ga^+l-*{g znuwn!?7l@zlOVH9+0_a=AZj-^wPsiauBS%dxV}#5%@lg~TY6KK-a$-{JgE?}l0UHc z=WsQ;jg>l z-vPc<-Mk>&6za|JW8ksKjxa)dcw(rxrrF%VZ<+l5{eYDe??9*QDDwP8V#We}?fxk= z_(k@vp)L4lhVyy$u7#anWaosoWRDANxtNyv4Qd}+`=j`Vwa*x}&#b@F+>#S=fZ{O9 zDT=a_pxj|mc2txDf$}*}aN=WJsFQK_Qk>y}Q)O`+zDqhEzbkAYXwJ?7KxfZNkl;Pc zn<~2jsg+x)5|HPF9BO&Rw4Bun4MV-$O5!fv&EA@D)4+=#Xuotvb60ne-Z{3;smq1} znJYlJG<$rg_o97KoY6^jb*TG1drpGuvu|ka9z#4J=Iqc`(0TQX?6cZQ`QFm(QEi`R zpZ6jaV2SYw0Q=lM$L%!4ua*LM2X$b6F2Y^?5-I5J7(G+XesE*BWe*-T6cT0GG7@Hk zpJ0#;5d{0ReT!OrtXTZ@N6omO1O+HIUHlG@6yazHV>{08Jp~YQgLjv#7Y|I$(y6(1 z@lIsBFT&Y}ws!ALbJQyzHFai$ci3pEH^MLmfet4y!V84Z)(?TJUj)JnUu2)hz5kM7 z70)6yCT`4bIu)@M7SG&FJ~BS^(xS|+NvKl5Tmx7`&fUwZCgKys2CqC~Y*5YCNIHw& z=E2{Nc0&O(2?D)nh#o>9mHXWAD-_=;tWkW$J+B+Q@4-*ZUOnbX590H6KpUMho{;M`&tjlNHvjtXU!+63G`lY%D!TCEu9@ zNQT7HW)n~r>75(6bIGSl?iMSrCxy(|&P$?fma*=yrh^jkWqc}PP z|J>qCQ=HG<1P-M?bp?!y!FdkW;&7`TQ{F7)-tL1@gIXkO^n?yK3c}7f$;IkHj@4Ta z01*WwCxMH9Z0A}1d~@vQELI|3Bz<`k7EzKhw6Nq!X9~&7gb|h|hC29R-&2ZJELahX zRj*h(87#>1PfDJd0{bznr2&q2mPllNXn<=Lz%cnHaDmF4@$O~_(e90eP4rShmPa`{ z2QvZMb)fg!kwIWf%Orb;shkxe=hIk9igpjO#7U>*4epJ0aqMqd`b4}^z^}64k0|(& z0U*1TSZ+C7q#O<<4owX` z;y9bx3ib(Qp#}eog8%C;tR&d8uTxU&k;{l++25)3)(QJeOYbYb!}$kGPsWU#$0B_t zcgXPUKpG9B^;=_wTg3nvOcOWqv^h;#K~TyGZ@kag@*ovG-(}^Zr{$8DKE!;*gc5Pg z8pL6O<#4Df^WLj$ORNaV)!Gys(<6cJV8L%!@COB4*K=zW92o+Ezl~#PG6t3@cq4(6 z?~S3LLa_yrrW$?TrjGtk#9l12(e*s2!b%V;HzF9d1dmsOCX;FDG!C zLna}mzb;)y>ht;vrHAzzvA@dFYh*!^&XGQ8Co6bTz>6&SUllx)z*{o+QaBuNBeKU> zxV=@(zqa@F(W&le!?tLBZwyO%C*z<{uV;|z$c;&g9LwKt^u}nrr=8wqFryoB z`Zv|$J*apYz)Oa<3?aJYg}l#!OE`zZWcGsz&2jc7`oV`=PISOh64tfpdT!bZmzWsZS~~^TF`=zdiPT79B)bHq>a5rd z3Kh7e+C;cz_;x7v>hy65ICt@uJcATKm#rq?|2N1S1u~MTCAz-tMvxBWKYo{QriaCXX976yFP(pu=wP3V(;w+>inZF;pMEe9$^A)G1 zfIB$-stCEo`u+*AKBYRIJ0uaGDb6$5I`~??!@00Ov7GB6_8)=(a>kHE^u*34)n9Y> zCgO!c0lfoBb|=wGhG=^cmo0Qjm;1j6Y|N|v+`kZ&zG;Y} z7}6_m5`3Y`rJFGGtXy4umvn+8mp`H1aTp2jCbT^0&rJd8~WV3ICDJ#Hncg9*BgOX+z(O82o>u zERK28U@dH?(T~kMuE}-hRwR*7mcj^wwG9lmg~l&upa|KzMEC7!{0 zR<2eh2Dt<*KZo>4K%k5^D(wW9= zQu6SiFUx7k{FPF>RdX{(nCS>3zHT@EgtzN~)$bL3(WxaRmf&e$>K z0nI^Va>%ODdU6}UsbSe|GunORZunkuphlUISl72x(8`9iY5bf!jD>&CcS+|*Nw0;k zR&Y(@54GU$v8|Jivx&hqji0CBn#TVNPo~8mpH}cE3Eb_Fj)6O**8}G3w+U{)eZ~FO zJ2AAaHVc#Dn9#QF-4qR@X=F)wx=RRoellh|%l^Mi0tNS~Y zV=OI&co%5`Wy*FVCWpyO_5}AeMffCnvnd7%%Ey5mP?k^}hj&(G=a4ilnYY@$-(Hp7 zf&`seVT?zUPKznxei`mPn>+AFvaJ~3x3p^0Av{l*OtehyQ-Ay=QMF6-H40uL;5%CI zD;0dU5rWf#o;qZEG@5WAk9vg6E?Xz_cx}N=Fodt^WNM=A!@7M1nNv*=L7(6m8C$%M zCdnSsnvS>*rfGBr?M3#=s`winA5|j$&+YhlhVPQjnrFS*{!ziJ1$;jXevg8m4{)AR zXsH=+4yWhpzDEmiCTd~DZO>;(kvx?5G5oGbfy+E37uOx-*Du9Qex^b;5TBOJMYrL? zDep&omvo-rNK(P_^p+7{`5Cb3i4GQ;n%Rp^2DG0ad5%OfyQljRdtf4dh436|d0wPE zPbZ#so{)JY5&xxtzqZzhSgznBM8r;r2ydWKLQgJu6kMjPRtWBDi+h6N{yo9!0XH9# zk{+xo9FB%3(<9ZXtaR&S2uh+!FjOYqQtvMtS#$=HrBC;ff`Ms@s7%DOc?x_a18Nv1 zw_B;&8qy4sa5NI>1TyD)%^l;xm5A%Kd>!Un>Ei9#8rc76^_u$}1=ndgV!^-VyQK37 zfs4u>v#_~?iVMQzO`|RD$BMhi;zDuP+@u_tlITH6Z#P6sLDqB-Bm9m-`m0w*Kf-Pz4ZstFpkuG_`(M>X|k)Q>}br)T85w~jlx zU_^!5yM_tRPNsrIMg30*mzrQJ{D%ZqW|(q1(Bl`*!@{M@k<=jFjiKFzVPXWO4(kCG zelfT>qbK`$0BcJ4bN5FuLbv1 zi@R2FZ+3BWQ*aMd+;0STH;a3@;?8w(gDJQlB5YBA{7Z1(xY3wqp5h*?xYGG7>qCmO zO>l0vI6qgMp5NO(%z~ldnqsJE$mynUQgot{Ml3uCZ$-23(gk*bER?hLy+T8kqPn;> zXmrzF1eJnbiWJ-*5L7#3gDUNMIE;^i&I-o^%rFG=ff%AmrwC$bN-!I0iGYmw*)l9Z z4}(s&G0>EH*Z`u+p0m_qmNuqWm5hPFw7QDiSd z2;emLALNxT3okK!<#AW`_C)+(ifb*IAFVch{Q&o1(wPuv5|AN37yXUry%dC~7?dI{ z$4Md(aMeNQbR~vzg$tV9j2#1lA!U^R)$AULI9`(@{^wi%2JSeGVo0-+{yY={eDC< z2{~)8$W~8&)axTRm>=!p_aiScwa*J|b{xSIJ#WV|O0iEV_e|31d4dIm3|>E?qQ{R& zA`l=Sk-i{JmJmsf1d3_>h?G(O)0F?2!heM2|97@Hp0T?8z5TPR;>{Mkk5(DEdlhej z2hX*aD=3Em<4245{cP(;Y3y_xcRLOq2*0J4-(=<2_PEbZ#*fMrZ?52-Xz@lV-b%q^ zv+24atsij*xQYPY=SK&p_z?^*K82`Smreb`HqTyuqC`8BE%+yU3?R?iRipUCP!CUu z53olh;#H!`qU(&wf6JYQ_0MB$0t`?V4HEO>f>l@pUMkfXs;$Fw$rYBDvZ~>mFD!)Z^P3p)3_%ffnCxlEjTf zJSw_he67*_6TVA2FFXp}?K0kll=cND*|dqQnGcZ;MjI zE|YW?5DNFIcb@GqldPO9PMv5^#A)L`JNcq}G5L<;6UdXma|?G|B7U9lyz3gH)mPlN zN#~143{RScJF}ZF#+Ct}cnEd+c2sO~?`DVM71*;B;$cht_k5Rht|#J64SJYmvC2a4 zoI`--6lwzv9T-B>kLIYLXg_w{5T(y=3I2UdW4YOSbrEy zj5<4&P&?dj2CkGaaL)M0ZOHa_CYB7#OR!XY<8eyghaV&SKS{V$`ZgIkiix%xcx(n46r8bJaI4H!kv|fKQFzw|4P$~SMy!c zDSwDPMNeE*D)=S=|Mb@e{3vy-;|W|wN*3hll`+p`WwpPkLh_pV?+8%^bi$)hjV9g- z#1-R7l6bC$c8`73U%PE=q(uBZVR^1)xreg6_dySmN4s>cVclYrQr#?6x3^T6@?Fv? zv{YGIqs;p_m1Ty|0H(7%E>|8~gvT8%##nnPkIx*louhXuGpH@<9$4z%+f zZ>dOMv%mM&>3j_5B=4vF-9dN9FXKK;#4{-|ZOLrJBRGk%U+`VhdHH_UnViLE?i`y; z*lppNN^2*fb)=>BTcvfmOUsVU{S|4rAoazJ0DJN%N;)$H2{!s(0X}C_C*r#ctw$`a zkt#q&8UdbGq&)=bQj2t*I>}4-u>~N2@BCiBb8^=yp?!tWQI^m;zDqhyLI|eJ&-K}o z=2vuTJpv5nNHg%=mr%#_C_=Wi?E&guBP5>)GcP(4bPwMposmS`?xXamRSy#Iy)F0^ z3jWo-46ZRcq2LD#_(wK?l_+?dfNPAtM!^pe@ZVeTd<9=4;JTAkqTnM1{8tuS=7OYi zK7n&!qFIVUI++6B8l*RdslH`#9%ktrv~{2i6K!(NK$}0nh(}tBeao^W;^TztUY6@3 zzDqh=f2Y*JX|v&^pU8TxAN2zCQ3q{snw>KA(FPb?b^MKTn<(7YvF}j-?4aDPAZ|7v zs#fr01$>DG&sFd<1zZpJPEqhl0)C1Ge}eCl&L{zIl__^O1wT%}_qE_j1^@aU7G{Hh zf51Ew@#6)2iw#{71%HviV^9~C+MhU4{IR}vr=sTz`u!ID0!5E8df^6iVJpa>YdO(+ zPNa;yofn$mCex@_>=vazQ|MoB=`T|H zbxa@0lz2D70-1=u9n@9BUzPZPmaSNnK&v*S zJs883)Znn(n@*cFVHQLa?=2$ovGT@}?>05OD_oX6tg=3oSkuQX>UIVV z&?#JxKBt!yC{`q$_}!`y?vJAv0A&88&rRe}`zWbH55PI`_5v&XArrUf@87u6OiV+S z>!U)|zf>Ben;uny7l|6pmf+v{4(I=gpzWq+1z#lKzp&s#bUHcGXcfyZu-$Z;Vl5V| zpv5{%vA(-YZ6WD-hw+&s!cc1I32K?USkf4_@%=7uBb8C6{=s8 zVI9k#ZBpmoC{m*!Ra>N_BArS|gY@ltcd9<<$GL9aWczly)cN~%v~mSE%6h)RQ&F-* zOXg3MYll$v_iYeEkd9O9q`8e@6Z{4_P#?2I_F^TlR1hNUg3r@loyq6b?H-~!Jd$8{ zdqAlnzbBCvSZaS&Y9XQK&*oA@TBbyF*I|Su(!_U3C*TojuJkQgCLK}V!OO4`#$!#A z?gc!87K0tCgnxSncY}TT_eY9=$IgVX+9HHhor@U3EZ^AK#t$x#HT*kFA`!n=Xdh>3 z-@@bVI}(f?W`djK!V1BU)Ub~Uh~aS;!g_k zD=qPEzDqg_g}61}C`E`1!W@gRo!a{tgFpkaX(PfI592ML4=R+^)?qS2FSjwPa^D|>zW3$CY3SA}-Is;# zNtW(iN_T_^O-TSIdTW#2MA&T85S}Y=;?yM05QUrMXOqTKZz+kOkT}b- zdPkkP_czeUW^lh#@B;;Wj0Jx}!CxTomdwi)PFEE}E!;wdyA5z21BhhS&LxUIPS`wW zo%+X$K2O+8Qn);UyV=5JDBQ7rHYq`HKSe)E(9gB#kE?J0>sHogt4;z(3*1o_?nZ_C z3*h=`<9kQt6(uoQNPKQRvQ|l~Bv#hWw=4Jw0{*N8KTW}lMY5#|H$~uXvTy-~o0v{A zYv;2SeVU-3W6{6hyQK5YEszRPKNe_i&0hX2fpH0sHL{mK>M0J%UVfLSI5c}Xwqz~v zu z$;=iqF-zujB{Ldm{>5O5Pb^dl#X@14rEsQF_{Ys`5(keipa>FsN+Y2NWV(c!^qU)9 zFNwm`n1wKdAD(!BJDjA|sU45Lh5_CDnuGnJgDwr{7YO?on~WVkWI+)Bg*_QBi+E;f zS;R5)tXZ6{6paP&QFQDUWnVs` zPpn|XSOzq|T~mg1o(giY2=dwGMzKB?De3$=h4Gy4R1gR;1NVbcf1d-M{odBKmilc< z{cxpDj@;kscPKi#n7nV6MPH-npWmp$sMW7Qc~F5aREmf&L~)3vI7cb2H58%VCZlRo zYd+b04;Xh)>SL5TcB_f{oy&|dw^!;XD)sd0ZsF)?Uqp>q^fy?{q%+hAr|RCZM${j` z$!=D9cvep0>}lz(R(g*pz8NSpQ65ymc}nqiq4;d0QE(rnSZpLhfAW(cR7bXO+N1BK|N- zAfW_q2LTo_eH3U?{6_@;xyy|DEs8%+@y+lXg7TmSJzXjOK`7Q(ihtxg#M6EhQUc?6 zrSQB^$g>opO5x=jSSJb+{B}W_M9McMe#Zed5$8bJlKEd)4NAzLs{|W}pq&!`tl)nV z{_8FHaSDE_@b8ULOvb0*@FkA#e|L0^Qhi&fo@J@N!*}>SjqANaoUh>T2>2Ka{)B?R zK;Yzi1;bd1{9$;!{3EWhVwvx}$VYPUEt2oE)G+0`S-3vdU@UdCa-HcD@^udFiTH;C zeuV|!O~DUVA@Plpd=?Wo;bGb15=%$opL%-GKp&`~`I^3MlSM7Vsm1qopMOK}vsIE$ zMUrio7{MD@rlfP@Dz9}9Q}7-E|C0ruqu>`1cz@xtZ)&CGPXHojhc^1GDNgKLp}xT} zG7;|;E(E1+Tz&w$PGy>86A zGP;JS*awiM-qnKXvp+DMj7#E%in%~)a# z{W9Mro$jl>hJIeb^-SIV7W{V#{uF_e=mmVxGnPLbV}mBxm}2r*kucvbHjGS)8R?fAfu-m@@DdHpN9W!|6aut7{0MA(!POx zTp6lovfOz8bEVhpr3!w!fdAHl|5U+41WrO(Me5YQI|E+-CZ`ONA_z*P5CtWqP`t~s zsaJy1WlV@&Zy?aCz7fz_26X6vRQHf!g!M7%__eh7Qx zVx%3^*-ra)8YfS)<8h14n-JJk%iNbw$TdjvfI`tW)&sko4{g=umGWg66pMi@dJYq6c|+ z!N#4}0^9YQ1+jmokBx~JUIsLNdK{GPm9JakyD-WVuSL-QJ)e;WCQJ2WpHykSS(zrJ zyv6Y_5x+{z_f4JYwu|fO4!?ia;`Qy{Dfq7i{Ers=Oa(6^a2{~t+-wf@?pNU5+?Joi zmhFUPbE!g>9jwDYFoVjkJt-2$?X-gh(+K?rKrb$V?g;3B9xvr724N{rokyh6c^rXI z)uy9oDLO#7P&9g-!*C*gvnch?T4RH0>O$B3ifmw_k*u+MYg#q{{(Da{tW@Ibg!mdu zd>^)5(m95Sn@|1}EBHMEe!c}4sj#0w;N+G4-D)@34j&Mpk3Pa_;Y1QD{sm=GJ?N~S zW*mOXp*RtLNF?d3F;*#NnehHcj6FlYRn)5B4-5E17W{YxKaId?pvX8u8piJh^=3c8 z!L29ziTtcmjn}?WX~seNbUcku7pf>vi6|qjD8J=9e8=TVmRu9akqZ8_fPZzNQM5_H zR}r}11$7kGTL1e60LY)=Y;;zyJ)F@>n@NdI%zFt!rjMVbY3tFkTW$SbN$2)=xSRhi zhg-z)HWB}ms58o{a}VEPHzVrpx3Gf0EZ|>bBTC|Svx471;C_$VQ9O!{GLj$W<4Vi> ze08|<#1HAk1q9840tSoGqs{NhEeq($_(PfI?ZffMdHPJm-xf(vvy!gkJAD6h zh51oCIz|~nx`7w9aHJBsJtgdIw4BPK4}x}>(l9E$)_oCxXtLdlvGTLz>y2&5D57Yc z&8f;l`IdY5-7c2rrrMAkd1j8(JLr=~zw;6P3sgrTRkQGOf?7viu^-%_+c z6qN~B-*hn-c3Ow&Vr_q0t(Aiyw{y~wh<`5p53>9(;k%@h_@!$r{DuSghSsvZ|5B` z#V@j`+}Q5^hB&ss{+v?bsD{O(Qw;DOic+mn`nOFI%+@I9nJ8f#qlqslQR<8mUtley z1r!Tf$#AXD3?nv}MXw<#dD3eQ%j>k@h=iGJ2DQH6*)tK}O|-15Fk0@TTJA>p$fFJOeo)O8P4MR>M}OQGNMBeMK)v*Jj+9e3u+SVp^eHiS6F6PmY1oA{HB=< zTez03Bs0nDc(kV6bT~8di$WKf@DhB7Tr)G~SA|g71>fpO#ynUkjgKkN;ZnAH)l- zB;Q}lH#F$)jD_w*N7Cbql@tZgY54v+(BV#`5m!=vu~L&mj7Cmh;snF(IrMWzatBJK zrjjY$DU>##a)WV!fJ&by(qD)N4C3hLv&$!)Y!7L}nzE-2%K8Lt=ft3;xtbmTNVjdo z0v5lL!8I}d$ttJ;LFhnKs1d!f0_Ap;aexW^OS{Z^Zj6I}YCe zX=2S+BiRCpb77Dy{nZbcfqpLsXRPE`KUz_4Lpi9w`T_f&i!mC`LwN$Y`+gE<2TRub z%^3IBPx{9}@d=HCaSpeM_+$}#Un_PU-zA+VFSkBnd;u{Jod>BX`q48a>O8LG&ie;2 zII7U;c^$tql}$+4JX&U~^S-*?8HA)%sPkYVPJN*_`_1L19c~EwP%H-HQ{)bDBwypm zDZ-pOuFWuigC}Yne-q}%Smvvg`RmKTT!Tj!wkiW60O!gSKQvbcQT!fH;&0o9TlUKe z;|C~R530tx5FW%e_yJ087#fbw%u02usp#aHI0*eMWKKJ33L%>@CIU*74;BI!Yf2yf`_e=I{V_h-wenpYx)bptQRkCV zqs|>VSblXG2}!@t{DY41Lo8WuFW}fkKJuIn1b97}`(u@Ffyj5NmG8HFmvpvV3i<3u z>w4hny9nCZ)fcqoFFt+5%~@T&pd=|?^T+~y{tk8wm! z#A`*4O%Y?K15}RrB1b0+pz?q=C#0QnqobRuTAvwCf?|d#Cp?kRF{3-I%$5kVIhNT^ zc$_DluNqkC4r10_K-6^^0J04A9-W$n zQQ9G?gN)LSn%hAg@*_j#GfLQs3`hS$KeX_6{{cj)3c7Cv22f-FDZ5do>=;iTiFmUp zcgcKX$=CS~=f6m~mco^s_&m82)8t={jukeBTl5#}cu(a7836edM`H3Dc2Szh5Orem z>vb{t^}1+Chm}8^nZY&SG^x*iH)EiZ-tBa#Ult0D$h!(~-$|0fM2#B2X5k2z2C&;X z6l0r36uq{@D0(5sjHGk;B_uchkReFW@6ToF^tl$rb)M;SJ<1zA(`PHnZSM35sbo6M z2Ywix)ro|&0Hl3^@8OFLyObf9gFeGrjy9YqG2Kz+zD4AIXP%M!b#?V6Amo3r83YCF zRGPDu4o7Hw!&oS+u@pY%JM4duC@q<-3U{Z#U1;GhP)^x^8~hBx92&YbX7aU2BtCrTC^$Ty7~|qZCINij)SKLo@ae z&XJ1#wxEYB`Wy`#A1r2r^kQIQ7~y>#ElWOU=-ER!n>myw;{PS|zAQ2d?xXasb?N02 zy`Q4&?=pAX@Ph7PrT7n_c!#C9jqmXNdm|A&d8AJz(FO|3I_KU0>G_BT!lY4qq;l^O z?uS_JSE_lRU6it;fMB8TB-m5y+bfZ;gh=Ok#wZh2g-bz1lbcQ~qk5o3E2XXWXcNg-wCr?%j}+c#5U>dN=W+K58d>T2VsvBSnRR!11FxA=>1=Q z)bt`))7rP4CNPqN(#-anB$p_u&tATYCKGC;Y`=#R0C&0m!9^@0{0IG_IsR81sT1+- zhXSu9v;ACS4~a8)$BB@11H_7$%M-2@5ft$SVxNdS$FdY8V@J&jr}HFOksGJBV`Rii&x6|Qzl(ZXZ)?FM(DhPCt2FTxl=j}GSIdWrnYpv zM@b&d(%1=q5Z?SVVN7xeZxXIF!*JV|cAf`%Xgv?Cop>%v#P=5izI3+nfy3Eh@%~#K zss00G$UJT5gOqRD{#UIbpE7OBr0Y9Iv50411PV>}q6LpLptL)}SvN|^DOr`H(a29+UmF~k}eaN$%D5M@@ta*&om*;Ao z7^UuWwb|g-3zR@NJ+pVfjfPooDmPU6A3f{V`rcq=o-F6+ljSG+@2#h?=_H;H5^?^K zl$Okw<`^T*U~{4WYrHlcuHeUsO>eQ_qZND-zHiA{e12=^^+`%(rVzQ$ z63JxMlg=q10=Wh{UHdPN_KEl}1i#$ke|ipygZJMQ-%RYk!-Cg85lgSx^z#L!c&<>~ z$5O22yQH%L6pZs4w_?lDKHyfp(@h}9?ghS2Sn?8(ogmIrR>i{V!845oPf_7#5E8jG zNYf?|99N$#%O+5Rv~#5Yz7%d(Wp=(WJJB+GmhX~IXBC*)6z+YV_!IF91blZ3{u>2< zh`_01Jm=!f^KJfQ1%!qbXy&EPWBDPd%Fk&QZ)B|j9n7xRwRWd@{+yB|Rj5#xh)|dQ z!WisLzDqj$8KEc(%pumM-yg2@^}G3gxo44{r^K6t_<@%AQYHQekU)obQXIk6;j3As zV-$kr$+&p3fNVzCzg0clKr5wpxeyY+=(ueaA~f$rfOEcQ*==7`@y!D#tAy8zgt=D2HGG$J9i)hqZf z37ou`I;7KkobF6Ber#CIN*9(>XdXEUekL`wg38xmSg#IseNltidQY$LT(ZE{l<2`%?!_PZSSknJSfuKH`5p| zljTDEBWcq7a<)l!&Jss@8$fXx2GFxba=?E;7-slI*h zEr&CrM0|so;d>q&|4~Q>Z@_4Ok$deFhA1F0{+i-4G~Ane)mq^>$z{A?i9m)l|Cm+4j;79J_2#_jKft=jmrAAd=&Rgui8%J5iT%Zv{X?t?zJFdS zhC@3ISGI*5jnMQw>0Cui&WuoRSIh%5gb$NgnEMo_`+P%Ws2|{9+I|O-?r8>Km>=M2 z13=08zAE+-5&Jot)$XWbzaJ5^V>LU>r$6Fe1L9--dbnvTuqpLvqife%DYUciVjDw! zxD98R@PL5w)%A3)7r9?6$9kkp1A)+@N*=DHU_%^qK+?H@A2!ek)^9rVVLkkig`~xN z>5&rsj4}*^=lr2Iej0(Jk$7Hs2G0(mR-bZtP|owb=qPrfM0^!#hTjXYNv~OrJT;#? zSM+dWGG;m6TIz`H7JXH6F6T)rF+ER`^NWs1=n>Q>O^T~M!CjMx-zL)JSZNmNN@N^K z0~a$!I!LBqL~_)@XuC_9j7D{27$3hJXfIp$yaidL{O%QgYi$C&yYhRc1mSK+wEC!| zNIg-hJSt zd5qd0vum^Y>@=0=@AKG>9mJ2M1q`%VoF@8OE{svv!6liFp9 zG*ndVq~XnZxD(BRDV<@S$*0rbz;jUlKy<&dCevycODd0C)@T*v0d@7H?X)Tk3 zf74hpb=vjWtXipOWN?s~XsN%b)X($@GeyCN2>6Z`JVU`peUC6$aUrLmb8em3CmKc) z>$a<%LJC+J-Xux~cZYW-d5AqK5#L4RS!&bt15}>XMQofwZE&9w(HDS@vP7h7lFm#b zV#h>-g69g)?JW4gY>lLIu<-mJ_zj_C`TOzTar}4KaT4*vM54OW(U*M6FjFPE<~*+g z_bK>T0S{U55>^G@{~_=}JZj)hETYHQlm{#xyc6lAB!;t{JtVDDob2uFt=y-H_^~2) zOMy}I=PLKT=d#>`cJ`l@$W$Q`wnW}yGbEi-B4Rsxt%9E<;Cox}sS19K@a#AG%nB)u z7jOnBaiw2pBR7j(~Oy)VC9p}N#}Siw2Ol4n=@aXYQV=T_?`qlxHr)gksvE&_KxLI%#mLM zm2%Uy6#R$==kg{jWTDE0kG+jYSv=nB%aE}`gwVpT3dY%_9{;ja)70#jTU^p@( zCfVKU_!%1q=O4~y9pQQXmYzP_7Av7kgwTsKjIqvELe5zrWWDbQ1#b}Wn=JSc1%Hyj zW0|k60Ua~_e#@ST&tC~rX+Napj5P3sk=A9g1SI?G-f5@qox=LOZ@SEo8aud=z4PSG6h2>onyLcwnlo9<@8cV$;dI(Y<6XN8+uMa>RQX*y`onuP{dAEIeH ztd|RD-KQ`MG zLK-8vC0SZ3mS&w!Y(NY4sVp+)*k zk!l&~e>5Oy!p?pIfjH!pN)k)Y?R$s#^iJb{>)CG-as7O9Vw%zW43+7>e!+UH|1DE+ zdes0K_B9s#YgQ)dtS9h(j$(XI_p|uZ6y$sebXbJ>&(ZBc-eA1u=l&ihhaA9b?njf~ zYx1#>Yr{y>Pln#j11b^k7SZ3tCXXa1vsLsj3n6;yjvJi=M0Y-lGidNM$c+C`9!+W* z&(o6)A!VR1i(F(GY~efXPZ9%~DTxSuA}90q>mUTF#3$hRAi2?;Keuf*gJrz5!MF1P zZa_wgameklhUU=@dk7j8S)SRgd^nm$d%it5f!|#6(0}kF2SNNW1ft#;_Fft2t!coYv zIyGyemyW^7L6w6Y+YUZWyoLQ6Kcp?X>}*-x#2x_ogjN5bo#)@#eO3vr5JJyQF-BRU zgx)_D?E4!9Teq}*&EIy>=>b;Rlenf>rC2%Wf$e#gLkHg_okrpyx|5Q$yO^>Shf0&} zccf(d*9x+V!pk}FMEnL(^1t$plDDxVC7o|)un7IFB}1+y^C_ip zt5CSbQkbAh{2mngMQp6M=KJ5lO@_8oRG{vqBb|6#0taIX>Ak%@4jF}px=g1Slm-`;{hqTpYiqGI|C=01i^zkX7i z@CFNhv4US9eEZqUEPAX~ zt~cmjZrV=gyi=v?XeW`zHWaI$q|)@$@02AM8a;bE_mN%AI1AGqQ_%6 zcOmwBmNiN`cl-?a-pyh`r}Zf3!bI5q-0Ne=7X^$PyBfPXjHhwrmuq!p$+c^(YL~t9j%l;$5QSK32!Ms$-Vum_DZK_xz7j zBA*M9*N-=bI!=i^dy?0#l7}YZUkLc!7JL$O$NmR_4`$c?6QsLD#X$uI1!rJ_4@1VHHr8>0)B-B-JG|ImOvH^o`c)v!m(q@5%* zr};|RpDOILEc;vdF6sR91eR7~Uz>uTCg5*PFv{(y$|VW>e-_N30d;g|_7j0pS+IQ1 zUcGL<@1Zh9M5d6H>21El`;?-BzUTiTyGSBlD&Tur@CMa*n5dBAux5X(zt0X=pB+wk z1N&@+@~srUw;p3mw^{lAYBKPR&(d3@kYM0P0mgd|^gjw1#Cua71-M^1d76(RM<1*1 zBL(;a9Nr~5eexvjOY+S)HwuJMBZhV(mcc94J>Qjmp%qYJ1qKwF@gKds-n21a$J{uP z+7A-L&_}dw3N^wHCja<{YbMt0>yTt6{b`%`1%IVyZH;d@sQ zBs|D?R}lS$=M{ub%4P-O*-iI+3Tp*@R|BDd0)yB25DLe;f@hz4leh{rBG1K%tA&t$wD?2 z=nYwDtmK7kj0Zs?{*L&-VMiDEFOzV#%k35yT&PgWlU45(--N`(*4I`0Trgeu5P}%C4Vu#uDxRZ=|&%=E8d@ z6N`p6qr*Ce#X_5rl%0hOEhX@Li7?G>yDoB1Q+A&UyY@U|sd38gnPVu#Y~~T_z+4C; zoGAVi^+wmSoKktxP4~Ouo_;nE^G6QH zjp&>3WoKV{l-r7RCFSbyA`5-JR$t}*CDoe~ z>T-f+B4m}X?dl8I^g%WH?khS5zg0v1=Dyo~inh@=F{o`fPun=ELlT-xFnHjStLaP! zo*eraqq;vC;Fp*scoT}hBNkZV`EqJSfR}eU5 z)^hZqQ!J|wcEl97BdhZDS~~lJexU0n;$ofWb7qlv5aAy6>fSi>sgfl^Y{O^1;#I| zs)&rQEWV()s=RpK*acPN>mn7^WtDZ2n$qHu$WaSS)e+-EM@%UgUs@hn zTvJvuzP1McRp9?c)iqTmMe~d6ipP}%=n_2e_~5C9)2DM`?Bw!DWl`~bs?77$MCwv& z7S69pt*WS9kXltzU8b}mi>s;8!>OdMro5=Co+*2(Nr0MC#j>4VS`&#BLHgSId9^IP zZIK((URF6jU2}ljAw$ss00y6hs)<~fO2pR=p$BS@98pvosjH_)UcNtrkG=pv%|&G; zk)rx)*gqm*R|jTJpEfmAG;`LJSu=}%fmt0hJ^n&R%$zZnn*`4Mn>Wt?zr)8Je&kW( zfTn+p5}YZVf-|b-N6NY7hw`IxQh*#vZ;FZ+L@MjxQs^VT^>wx_1d2;wx=3m8cyWc` z$dSPjGts9dHD%RxWmT0ycFD@Bx?p7_GCwjuSXxyRtXo)C8=N^~1T-kFtQ}Xkq&h+b zrp`KJdeLcxv! zuQd=rWl2?KT}@SannqASyK&X@0In#8Uw_vf1XKLob&eErfr{b_;6H9V43!GvEB`+~ zK43i=9k?;B5IDT3qBd{@{c{xkb9f&8>v5!Ml@}G4BZ8JyR@c{!@P}*iYD>r)MvE5| zm6eWT+m54PI=`%VL2xp{Is2|f$vg1)y!76xahzjv&3y8+v6Ic1ON~W1%jI`RarID)AQNh}Zv6JT&*GA09Dk-Wgu853wU4We26DC~OfjJ2Z?disY zwgv*YDe{a7ityI^2q>kUB7C~`{$oZtfHslvhju>+R~g*-$A=Q}KuEip?D`Ypb~Bgt zZ}w1dZhj=U-Arz2+6UBL_4#jDz(4+7{eOu0slKkPeEhs6b&;atnwsJzH0RL}- zz|at_+<&o@xy&t@yBzG@$zPUUe`vj+S}kOvxvjXYGE&1ObTf41KirO;>`iOAmue!# z<>gf+P`#`iIYnJnkju!vf)zXyj|ebgNpUqgd&aa=i)K!vlUg``S5ZB#AGy@oTd^sa z1&=#ENI0NGB#nOwc`u;=ITm_@`9{hSZh#F(Ev~66Dsyw<2=dwf#mUD94P>CQzM_bn zy|$1p;M)fHrwfSzXcL zRxK2cu!VtU6iRI>ikHlb6d?gGuAEPHm2&MuEL|?3GIc7LAc&W7B4ElCm%Q!O^|cEz zl1doKgU6bB%B-2ETp3yHqYJwdSZ_J<$8 zY5g3=S^!6Py{fdnynIy6X!6n`L^DcOO*fUyv$yJL7#P;i0_ZsQAaX%8!1TpSf@6Xe z#kCiVLK*IR7%muN<)aVvAo6HBt+ISc5DSV3<({>{(weG@AVH?)ykwV|ea>@}ffwj;e zLx7aPU!=0UxMo2_tt@^(-C-P-{DRBsu(q7yu(nOB9wiA4>bRiNXS{_@U<1t6ORbDvX{2`S zpV7L>&~iKg7Ls(dvRS2*7?C= z>ZKs9I?Lia~sX#{>z3jH|!hMWy~Ns;R0o8$;ZW-U_AR$1khI!h`244$Bb;wpjL{&=Zxl z)#b%zd0k3thBPYq>A}L(gC2vVZ68!0q%DKABY*R>rrN{I)-Fg2f1SMtYKPysO?>Ca zcV}+{=5IIIgK7Q05^v{~RTLv}#FXallu{V0U3xeNRJlKb?@KDG-6RYJHwgoj+k%4I z!pcB%+dM!}1UVW1gaCL;VE}mINI@Sl1ENb1A1A^FYL_BeL<6oz1V|b$Ap5p~WK^l4 z(Lmd4?mh|DPcRDTNH@YS|7z+inIF zTg~E`fkDGmTjZj6rkxd`7On5+P?j$SLQy|z7LNu(x@JR{vK+?t(X-rL5w)G^rD^uk zOV@1LdElfINa;&^8VWzeI?%J-JiaWVThFwnDk`J=1dllE6z17Oqxr|T8C$`PJf&6G zu@JyyUu^eir8IVO{kX{d(s8u^YbmQqZCZI^{elC7jBLP>ZWl2MO`<`pcl^y>Y9j(j zx9lcFfOniQfQsiu+^s#WtfjflN>36)_FEH*+OUFAxKRnB?P?PAd7_BlFGv;2InuS z3c7AOMo6NdMVhMeGW-qegH4MV6jMBPX1mrNTUFESddU$Xw}~$@rJ1B zQx-&aF5G7bMsC^qwVpAE&?0wzdC`3Hlz{vyois*LS{`@-SSha?T0qJ(;FC%WzA}11 z+si0`FS-7x{_R$f9o+VTbs{*|uLppGxzlu)F5#3h#zc zgr4VM=E1l(YjN0_Nb{3dc^*p9X8_@(<9QAgkBVG`RR|VgWeXNWc<+!PXn&EPo}_K{ z_~QWW9{Nc1=ou28$_Z=!*V0eh>Y+bCyN5oH2oGt#<@Qw1^8k79$roh5yxu%A;wlfc zZ$Di&)S(nYAZy~jzMA*5GtdkQK;Sh~L6m2tp&TFr%^^fgWJqaUqY zQlhCxpR}zW`UA9k=#vLg*6Xvcl|OCk53_#t=HHP@oF|eA2-;$Up{x=qxNoxYPJi?y z?fW0M*{rSK0SeMePaMf!R2q8y^C4uCRtQ8-dvE!e?2sC1)Z%#p^mYXUW;{Ebh(?DXEEvU!DfJuBr ze?*Rd5$wNf22b#oHSd#QnWEMlgLS%OC1xpIRVoK^%8Qr4=aH!C=(iLw80kJwS};y+ zi}yZ_wNxS6_UHY9@8@eN!%AM6Dee? z-2O8*Et@IZ!=q^R0Q58&;3PV!EVGb2>no|_XMO4X#7t5}a1@a{W#=FbUx7>6Ip`1P zcbCdVI;o-`c>kNENh>WsnPj;z5v)=^_}ztaI+8cT!CU({|!4d-YQ zuT$f*XQJY`lp60-=Lb(bHxe zy=YNQadoxbAj5iakXG=+i5V{dQO($Q=%mK&1A$L*|I~Ni_1%p7`})4?dk)v9aD5lo z!i>JYt+-Aa($}{K4!K=8w6E_-T>pUUEL`sx*4I~u>rt6~eb?cd#Pv~J=WN&4_ZqID z?fd%Zr!nT?n!7_F@c9mXeaGRt`;L8m=ivIz&V7B2xNgIBEv`S_rLV6Q*Sze$zD`_k zz;zq0ALF{;j)A~;xE_b=&N;xxbw6A$!F4RIH{)8nYhT|}xSqW`@NxZk&%VB4I|Tw; z_v-6A7}vfZ_4S>E>;8N9^)1Bpd0dy{y6--HeRtw|Ij*g^{$yX^<9ZdY+i?97*Ic~( zf6ji;AJ=DbEx`4>{h>du&*6Fz>I2Vp;4_c##t+c^+8bp-HnJs;Ou zxDGuS__(ga^*UVd9|?S1{}+329v?-K{*QNc^~`kjTs@OXGD#-MgainLBghpw!+i_N ztso)XM*sm8R}oa!Ydt_wQSn?n5D!+-)z$TO*K=3b+r|4n7gyI?=lgzF&kTtR`{(bU z-zTpZQ}xvQd7gUesi&%|tE-zG9U+f&Q!V6?&g}&Gh+zz=%VZjmo{e+{(yNfJL)xxO zCbJFcDM;@|`XSO+kiOd${zrOIH`pJ=eQ|fxk90WFDM;rdU5j)p(yd7E?}7S}?mPtb zBb|n~yS_#`0WYK_T>J+wc(bqpslvOHQ;<$Vx)x~?uZ?a+ItS?-IaO z?;uUZ3}XXcW*&f)FGbHnItXuOpM>k^Y4ARlInZN*G38y!1H$=^v*<9_i6DA&<1* zQIJRa4AT3Mwwnccq<0|w38_6B@`dO}qyvzCgLD?siAO^o=?zG?Bb9R?k8~;0SCBr3 z^lPNcnlc%;$T0qav zmqQ-u8!eDWI&~%Fkv_5t@<^XN2J%RItcHAvVO+2V@<_+6g*;NJtU|i|RKy?BDVq@gNH0eE3eumE zevR~!-@<=o=trdWNIRSkd8E6Lu0ndw8F*b4>9{j9ncYZ(XJ<0cGCc?KNavi3_)i(e zY3E`5AT2x}@sIRlq-&A>VKeGS8s38VM_OPPw?q~9T(cOk}KIsA1I`X6bxi_!l` z7b0DUwB{0wU(!hLMw&tT3etPFK^|$ZOCeu@^fHV;q_N8}{*hjVbRE*@cE}^0iu7)z zA0mAP>B1`@k96pjkjI)1`|N@|(iKQwK-%SI_z&q-NE214_ZIjM>Gik5 ze@I`t4gN#=>2CNBY02&IAJW5-zJT-!q+cU_6{(9Sen;Jb{zGc+LH{A$iF6gxK6j%3 zkoLO^{f9Jz^jW0c@6KfQA-(+`$X6SNb1&qPcK$Quk+y#T@BvV=Khjf?x;2JjJqmfGUq1$Ur04z>_9NZ&IP6FIAksZZuX+OhLmGJ!{zEz+X|g@W z<5Tb-(npcbK#HkB{|WqeIbxkF%0dBqvp_tc6k%f|zx^e%j~o$qWF$E@8a&?KXbdhr zv~$lIw91noj`S_?VVceVQSU7Ldj;ih1#R%N z@^}LEUiv@qUciGtA6PyOol}6X2mV=W{Uc73<{P>S-G+7G8G$tqhSvTzO!4qEinakC z3VgHK3Lkx%lpD;vo%~|QtwttO1(v)YX6)Co7x-dLpDJg4mM$4%gQ+sR}Ct@W6{yT#Njz@vt5z1sLTjQ)F;@QD|_y|0=hBKKPkk9KtmVUG>T+bs1?gz@&0AC6GKz-N< z>`%b117BXRxn0|?`ycq==zq%+I|fCBXoEkP$F~Ci4mkgBtGs=(hi(Jy-vc~ei1l1+`N{k3djWU^I6u_1Uw(65 z{%hc86dkzFs59X=j8JhV<7*wW@~6CfFW_G$GnsE&*KdB2w|@%o$4hYk(guGykFN!O zW@#q#dK>%&ji*tz75IoU$hWp1^QZiW*NwFCZt#fmOlC|Q9gX=qUIE@2IF_#aeK20b zItIQ2UJ3lP*0}kbynGV(oTmbx(*~cG#~Xk@1bkr|e18nh03N8o+Pe+@RbGA_@OOdV z-Ui>PagL2`z>lrWWG-usBhR+)0rnLz-L@gw>*pjbs__?qe*`?;T6X$=ocGhj>s#ZC z_Tvd$hra{fxlR99=?RPV_X2LV!?!hB%bV|L1;nQSe+Kw1ZSWm=d@b-Js}CGMX~=8^ z-lGQhC9Ugc|F7`oO!|lax$#*YGMPr?^D&Mr>wf_l?_)=`#vp$JEFk_h@E3qT-WoR_ z$lI5|{r*WE4=kUCOfTTS2R^a2Jg@IVv|D+B6c3wV( z{fkYV4?d0u03Tk5^=lh>FOaBz7VxdWSGMuN!u&jP67YAs930;c{E%*$%pcpx-YAdhl^{67ZS*5yNfd`{w1h13zIvCiBlW_5UMZ|9!xF z4a{Vk+Th+mXZ!X7Ukkir8~qh|{XYReaS-m~Tgz|I>a~ATA;dZGQ(EKZx_tcufLntz znN!=~9A#eo&H}y=_?R~M{yF+2;O7HB(E5t@ZwG#7V^MpKp zfByrYJPd1|HtoN1fBS*IHXQpTZSd#wcnbSo7mv(jE^Jf(+4=eh09T_jnW=4X?;=C{ zX8|7sd`lbr%)I^4o#G2YhB5d2gbp{`-KJkIrN+YJ;DX*S{C|1;7s`{}b@1 zfX`?nKQS+#!oHmxgMETFxVO;Y96SK{VBm{d0l@%7b8!1N#`*uXWN-?*Ush?!fmb#4X%F9626-XcGq$^KnoE{DKJwkHg`>p9Oxf zex46}$YBSU-voRE@V8-0ex5R4$@jw!;EfYAnFre7cjfU%fxij-VE41{0Y7|FCNs3P zyf;ksIJa=)z6SV&*0?!5ufGQP)xdw-20tl}4+rkUKIV08aPJ;~{+$o}Y~c5_!FT87 zHvz9c;^1+-1Nb)JAGDF*zhCeu@S`VZGEcR^AI|H454f6=$t-EpzfJl6wQz%Z#MDgY z_%`^8JYEBQ=d?_QAJfhEk6e()hXen1`oZtt<^z9Y#;;=9JUVaRCgAO7W-@cz=%10- zzXSNfBMU5~~-SDShGLp=|tQRjT%KR03jtu@a3Q>j@D@|+2t&zmtue$6vsKhMqJ>9`=1 zIlZ+V)N`yhMaS~9;5lnyCUXXK=KIP#S+|w->;t|JIQMY!IM0ly3Qf#$}wz{Ju6&sCd&Z2)$#vAP@hHNX!xR-Xm_H{b^w ztNVcWTz2re#!X;-4E$hoO+D}nm*W{#Yv0H}YRAxblYsYJk;xdp?#F-a_uX3XL|0+| zwT+H(nt}7hR^Y>q!9IN(yeyCJ0X}N=!RNadfZqfB1?bB6vH4U!-oFOkvgUuR|8Zkg zxc0!;y);zz0)9Afe{1`={{K!_q2q7{czPb2$-L9XhF9}8tONcZ;49kT3-WQg4fwF* zGMV_V{V2TgKtJ(0O^^XxzcpswnYS;AXEvKo$i`vr z9&`Qv_B8;%9{8n{&*A3&dy^T!D}R%nlk)g}{p*0=1N_7`^2g=t-v&H!BI2mEkIirM z_}#$227aJD1h(%L;D0zNlR1z2^Y>1_&C7oWymI})?_H93=Cl&{$ky_$+YkIA;1{&U z&Ha1GGk^z9&VIBdU;qC0tpmOg_+HBA{j-0MWE=2y8?yU#dHmYE{dWW33H;nP^8448 zuK+LFh-Y5(UtT_wm;Vm<55OO4BY$5WPa=RW$FnW|H}ZLT*)cx`8-U+%Y9>?B1~19u zGk{-r8paUuT>ItZJiZS2+nX|(v)bSr^Y}L4V^2ryw!ypS@who+ z`$ui$LwVmON^#$MekRkc4c_oT)msieE}jK`)BnKt0q=3a!N-k@XRTKPKiIgb2Y%{>napEt z?At$XCIRob_29l=1-$P?*mrE>+u!7UyBYXp7iaH9a&g&e-0TMa4?LSq^SY47%k)Ko z{(F}ArJ2l+)St({$?M+-{FciPyzfYP7e9lyXEKiEc8}fPe*sBiG|G<_4J81s{e;W9{*81g#`TCy)z8%k!+w6U_ z{xiM$0UKZAC-(aT?>`bxlw*Gs_yFYdpyrN z0ME+SJm$i@AJ+koTzBASF0^4A@ZrFpXd}P>8NuDaKL>tE8~nVy{#Ss{zy82+N|FEj z`#%r-p9lWW1OMlN|MS5ApFOY}`$7C$l})*<;NKTsVqoi7|LOY(c3olzhsv@i?jThES>Y~zAW94#lO$exprpN{ePBxm+gmNS6H9z z-^Oe@DVsKB(^c7YV>aEKO|Q!&D zVg1szQ=5-nvG&+>kDlFncB}8&=U7c1a`GX)y4CmUlr1*8K*H-@uaoY{hO~7M-+KHn z;^C;r|L1Jqm*!%~vmpQL@sF#?@xR>8gFelVi9D;$c>Gsx%<(_m-NX0gVzMoJ*8Z8B za{RaV^cZqOf6MagrT?#)L3{SNr|0x9>g_Rg`Je6GgB=#nKNmK7{CDKW zoJZz=J^hbv&GC;M=RtqS%?WulozX8e@0fXsUu-Gt#&8;}o49sO> z1!CYdg&)0PNRBnxp?J1t@J*%Kr&-jwXc>~I4Zh2fQJ1lhqsB@WoWT6wm@iG7!{i$N z(1pI*I+`I;(9A)P#~E~a+K@+p@b!|9 zGpTP_Onuu&Vp~V^8sY|&_t(6(Xy|7c19iD8ZbhAgba_(1@kXOAPYX`dLv(qqA-N~# z8zxz!uVM67>i!&kIL2i)hFFWAu>ls>!$am>bjN-8@r{#fDUzQ2CPk)0WJ)V@re@7~ z1DA~Ht<0I>nPbR))G-qeNszd+gP_D+1$Z)-)iD7Db37e}>`2}w`Kjie-k2a z-hUy#FvrU+NZ%q^Oc8h4yd3ZHR=h1a-rFGPTOo&m3DLp>mF^7_4a0w%89obT&Nkpi zY&d=bBf>Yagqw8U4`V^+4F6L=7T0CxL&g>V#P*L+Y}1vC2QzDKSsXYSz|Y7l`QY1{ z3qe8?gV$t%&=6!w4fQz+{XLPNeJK?1Z@=C~j2NwVb{(@hy;G4_^6I=^x=HILp!K?j z8iBlj4v@)#|I)h+nASVFp4ptTSyFWf=LlL&cyM5i|Td%uAr67bb%^K2L3kLAA{1bc{h>$;P}6p5xTE z5zw~940R~-{@+1y=c^E4{L@Fpk&5l-0MoX0?!atLujk8jUN47&zX}p%xyk1P>cOAb z{`Z`UvRYGSyb4YN#_CE09455@h|YRa`z>7SdF3xOGZ)S^nGh5V~*`nUe7HvDTHB*P_&1{93U}bG;35s&F94{1dJZ3@JrKB73 zU2N+9+V5_WPC-ZUq_>u*T?cfV6D27g7Nn#5NxG>QZqpvh^1@ckL8ZsVz z!%F;*hTEZ;;&LuI#7($R3PXMmb^a%$0^qm&PpJ{eD16_`@IM_VH`L8SH~OEE-9TBa zFlt*LA<;SmuZ538x%exGqbR~c6FMJ8*ahB@vjO<6z}t$~e62I^PMqA(`5al^m8XKz zdhA`{`;ixKMXBd7zt+>A%h`50f*`)KleDss}A5WZ%5>)2|1QgaD_TdMu7wgYm3zE@M7e}1@NvRE~K}F zi<#UMonWIBuOe@WH=)R9$P=*ARtqHO0Kcskt8K`b4l7yWRbmFwV6{{emLadls8!44 z+I(dzB9E}Lr*!kfY`!=H-DDc_H5#`@?f{&y)G5I)k+DBOuc=eRIzJ0C>a?Iqhq&w? zfvO4^z0#1Mvf53)zkAZB2fKr|XQP|c8J_fo%%AB=%k!xAEMFK*XvR6g*_1xEhoR0* zP$}|<80x$Pm7o#4-BRcK`h$XHJA%JM#%_V_>N-#N9k~Be*JpKaAaH|k37Gtr+8MkV z8Fj3oZc0!N#x7*RF5e|c61KV}_!=_yh8ZZl)ukc!2h)+iEkQv;{*E5s?YkR9c_l7x5iOC#H;%9WF5FOzOQ+&zJHRLW7 zsfT0&Oi=$^@Hnd1?fHk-9@9^b=QUxewR6#u7a|Nf8>=GL_M&eDWD+6uUT`-u_LnsH zeXk{-Ag0s@2}X&1{1oIrEM^6U+>`+Ek?&Lxeph`Gd;=M;Ri7qlux{071yqPu9Y^lZ zefN<&slHad88Oe&uK#4C?j>6CjTd$11FZF1P1xpZ7}V-JO@z!hNPHirylYr&g{+~P zALJ*{-7cwY5j+)OHQs$Ul+cD(^SyLO#OU~18J z$U0rfV?C~gLh-P?$F(TWH3*jywOGi@fmQOlrH&OJATM}g+7P`Ft3njPZc{vm_DRF( z&nQ0;-AKJ@wO*)R2=%~7&QB+2Lq_|5g9tIk?ZDe;m)ht-uI`OfJSb^62|D^zA-{sQ zc8%&h5nKapMK&kO^F^F4DU$TTW>Lg?imsuTw}?Wz$8f%-F&7Bm5J2J4YO4rdM`Lur zUnGjy5*_dtiz4bp=Y2qlON4JDNUy;+i06u@*)6@hWG(pAPnbNB#xBwIv`=o2}t!Gaxs2y z&aq{T0$|_*-w8_j6i7X}2X}-Ws1dtRlOgzCJJuwHh`r#AlC!Yi3hq)}X*%5&yg5#8 z1k&%wa*JeWVadleqcsBguGS+!{cfm2t)vh>Yhr{d^*xQD@EJEFlnxyZW=#C6(cn;( z90xXSNmzw%M_%xZo*}MgtHhnS^qZm?eJ;ggh)_#hj)?M!Ua;10NQHh5$K;6+OSs`8 z>2iUy7qJ&Ec67c4y28ou3fKa;04P803Np*>vY`0+}5gGe9G$K-*()k;) zVu&QY{NAz1mz2B)p5o>B|7Rkl#cv{SSTx%j%Ad*j@w*X35ge&o5=o?Jh*5SqetLj#`ita-&(HUQp{M zN@xg#Dk0<+$sy!-V?lKh`JZ539Z33j7s>0ja6w2VV2{}XYYJjn3on543yQMB7m+9~ zCKoR6tVuoq9~6|xcd5ZmIBENAnL-_) zsW8&2p@q@J0H}pyh9M*iWAaF_=|Ns}wYUv=k%nqRwAaJo-v}X7#4*QVBAzblmVn8hE^e<* zM}{{S#U1p;&x^R?juC3H)tcWJ#htYuT=R??NY<%4p*U&C8_})BUF2A>>57wG!!IH) zenY#&QSgQk6A?tFxD+K)NaQW?DI&xtx?n8%#Zs6R5F22YomFvkSWj%l=vqUh;TR#V z)hd>vGAWwiR!i_54WHl%WxtpSbpg?prg|z$>cUewH-3eN8)5?5D#UaQP*aqmM2a)O zYKhNLr%(JHcKO9g;0a`f$A-62c%v4+MGH?v&8B!0wMp?gel2m2uK#JEe(^OrF(7)t zMjJO5rDf_ix_SVHNomUY6Y@A>#KiXvvlda=7qL}ZE?JX5T~?-ecc|7HWvN1T1sJYH zL0P%U+a~SVR81tt$!E0g(%ITALHwHH7*vlgWgMsKcWQnK4V$ z&Gu+uMK31>?u}+O+F8*Ms{(PR$=9bV4vqBy@u1;G!A)^}s~<32wu2gSG;I%owfji6 z%}7z9Zl})yVpzo7>Bv%_7V;Y|8!;qDBg_ZEK^6VvX(;yBRt!+vfSG*Nr(#f)1{mft zlvE5(uy)%Vh)$?zjIwIie3!_u7^_I?)m_E#81<(O`CGPrq?`Z_e{E%*`WMx5%f7Np z_5#K|`^s+K!pgiF6;)<;?M?35SN70IlIv8XvdzAIWiPn{+JaQ+udi%SGdL}B`@XWD z7Q%IoLfH*`lN)_sV zb)KO%5ic&`O}q?)j!D3J^ackt!>Kg;+x#Xubkf6rZ)Tdl`|zz z;RmV3?^e!M0ge)m!OEkv>bRd@*=9q(vPr({@y}O%$^SAfT#)7e7l}5T`jv}h0ob5_ znOc*+tO?tE0==K^q6yc$2Odgy z(?rq?V&J6fHIX*&M!2UB(L^l#kxAZD1SV8~TXi_q*Q^D-1w z&D7WK&E~1SLClJufU<1})v9*tGS(4=C#%|*@SMA0KEMh)CY}Rfn=i4FPRW0Pa7`Nz z7pv-$64FWYG^|Ogy0$9@kv5W?9&ZJcPygIOMb^8h-}({SELM_aP*JJJawj+p>(2z( z-Jj$E<_wD=W4%O_oy!`vm|=a*GIk&<&X(E8%C2y4Rc}e3`O{SmY5@ur|KEeE{xL2h z;Jo=p)u6~)S&5w73awQmWi9fl&G_;0 z2WwB*Vf~hcbi6L~re2F)k3WW3X%}+8WaaX$w~^p4-h}C+@YrRqJD0wJ4w>l(O1M=T3Lh1%&Og;t_kqBLYz2X(~ z3{hBkJb=3mISyr_RGtQ^oKM8sxdt`W)J5)u;0L)!c3~LqCm%LF3s6a#Ig!p4vhhUB@xf8+F!7k==gMozhTiJ6sm}*Cak7lv9?hkitV>StT(0~@sE05P|YnyWKE0lLLtlX(5jTrZ@k!f z_B~4C&tjaUC!ifq>uBGYRSHMxKe0W1F1qtEhvV#+EXV~L{{S8a$@?|f{NLbUF#b`i zJ^a7a_W2jQAa3^mnAWQF6EaWdvAA#{hTWEHm^WI; zpvRzV1gWE<9y+eB;5@W7Tgf7?E*SpLTqRc@!PziZ$)~!Kjk!wjpT-zBr1hm}OOsa% zIdm;mMvzJ>zJsfdOK=7}1&{vtr$2p!RBWGxlNGut#|`po8LgY*4fRR6TF{gevrU;4 zFTmwUY%HQab4ybMcX+3|L!b;68Wkb$+bB8|IEybuXrx%7pH!?W#h>a?v4)Bd!us8)9d2} z?4LndhrYEw&Ko;1G{K2VZh7$J_&8+5@T~jcaJYX=mV4j&PW6}u_?P#=Sg0GmGuw`= zTXViX6TbF*Kzp?hN{k?zSaBSDP+iE?MDCIHXP#on(xci3)x$g=Y~G-wuzDWX8*`+jtx4(x)ifE1mFhGB#LX3%8a1y!-=yH zv3Tk}6!BN9pSqV|VuE*!Umr@l1{baZ?+JQ=_XNGbdxBozJ#sgX8H2&rzL2s7-a~bh zH;`tB;%+SR9;%yssBYG#dJ}OlKFv;vGm-D)J+O~4Z$8bR&P4;5@L{{zrQ~Mh3%qCk z=H%`u>RL7oMV)QmUx;|m-($?1YhZ^=#>XeRg&NZBgWli^KaxRO>8AN6%|@#i948QDEM9~X63Mlrwa;9Y?!ZxsPi;k0M-34y}FI_<# ztqEH+_5v|S6Rx-q?h7<&A}J2<3}UV((xMb?4>W6{R@8I=F;5fqqLSL?YobBSJru-3 zU1y`1+z4WkCWeb>J%}Zm7%d__K`hn8c!B?8(+DgNT>v{LiSrR0ffh~77thn`6}n;T zgxwCrN==+3_*gA)j3zdU^;IBNYhsgFPMI~DI8)r(55!tcoFmF`T?rhkiOr%T#$@0) zO>7m1jRtYNCbk8d*Q_=ICum~3_%|XcfbUtsnOBRQY{H3}xK`ZDmaf;2Id_Oj)ONBa zt`o1(oDG_|Ud$k}Q4=?acZi&#i5ml}7Nee1oqwQjcNzuW@sA?*;k9stjxh_tKNu35 zOqX0=D=V12FKr;3v@OB4SWxj%4?oBX30>5p$^u zAXvPJxy;jGnCH+-mrEW(a-hu!JR0r-E~(F-NbmfK$@3?HCu9Kt=y^&_WYFN zZdAqdCxK^V16ka_Yia{o_!K7aPp2L&Kmg$>Oki(_fbDG{n#oQme_b9$q1wRD>R}3T z1`qraV{{`L@OUebiM_`b+I+edFhnug(uR+%;b8~tE(y6AM9>W+h58&h`|T+xE)nrf zAi%Y46v$E`U+}oncp`^c&Eu-dbN$!@Td`N@H{2SCx&;~SEtMa2E;?O}lNXa^ zxSFnRQQ1+aW_S)29Cd1@CM>aM5QrnQ{lfvEyulgL13-C$(-mi6peb)~CdB;+3*`;Y zr09VQqw)r4N{_^*UtnfRgijFCDVV z8=MU~Fx4WB4A6n87VE9mMje>hb7?~i^UhJIXsNu1?N6%Z>SMHD&#uZFzdAhCiVBu{ z5vf+@B9gu`%~~i}Bkw^TKHi{WPR_?0b?soeqa#>Okw1V%N2J;n=FtJkCl5H>=!bw| z^0c4YEz1Ew&+Td^GCCsF9R=iu9-j77d*o2ExauKw7FigP>fu%qsUFEiWOU3r19N=4F~Xfea;(QPUTw;=OvthDOrvv{^e#&M5V9M(JWK znNlBTl=?WM^o}!{JkIDWkejLBb{4BIso%k+%UR;-7eUMo&QeWaIl<$M&NBH-zOogO z6qfu_A7_;MIHUBAGnzck=&X@HgEeY7rv%4>&(GtG&Z%LY=W#~oG<}%RDEbEOJrW#x z-6FB4%xX9JYEcB~(}QPHn#UQPGdyV?XLQc=q)i@Ybk6cE1QVKZPVg>d0yxg-oSUFh z7mpT`-U>pa~&{^wku)y?CK&JDhYDQh`9gFb+P zV-4q~1m)mY9%poR`Swwr?c5UV2?jrpGdj1rG{n#2jLvNd3K}MlGdjC{F%VYBxg$6i z8NuU>&YmK=QtQ4mMkO$n#~Gcwe1k!GoqKQi0_3GW&M5V9MyZc8O7A$M$>WU9L$Zb9 zA?LXupXUnQo_~1l5j@W5Jg*5Wz~hY03lYvLnAdrn5yy6}giJK#ycgsV6+aKsIPZHc z;c-Uig9M|*&*O~FhsCS_yZ#9fANl^tYF+1(U|&djt@<=cgLSJuE1*KOipLq9&wYiY zlFrxaJaRuvyZ)1nIv!_qzVV_?@HnIMttM>2HqX&GIMK*aW`yBCoM>bvHfC4cFFQEV_$ehC9h_)95fWi+qB}UzNc*tB z;^@MOMpBl|6O9f|G(JSg=7~lJCmM;lHcvD!HGssJ5Mw^IMJxJ8$KGUc%sq4iN-Ujw9&zd zM&5;oaiY<|iNLMA%=m9`#mG>Kg#k{siSRHGQXgrQ z`beWbcV&1VPTNF(Fiwt1w{!I4HnuI>&T zX(W_16pu7IIMO%?I#P`ejx;`tT%1Q59UN(-PvSh%=-@~r>xuJ7qk|)j^bgJq(-<6S zWaI@$J2=wF%?>Z(aiozg(Gia$jns?Y)OM;c!T86&gT>>EV57YL~jHcEZ4QF;d( z6%RH#IM|o~j{e5M#^aC+^G?vg!A6Q1HV-yBIM~QGS~d?hIyl()5Fwl?H>!R|sJB@)_D7v}MG3FNumfZD zJyA>mcbL2+tG|{)?0q5cV||MqoT046yrb8<4$e^Wn(VE29h{-mF{tk{9h{*gW0T=y zSSc=T9Gs#2ifSe~I72x%uLfr*88cZmI76xZ&a>r^!5PYa;Ea(SX8TRWBTRbL;RMXY zZO{kxJ+VY}Y*ViBxFV*yJFa9n{KzNB z%UCVUm43vDke`h~-SWDgD8OZ#0vE~0zyvcdQ9Q2Vnd#laf%Uv`V7XL&Ll!r9xf%!- zy}k}^&)T_g`C21*MUntwi_I`q9fYowB`AX4Yt@3h-rwiCV;ZhE!5#82AW=7Xle`j{ z$m-Qba9500L{_d@!n3VInP0PZr4hU(PA#~!25(gNA}4so3hoTCg@$_!O5pcf*r40A zvZxijOMZ-E|FUuv{!y$xnku=5btTi5>L~a?`y&q$FQXzXd>h_SNxG$KhE0z zG6r<;6Ftb1hS}5u)Th#qdQjo#YHz;6FIrdluhtdrYhB@&ttU zCW3v0dJ};52fkXEM%y5)lfH3$Sa`4^U`1u3E^re z`@?WUrr{qUFG*`JUD_AS!!d1w8SNh>)C6#Pc8(Syig}id&DQPJhVPAXM8o*8;0R8D zGng(8P88~Se4 zLLLcT^!;L?>@rxZFHlRos=&3bBhaNn{tH~5qkb{N-N7jJYYx(T%|RWD)fwJjkcB{? z46iv%MJ~+O9D;bwfdWv**BpXi8$d2MSR&O~+%@HPd2n%_YdiMOf=kd^V3rjcqZ*OXm&{Op zr7;lGYiOM8M3zu!hB})pygvwe8*g~b3eSeTjW;WB%)%u`XqMh+wZ-rH1DUM}Wtfj( zz=n>NS}XYwW2C-IgW`jc8j7WQxG-d4L?q5E>XTaRK`hyE;wk-s*ySM4A_`!@7?j9&G2 zw?c2ko(5qH?rw$NEGAdl@Ub1{YJ_pqYd7IDOyxOg|>S$enW=20TTn>7@-QwaU-e^7L;1bjYU1FDT6Q*7ik0i z)vuzdZYe$FG*+)_2cm+6f2?6vBUar?`5iQA(_`VtA}Eu;45aCIV#y-p_sE-a7h)2N zjmwb45^^Y*qE@WTcMUTB^I&i+RlrHz-`D~9axZ@t%PVvqeH|-SPvs1aC0jKwR$9bL zZFdcR(Y#fN?O2thYHd_J6!`{uS%pR!(g7;ul>=ZDp2=hWh{xp1fTM|cv9Bf#6AHi= zPnOV^{vGgsyd=enF&g8AY6v*=Dl}eHMy-Zxq6EG8F+w1olGI>11rc>J8QB#D@vNbwHpOccqp!5Xy^<-Q-0@oz+%5*2aJn}dDO`4h=grStG?qC~Z46|^={ z+RC$uQ~}Mf-G}fC&;A`PNVJ!%#`lZ?6WqcIbtFXf%|T(M9_8NnFHGz4kGAmzv%)Ib z2W&C&nhOyYMXKHL$hVt{e7d+~2l9gNI2EU}##H?dqFJfmKMn;kn!_%xzb`s=lQx^X)^o?D))E6^{%ue#3z4#zAjf< z8mANed|j@z%**q2xzd!*;|Pd;U9O#eU9L2-54v@@mKK)KPMnsR=OhOo(RqOn5G#vEzNi9^|r|KRSvgqKpq zoqFB*_z0?a4S7@chdL?GLG&PK&p8xgDLELdUd@%^izqe}ZZ_mIxS7L?xu^loKD!rK zYvgf&qgGib`3N%pYjDjj>zt;w{t>XMtgeRD;?AzDgL(@&u?^8(R-2?&!@UN-sI{aw z%DQO{Rx;(Na)gw)s0@qMXbFXIWq_%vL@5DVj7KG@l2|(sxT?}~K1PIj3g+rmnfweA z+W6vdm?0`pYJ>_|1cwQEFJ=N$mO{6b%MmW9{bBGF7s;MTqDj1-wF8+5Ul1&=DSd|B z$rlHU+ou?){^QW)#U0Anb$-5FSlqF)Blrz#MtMZlL!lUi#*`Q8=e&m4jWJ$c667Rq zi@Sz_DAz%r6pb*Xyh5+g)8czv=*rt^q*lC3qFN*Mq8sM-@-AU&ZV)eHXqETSNTc9P zSl&0$8^~xe7c)=!fK)#alMEk^&-ge+`i7*@T%PYMAEeu5xfMaRl4f>?nH3?uC2#0j zD#B6Lg7BM-OL;|9jss=6l|gkC`RhC4K!0og$~^za@Ox!co=ERLoBPm(>Gt|^Wm#20 zRmX~`t}5y^#4wxD8C5Zv0Ae+^3#;v9*@oz87uS=!A^tQRL~)dWE&f#tB2~y4*416o zt}1H_yQE$F5YK~nUDB?DM!YU*SErrTfRi0yFyDoT+jW(FS!-=|M199vr@}BC64tjv z;y>t!YJ6k`iKXqSF4rcw+Meomnn-GUsyh@>K5h7DmiZMLU0o}0L6MH?nu731^o6_& z9x>!cLs64F4MX3QeKB~YdJ33c}GDHSOeXWFm`J zG#fS5?qGP?KNAHtH3^1?|G`G&+ZVIz{d+0cp}>X({xbA+O~+z--YE!;5ykRxfq}4M}rYE>G>~tx+ZWQuv$9O;Fb!8okQqLl9xO?%7t5Ujyqm~im^{Yc9d;M}p zf+%n~SKDF~BBnz!L9V3Xiy$$t2GF5YQmf_G+Uja*;On!sPWBSTGpx12G6J}+twLfp zVY;gg%af4T{nSxKXo{@CK*y6y%$7o)jHwAz=WdMSj#2psz)`znQvQOBo^d*s6f>`9 zoQ|bBZ}_f5iRE@uio097A)Rs!q3xXtwC(V075bx7p_E{=+|Ei(C;#kzIDr_-n%zVq zTucCFQ=j=Rriso`*^_y`n_U;EUW>ebH{0a9*>!fi-?D)3X6uI4IbmK!oSD-oq(4AeENa@R&`w$?spD$sWT7$8JS`ca6$*;9-L+bthrCtWO({PDNPK~7 zTenETODHq~t5z;Ix)oF?1VGv_uS4*6E0ph$-|encAM#&?9_^kRIM)}h8;$*hJGNtBe53mWQ?nAc)L?^PxTAa5mm2i0xVz&nxNQ7vSM*GRYt z0=8~???T;lXjn}RTd`K^T`W(g2D^`~UZ#f2(dB)@VP1Q{G766A6O|8=WpUqtNKk(DIYTUm&3zNvOHE=5T+p{vyI{UyHiNluO1?{Bw;`a0Q~2CcEEBT&wWfx+HVVOB z2kC}{OoPpG4|UX9@-t`;&8^iLw1-A@(1L&ZI8ZS;iTtbj1=SzO&o1ugW{sn8ze4Sf zCLQtpinQ=z!#o+z>6ernb!GiS>TL?IY{XX%bM9MB!gUECRH@v&1jDX>T>cq(%N-C^ zMHqVewr7Cr-T7AcLx&ECaY<3rXqfyk|9}GdZ}Nu*M%Bn1|G?axaxX;Ez_@lFTJ$oy zd0;}e1DhW1gTj$xke5fIQHDGNF(YJuj8zQxBS+#hb_MbXz)^QlN?wJG{~}zY29-x> zmY@53gDPUI5&<%(Slx%5o|FcaYORL59wn&t4RTg#4VE?7uihjh_j?8hyy!Q~^e|Ai zd>n{X-{`1LxMGNZ!a>dD8!B* z8p(G26h!gR0&Pu`wsUAw+lh`J562BHl_yZRc34n-NMSZ*Sgci3h81O-LhFZ>Y3tL5 z*&BWsRxa9f8FFbuw~C^4p&vM@c3WWoL1Ilw?A2F}dIv)@1UdBopwm35qQDFvLGK@S`M$ z$5F=({&YLuIVm@N{QId%?(xCOL}x8{z@X zWs@S_U4MXYw@xb1gewMO)|!;mL|R;l_@7iBq)5HtyBR~>tcSZNRmz=Imp&Ym0Xkp! z#^V1VbF)`V>OQcB>NBx8hEt%3<+mxKP+c8m1$!WWjL}?KvpzRK7>(C!XDfs}kO_~}~mvS}z zEr#CITu1&u!Yw9Xi!r$End%KWSBxi7SWM%R;xZ_j>Q$5$SCA+zxe_w9VjcWGwL&+j zUYtmxQa7kUEKh@|)<~mRNuow0qYWQB2VYu(g=bfyyn_rQZ#7I)stVo|HE`0j)~=sc zlr>WmpptSZBQckC@)yx}mjEUT$6 zTs5;wH!3N(5j?YFw(YEAW}R+fTGuhNOPtDT#mDHxnVuu+MO77u1})P|)S}~O4$?$} z;bR-j8#WR_QMNjRLcC5N zrL=Oe?8B6Llp|M>rFK?OJ&Oz-HY+#P(_yo6T|5R3m{qD9lN6Ikl;s@uc|TC)@<3gYijMsrd);Z z)&R*>7=$02aur5MR25Rb*6`78^Ir5(Q?;x?-g4(UY6o@i!RVix^S~-xV&+B)3BUtu zDRe!4=DKnt61~K1{wT~>MWjUd8ge%RQpgotmdJ}xqm&6u1K5EagKI+bK6xitqv_@! zWCyVO`H`yTpHj0B%zl15rupYoe}t`n6!X8N?q~k?M&vW8Jy`r8B%A-OCP7l)H8gt% zSq;GpMDzD4nqj$oK!Hc#b1?Fo@qCT-#3-U4CO1b#_)HKoieYF-6W$VXZ<^NDA{?Zl z-oZd`#;X*~;7ToS#wmoqA{W<|mWc}9;>5M7DN(_jnYgyKTvYJp#0aix#w$-BK*H8W zm53CpGu(?E`}IZh;2R!~vp$cRiko14YFLc@qbTiWYQBT>EpdsNkM>{12$F zxlvSb$r!(<5yTKtF$JBDGa$`4Ke`)=y_WP7DcWhc{Pqi4vKHg58D|t&bsO&EI3Pm0o4KsDMwO#=0IQTjTl z(Kg>VZ+<}}-vKqrK8cNgRg``RYKGzaJwnho4FkeApOelF(Bs>Q=v2eUf$yI}t_5H4 z>Soha-EdE)j~FydvykEA9WiLO%tCg-Dt!vF*=H7zaT{KIMWOl|JI`;*@2RG#xr3=@ z0=1`9qqw74^cjl5b@4EewWe$Y2Ci)^P}o}WCCj@Pr?71 ztsggn7;emuWe@I{MbVX?Xdog(gqvW3ZF=Dqvvg(XRzs<6cw=%0n9 z$!kEkA_})IEGy-?kYwPPkz zO*D$XqJI`vX=1b}B3rv+Zi`G3BO5_fYhp%V&D06T!kQA=hFeXV)4r5!i^Y2+I%u|4 zViEdrVMk4@6Gx>%)M}ZHBEd>Km0k}8X9il9tS}aK*2v~S%iMLw!a7ZC3(P%kk+HB# zDeJjfG?zoBYXR%IQ9K3zE$pU=-NKKdu&{dpeQ>woV^{e2Oy0+C^wH_&iS%&2oQk~e zj74GRB`B5Oa*CE}6g%Sy1fPkGg+&qHx4`@yV2h$fym^mcIkCu%^PvLnm=@tnjKK81 z!lH1)AHi(6REPVqotP6B6-X+uY8S`U;bc4?!)S3qRBvF4OA&I5z1zm5_!7~%xTug^ zX~VY*QsyP_;NqlAqez>&Bx0X}yc`7o8S;e+n)*DN15@oKQTcmd{)aLAmbe9Qx8eUg z8nYy(pMD~kmxR@W;LuO%mPGnDBX79W2U`z=SxXAE2CH^Sk@}g8Hy{F*6la&37AAou z$wF$dMFf|;B_*1`J%oHG$5;fNrjr*2w`rZF3CR977j+qlC(>}7Jp-1-lH^E zG^3qMsx^`n?^DKGNTkIIG=513jo^w;gUp}M;w80GL5($fX+&Mjis;d$(U|AaSD*k);hvEZ zut~m%>Bx}(!E7UBXgGzR$FG$4!U2|C26aApH1dAA0{xE1Utcyt(I9CCt6PS9d5-0l5h3oe`JbfV$OJdv{CA^8Eu*~rV`=0^dwGt9mNDTy&}sNS z;SUc5SZ+(BdI@EE1h)(+q~(TtIDRoI9>Uma87J=n6}4Js_;@rXGHU1%M$61XI?-R( z4h%zb$Yn0Q*IsvBU9xCB?^mgz-|8N+=C)mr+oe9NLZVO#XVfM{8gycq=c zT-d2THrM3lNy{?H7U{TK5qI_?FE2(H;ev?}7cvHSV`_PDMS<@ZV1CX}D-xxgn*0Xp zT~S!_2b3Guvnyh%7J_0sOXFSs%u<*7Z1DMad#vA3U;*|uvQNAjnANqQud{t3#26~c)?<6po7a@vPCFPk~-7&8Ek?PpoV`7E6 zxw=Wm6zC>ll6en4J*H5;O#a5zu4=%@6>P=oT=i_l>i@^tdw|JN9Dlet|Wv%_II2`SyO=*87Qxy;YJ}W${Prg=@6Gi72Zsgk%nAcnc=I|-fz%3@g|ux z`YRq82xKn}Wi_v!Al?BQh4A0tt1!(;8T~e+vsgikd{d z=Qtn4XT;M{c#QWX3Y1^Ac!ED2;n>0GW{W4bP`ky>1C+&+TBrhJM=*YJ%QL8NtegJM zmWe1BJBj`&Ev&K=y<+j6$@zdHopJGAwWI}Nix_mqZy~UFACFk_%oJ^3l1VSU-FU@(+Afdc{X|)#v@Yr5(me#b84oLLU%4B~;2>nZ|iiF|h zw!;Pe2Ls2_1}P;tmgTZ!mbU|)emSz|_CZu(aX{7uCm$Nd`ITv2JdO#?LDiYo0k zPGl_x=@r%9T4XEFt!VOp4NvTQ=pZYat2j}N6`8%IKsaE{iiYGzgh)g0ipBz|aB^GW zf`_#br4{`>LWs^-S)S}htB4v~nG310m4%QRTUlY$n1^}IN~N&fy&j5irBc}8u7HHE ztdTO~+`j|u%GxGE?sR-o5}_?@WrOz>v9_O+Px6c%;rlttUB@Ngqc=R4@sHM`K{U$ky-)RVnOY>b@fcZ<_qCPK19{eZSejLzclWuZ1ylDdxyz1 zW7c4R@OT+`#5)36&;^E~MOGEP(a2QZzN*&mfk#x#s=5{uA}VH8eaqv>E-GeKL(3rK zL04Q=mE;Ew(iKxnaf7U9Sbe8IxjW= zykHJ3u)5IlG*Y1NFGe3-UEw{Xg&UJ?vyq<*gw;*Pnt&C;>Spuq5C^T*Ei!1K?1N1R zwR-S4}C$C`H0ciHsL+Yrt9k+vshZ@A|iFlZ(!5a5#bcxj?L^@jB^UxTpN4Bzj zyIVo1qpGNFI$SPXSC5wZ?v58okRh@=5?1f#?FN6eYxUIRr^G50?^7#V2JUjOe)V)C zdFL>&??C42bkpe8t7l5e47Z$tSxsLeWuD`cYyQ>nM{bAMt)AoE0)I5RCYEfS0|eZe zT@zSP=c4J?B)l@DL>H`0Cy#+gHb>WH@>H4n=K9)fo{iQaZ?3N`uV$`pY=+}6^6gH? zS)22Q6GGS8_T(mb+-Ja{wSybJ24-aWYA=L_cxP#r;mKQ=g&PWMN7yXALlGM3U5*qH z!F9tkU%>Cl!R($K%r4&-;={-cqOk!8beA~!gR_Kj&W4(KcapAi0 z$@aOZ$th=^;H=xTFdvbQd;S3Am{7xn1&+^e!6HA0Ost#eMG+Oi~+SmfP2MZm_)?+gJBd=FoiA-Kg z8pS+0tlpEu>ODEE-s?rY@5y2Ho*Y*14bKX=*=W{v3%oQyly|Q?-Fp%qt=}27JdsK3 zcV_MUpnhl7@t6*+-@>rPF+4m?`F z(`re-lRFYF>X%{tmI@tOzjHGq_eae(_hbD&<^;j}7?>j777PuZw+M0(^}YaL-&+Lj z7W3|c;KjYaK~DhO9_TRZ&i6h5aMbUc-VXsw>vwSz&yv#mUDCu8ZnXU_ZLUHyYW+61 z@Pl5d-(`*b;zrxAr-@%`X#Fm4V)u~xeXEf>W~|?Z$;W_4>vvHl>34E_!39BoAVJ>| zI;4K*4b3Fyp(1iqfHxPYUGFe37%%1mwdcuA0p5pk)=zE<@bsntEj%kThlS;)0Pl7w zEH?#sa#MgOHwAcdQ-CKo1$c5(fTuSF2#EEgG8Ynr9MbQ}A^n~l((lP3{hl1s@5v$k zo*dHe=^_0Q9@4*lFOT@kYt~QpK88m)yS}r?gT{oj>!%cX_>pjS{oa*4c1Ae6ergpz zK__R|?^D4~vV^nirxp3_4!OC0dJT7HF>I`#n9LqvxVe5(HL1luIb3j)r}VF%A-JRM z8-^xNWJaEKzG0YjV@K{M-7rGm@preN=!TJEKhYaXH;j@Lc`w!3Fj{RcPUJx5+uf@J zv}nhMamim2ERQ+gu&3n5jtz6{CHeK9(hU>bi=`6M28qeJcxS3VPQmFipYuZ4$=*Wd#@2vZo|>Z z8H98nL}%w5d$aLLcEZ4!^z&^ zEX2GhbXxMRpwQ{zLTA`QcM!yx-lZ(Wypi>oyliZ(eUTAQ!s)jX5m#&+Kk^`8=LzE* zCp3&X7^PgED!y@Y*?|ajxa-heH}0Li3p9*#d8GBmX~{>CYk^yZfoJ240*|9z=-!8p zzj0O~ilO>!wBE-4$@2-9r;2YJSlj|XhGZgXt9}H5gnJuH53YFy0i31?eYkOG?Pmzo zxCv;Rjl=6JfWF1$5#t+2R*yoU-D#qrU4=|(aY%;a{}1Zo6Im0r_k%$yeUASUO87T0 zm5oen;j&LMfnvTrNTAR04<^vxGnFbc3+!5qKF7b1ssCgu70$3Al|ILRgsC;~MtUXA@PN6@MaS{UeSdFwi$5zk&ZcyZEKGXNTS&xH?9L*#JTt15v1{ick+D1-d{e>X zEttGj&mrj+fU2GfPnnbdKEd*I@J*Fz#wWd+aMl-X)@mpJQ)cDq;G3G#jK{IP;jI6$ zSu33UN6gC8!8f(08K3m#nXJQ{^UIPEo(_I~+N(PR!g+;@_kZAV+4I~~nzeP}ayodS zvKa|i;L9XT-Q6m0C_;@q3jBhhva&k|VL8Jy&F}1FQsxZW2eXL_zK2sW!H3J4fqZcW zt@Rz}LYxpzA^0()nXX0JhC{N;rn#}l8BfRYd0NHr9_W&?d9ha+O$YA?L3y|FeDVu% zKKXXWi%>PL0sf1s3e=VeAIAz7d{E}Ml?pl-lM~ zi{$(5;aW!zXRWutV~CNqVVu;u-pR9A$DUY;x|hTT8=#a@K5p-!$XPplw&PsVkAjBt z#KVGQfKt#dvx4Td-h_IrtDp|0HHFVj`UiLqSa|EEQ`zJA@71+DZ+zV%YFV%6-PHpB zDIt1`h0hb!FQ_C$oNv8GMV48l4*i@wp|)}xY)Lg0`+O_~`7{UJVdN2}8cX(3w6CwO zt;it%NmTt_&#Q`dGO|H8htaOAB}CGxXqiROHaK}gZ9QT#tfZA3ZMxbOZ7M)}w?%v6 zG)230O+CMsKXC!aBb@EMMrvyW)1n18aaRq5G14=mcrEmRdqy?kM|$Qttxt@?TZBc9 zdIz*AI-KgYX==HjspXb6wKSIRcOV7A>$xI~t}}%0AVJ5IvWsjC_mEbC@_HUv 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 literal 0 HcmV?d00001 diff --git a/flexric/lib/flexric/libtc_sm.so b/flexric/lib/flexric/libtc_sm.so new file mode 100644 index 0000000000000000000000000000000000000000..ec1395a8635428f77414045a47d7baa2d01d555a GIT binary patch literal 327632 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ϓ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%0DBu8oH6_{~@?WIHo){%I_zj$7QCIZ-{;(fBf|$B? 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

j)zy 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ⅈ_Vp2rpEyGqb3UM%CLpiDeMjh_P$i|_I4v-FF;#5RbY?(v*H~S>Mxv*js@&x z84B#caIpMy-Q{CnoaBOK4(^$;x@^5?Y0U=9SDeLns!LO1T@heg$ig4dHqTELcLfJG z7ARcSg>C=r`I@r|lhL#;vFCT3RoMM#yneOk_nuYQdo~;tSojOhD!h$Kzr{ABj@}o( zqRQJv7oO~|wEG_{JFE8!6a8INS%t?ycV()Y>fn8Gp&f;MjXj-NcmqSq_4afYWYRaG z%;YYLMId)kaF#onWwQXi^e|~T-b<>(v4+H7)n^=94hrJ1RI@(HoQeg^VX1arw>t`q zK^PYDwoFlqb8NO|wyrdgSO&=7^I$9-jlt?@tKD3$_4#f>UsC8rl=pE~sLShJFtu!v z3P&T$Ct3mIG5v$I$s#q9EMgzy{s;$GpzL>)EdIvy_nqP_-T(n+amUmE`|SQ?ob1l) z_LlcPgmXcclf}t4i(Q4+py&*(XpVa6{#Fg95;fp>{w9Qj6eMdzjUPU*#gV8n41*Ze zs7bXXYSg55=5?z^2g2Ca(ov(iRE;)AjXNV#8cZc>RN@;TgoEj5nIHHFdHX)sU5z_& znQDCC_e<3Hz@NzLR*l~xj5kz{8vpRkfT*PrIBM)>)nF=7qXLFd2nS7QqmPIh&;3c& zNH!5UWW1{Jh~Hb)m?}%5NBnli*p_z!!nvT`QRE4$$jrh@^snFh(qA#ENG&~WPrC|t zcj1A&Jw>J6fU!fm2QfuW_~rgK!ogi=tapo=(~O!IskRsu{{#V+*4-PZ#*~tH4#^6Kgg(ZDGcYdA7n0L9D*Bwbs~wMl zpT#xbl1!B)+CF{QP-F4hXn!gz98fc)%$QRZ05&p)2l{4CWU6xyS}WqywJO`n}B zv;DFDj*PJ{dbHPWGWqe+g9!T;hJu3yw!*-^6~j)Qps)v9*jcgyJlNlnF}5xgcB4~F z-?qin)q8~jJ=lO^HtBLUXh_2@%L_uRhG&Ql7d+KHYgZc`X4r9HE*B6p{PB#iIn#k) zL!7hUrhvL04PYlhedI|+eJsvdfE5M{Y$w|Q^s}R#{C39JoN0f#!^zncn=|y6W=v$k zIFJk%lHP{o#+YQTAsL>K&_ZRn-_Dp)lGP5$Scl|gmd|;p`Whkm7*$`4%VVKYHRZS3A^ju>Pjo*zI1e)TEXYwbXBC~R<02(wTr7AI5gXoz zB44n}UH?0!IyGi|3Gt1n<}b_~GPe_y*xYSWo(2)%}Xqb>f0Xxl%bFek)C z{nXB|yXRu=FYUUPTcKwB`oMUmf#}Qi*=uHbcE?$Lxjg%uIlcF+zFeKXVJ3r)zFeDa zHfK}&R)hO(nZUTP>&u1NRueh1_x(nnyG`zzj`7}=4=%W`RqrQE>~a&^mkY1=%&Kfw zUoO(#H)maaxkmfIoXtgohHqfR`8_ke;&DxXm4U&+YZ8i(cmKa{(-}|iMYNteOrD!C zHa;ESiZtwmd^|+B5eDwK81B1-jds*Xm=&yK7CAac8IIwKEj>s zUD>AVKn2q(ZE?V^PQa!^w@V3jlL4C%>z3UGV4OBDZPQuP)vf5j{Um{V_Ao5I2{*^U z?HR*$5{?t(g>5=N>dlUpoM+&?hLyhYaeowmt9;3I&; zN9*{n3Ms-JjzV)1g%)>VdLi6O1D7at4B^lp4<4yQx$2`U+-0#oYtdGx6JT5qeeNXy zzhs`?rlYo7K#LuH_DS^l3kHiRgnRwhINHysF-jp^Son5e#9I!nSum{jV zM-Xg#1GY8>JBeWPyb*0W3cFxk4p_SZ>*~GSK(rc&rjxw!%{nroiw@km7rsd39q}oT zsKA3U)kC1_e^wUpJ1}VPT=fpK!;;~KxPNj#{8JzedZ$yJqNQa z!JGjkk6RXTvxQzwu$7#JD}922iu!{(#^W1qjPv~4Z+{GCJvUn@u#OsZvxOnL*#e}v z*+PSEw$PxPEi~w63k|y2LPO8Z78?Hh%@&4ck^V~X7Vg2}zu9b|@G=Ft05Ur+RE0zG z$Y1^O^Sq(u@8LS4xotvvnCz=i6;2q2XmLq9(Z;7+5WiEv*R@sQwCOg!)5cfUAbxtl z5wxoK%~KffY?5G{2XoD4Hc5c<;Dae!e)R2>b@oUobM{CueuLSB%^nHx8+;Dag$tf$ zI(`6riq*lm3|`Op_OpAT8tP##vqYooVb*z0fr@48#al3W8|%eeFrlosU~-w=5#TNO zWyYL@OP5qm2i|{l6fC*kzAC)pIQ$q#!KQqTqu_%XU%c`P{M4(XV2Q8it_W8( z@-jRFQFGZ80gi%CVS@0w`|)FZ1RKB%x(s{-uR+AX;?3N(vOmVWTdGLipM&YG)vFlf z$h@|i>AkAPHDv*CbRz`j$E$uEN3#(Ed;}9_V0ia9NDb0G5G1$>T=!P&gsXTv6dt)R z7D{YW({Z7e<8I(pWq)}Tp>+!cQL(xn7t9t2a1Tu3s__1$l+w))Oh$}n;FBeO!_OG6 z+a8#B;}m!~;|KhvG1OXp%+#v-115J^ul|6^P+rgJc4IlwtUqAp#QFm!wDkwfu=NMb zaEoqs0Dr*0Knkh+KUYiz?SFIyEc+b574QHERkgf+2Y!qzV9A1U1AqYb+@j7Ro@=F9KaRu0mAcsV`O+f(6SJo4<)QTTO_PJA4ynwwmLkY}9qzU{jSN%+vylNK(2sd7G#eT0j7zeI z!EyxX4-k78kfE2^!{A7S#n~_I6(uLw!+@H4)!euXIWc<}Fg^YJ{sX_vJ+?1UZnJ%X zkVa{9R@ zkyz`m}@{N{CU0?uLx%iaW>#)^5}n}G9J@2F&Ng3#j6f*&A8wjg*B0prA%jMj-S zv8@wdqKFgUPY@8*u?2yuehaQZb+ZKlvFN|IzRakO-3N?fX7>RPF&S(;u%ng0#sfxz zy9B_0Z-0?^(f=kT%$@@lqh9tL*c~yl-N0oC82`OwwElaEZTG^-OH*HKaOzS%e;#pN4V}Km`B&W+~pu|b{b&Y^{Uq+ z3I$?z8Xy?G_6~-e)Fn0=&-e*vCkBeTB%Ps$?> zP}pC9Cva6~_7@mSP;4!5l>x=p0u%s}*;?QNM98iJFCZYf#jXNWQgVx31*neX7P|@% zMI82SML=?kT?I6^H{lA((CjKeF#77PD>u2th609n1qKalDDZDeU@w8)DPi^!5Q)g_ zCBPE^n!N;CAx00qbkiZ?*hU}`$2I~++-xH-6JoN1z*+=k#={N*R7Pez>>xl|>ztRE zu!P?s^#lUaGO&XHXF0F>EL?#SW(NU+$>sq;g+ayU0Wm5z4~S8*d4Qr~^MGLxW_@Dw zfcJo@4A?wC=h5eYiB@7Z4JD25kXq^vTLusk?Q7rE?FdNSVaoumJMMXaTwaVU!y7NbGT1HPkNB0AiroT42~XVC zVz+?xh>?u~zD7W1SZox)8P+)B<-{wFc$+ItcHxLuC)>Xu1+r^43SfHr;N6*W9N@4= zfT8*)*2M6^I|wndJ-|W)B#YP{fSO4bu{{7OB#YP{fH1g=ou9fC0m&k^2hc1|#TD?H z?Ewfz_q)Huukd1L0K@zHqU;QCH6^eqz*qQXvmZbf5waD)!3anOu@wOIk_=)i0BRr^#8v=AK^?A0 z%_AH~8*Bxj861KusKYDpgRKAvMyI>i;Wsk700^(l~L)b zFS{H#)yposq4l!M_G-QCvTCiDU1|(l{>>>`w@Q)0&Y!Az)t})A)s3A$=|pvy_+Nl< ziG$tnS*-hD;(sC{=wSC|{N{~`KZVh7jEVo{lz??V?W6O?x}RYzhFt6ZfOn^1|5mJq zg5Yk%;~7m0)<(gn2oDP$MC@>y_-C$9RsISmjn$Cc9sxPv=`tP&W!1=kM@CVm_eV&p z@gsM~d2Wx2Lm2lNFU#R1N-^a1efDVFd8Z59tD*mXAjTh_@ynmTYAura)VF_nI7#tF zuXzyOWA#r@W|V#R^a#Rm_AeZLxsd*XC~#kSK-6{j%& z;P4eoHDRl_Y_po*#{!|=vZ*D!vMn}j>F*si-|L;OB4ld$kzU_CRt%p;1y=KK<=GkD zfyyF{6xUdaPb0|?{mC@b-2c-^auh6oYfm5l+yDTYQsC*R_SkA%n z3(oSgFCBv~kK`l!EB4lGwVT55SN)ruNbqVjN()V2w7nWlVT^qTR$I$5?63rQru}B} z$$h}qDo7%I%KcPjH*;b%E-g#ej<742Y|`Ca_4qP-bonX@Qp z>@46g-|sMUz!PSQ33ETM@Hv^EJPz+Jg!v%Dd{NANJcP-7Q#8Rai<9!3q8W^_%u9rM z!}Y3n8l`=Q!_47Tm?^JWXlsJ)cr)wrVS8#E`g zlbo<|&W<`6V@b5Q+8vU)4hbjp?5)SrmFe~XzJAZ{b;4SRnQNzozs zmP5joLiTf1{4^nH=s~i{kW5QRSSQn>PR5jyY`IQVWRsiekZ_rjJs1@~Mo2dA-n~xt zXw;mHNk$Pqq!<%TVT>iwI@#!uOmIjzH)c1XxSE9I)!mdN88B`(Buz<9SSL+UCu1y$ z*2x_XNsB|mRb+N5ipvv{wNsR2jQ(J37>Oy1b>c-k+8)X$OI|dcF_vUEgz*;KA<0;h zu0B^7&8L{wls?9*j1trPfD35pz(kP_+~O6+BAopSx&K7$3R;a~NeBKp1gO|2;dnb} z;px{W;a-fedfkt3F6eUf`qHs{;BsAvDJH~yy~3>sXIG+b-xA^<@2W+S5Vzt96p7yo z<8hj;^sTUyF_u}2y~APt&|&6+J-Yy%`V}F`^&mOVki3$Ru$8_Nb~2`vq{Simn?u68 zh3tWtaUK(rmnLgYl9A(WL-JTS!WQAl5T3&8mO`ge&!KqIq2O9B`ymSJR-stGi&7+3 z_WR)V4-4zoaI~dh8@)B`WQ--5gD@=EuGQ4+ZFf2(yqS|~qL@@ulULXu;q2D$;zheK zPd3bFm^S9GdK>~|zA|BEHC-8YGNzPygTs8S!^~U9Y(JEsT1gcqbuYmYy_MwLgoM>} zZrI6~Qj*mU$%PIH?_0C)pe-CPIeB%b?j)BQlHzQJ~7GmLTi{JB;Pb7N!R&S zA1$sa2?^_DO4!MmQj!jbWST?5M={w6sFU$R^3(*)Nka0lAsL^LuujH@os20ZX>mv< zIV5}zl)V7M;%xb*zRi%_7#A0v{IYe;4#wF@mzFiNgZ&v}Nz9VTAvrqv$X`6KW*06)ILkaOBFt$|133`F*ZZml2C1os6+0T55|O61XVjvV`A327J<)-3znNGeS~jNG^&?ZBUEml)o_C*+ygyb+lAL^KNbXCy^XPr4>5Q-(I*+zE92*=CJ~Yn`Lup+n91ob)LsIVt zv}$QxmsAP+{dK8M##j=q63-#I$syqb=j?i9YyZAg-g+-FEGPfCkba(QY7V=ReQ z$);<3kALhfS2`qoZl3)fa&o?q{A{#}B_w_OYfjEjNLVH3r)Dswlw^ZLa^V|%(x5+Bq5i^^8ja7 zGcAX{@XZ4p_mPTcY4}LxE5C}6@<`=Uw5+Xu4c|w8HC*$tb4>9^OR?1-XGXhbS`lsa z_tN#5eq?GEWLKL)?)%MA7(o2WH!`r%9i$h_uz2Plwq+Y+)V|LjN=z92ENbRwr}z1j z2nrjbGtRgX$WK%MV9y_4RNQ+uU#7{Jj;~V}75R1QJNA6zqCS@n(Tr54m<@~t z$9OO3m)`h5by1(|O!VJP85H?R>WhB$t`H5DzqqK#k5XT;=bIK4`2p)Dd;ZoUh{*)W zT*w4j>lIj1*}Kt}uaYm0FBquUiPg}X5a6=nD!)amgH}UV`R#liZ9k3XP6)YRjVTt6 zVAtC(j%T*^H?=d2Il|vH5H8Yc=;x}LeC7Y`ZbFYzh*m?lSfMVj&lTN-inJQK-3rVy zxuY_YyR28B^4SY8LZ2eJn>s*q*I^3KUp05I=I#`KsOD}Ka(9ZqJFnY5bSA>EGjnpc z$mXuANQ0q^w2Eb#<|i9c$V??_RC|Tv5zf9n7a!M&8rki-tMMTOSjhYNgAz6N^LOTT zt47#HH5NN+%qmsmJB}LsibT|4Dp8}-D||vVj)5Bbwf*C*-PKqKVX86QADE~y+@Hwn zR*ef0hHq_0jrd!8Esb3rHTYqRsKHdCMuk^69pUVq7^4FzjeS~Fjl^K6&k$83@cY{) z$4B;o-_Fk=Y@-_isa(+IC{kw2V`h;CL*-VHSyD?yd)igJy9?Ub)48f4;Y7nSuRsMF zkH(<&83&I?`l*7+Byut0D^X6L8U5h?4&MNM*3Rp;oK(G;Mm>IG{I6s&$B(rdk2?mv zp9JH35PWC|-iZnL!Ph(Oyk1Js;ShY35b*1&##83P6qN)A_0=3~LYRO4s+mJI2hYX? z{QBhCc7A+PO3>~Qyc`oW)AWR1tqLVYtqDaK-N+8y`9j z?@dX6U9*;ClBJ2mmHr2&gU_wR6RIDK=VhBC?ciIkGW8i8N60fdh7$6XCcXX`$?@l} zy3#x+;6{Nb!B>W0ZcM=H3KO0Ifibu+fYW2 z*C&~}BGZPl!ti;6ABFO5m3)a*W#E>?aE}m<>&UjDR4Yf8n=fH!Uk&r0Pw4mx1rcb;5ue z;?Xnas3XhK)|zVbIJcj~?;Wcz*iGXt_7dc@X_~=qI%=WUtDfnoCf@4eNwnu@hxNvH z>$7mR&$@xwY!qbQHm^sYKy&2ZC=5rwerX@!@h06G?us9HMC2l@s2|xwfFD}>`jNe_ zAKCl*k-e`U+57sDeS}v(^k|4d(DU^p`zV{krMBKs3!g1R?n;WR#38~W*b5tLSm99_ zq{61!ix5sYLk)K!!CI85e~azJ!iiWW)cgmuCNr=%O%c4StPq@E#5|BacU= zru!FnK@Mu?<0QRZkr7@7A6%pdl;4S9G4nkXS$d$HRA)|P%pj3!^r8cqW^lmT3kZVJ z;QAnaC=PWMpYDS(HN6mF{)cDUgXjCHr%_Dl&ylXaV_#@BFwL$1LLskc$Cd3W8NMmL z*swcJGkaA0j#nN;SPvLGHRR<_g)Rq0Q*q&l10aOI-^dYKt!e^W3Jziq8ZII12?LL= z2kogRU0JTRM?xeQZ26fc_0oLRn7h_Ql19&r)D3% z(3?K=AjHw+;|QMZq-QgQ>QJ%8&5&GvT?)PFZ1Z@B)ITSWf-iSebd<4Bk(@tE( zAZ0Z@!W&(yni}mGFWNPE0Rbw90DRCS1Abx*{v(3ZAMl_ylV0HIQ4VW$Tjo;Sye5!U zUchAe9|E$EG*R0nUyX6;Ku~b|jAvcah){qdbZe}>t={xA+)~Yw!@c2ITCfxmo`M$; z#`7z@pmJhsv{QaUep(Ip&P)yP^7&ozK_$}FGcvDnXmSNF-5=~ACIa~oZcPtvs`9Hd9gSx+F)}j_Fw<(rHKiJ{s*Z$^avh`r94cQWh>EcWnfOhA_ZZ@<&@%SDH&>^1(Px7C>1Fve7|MN zFsJ#2ClBb-T1LHzn3r?OhSo>9xe!-SMFk3mT$NgIh0x!nHIr`*r`2elSbD_<){wyP zv$9)hkov_kz_T^pGBT!a4Qwl%igxIEt%N5}|B=3JsYo}i z#%-$GCFz~G(~x{X-lre|2G#&TN*AgZd1X)yXm^Ix;5v_xm|#k^vpE&W&afh0%Lk4*D5WCiV*#nMJy6}MJFtJc0(<0_8jL}{;cyN=s*8@l(g zM%(+EY!7RONNI=d)uUJSF3BdeXEysty}3QTZ})1}M%ORtZY@cV=stG0CQ(j@(2DQh zqaSP+*Hdfxw3>?iaVX{iw%-l3on%m~5OijDQH2JVUe=3kJ$uBEq(=T=9$v$W zsf`sAhPc@4;_ITY)=_l( zY6?_AYt}AcBI(X8e63lo!JLR{JIP|g@4lX_O)95O8*)8cZi6$x$>3VD(4iVl zc5rKstITQ4z2b|dEx&i^bz8ru$?^niAG-i6oo6wQ_l(fNrdwI2w5fKl@RIdk)T4f} z$F#*~BP?CDYad_>)jNPPx4L@F40$)Udu4JWM$2+4a~p$AkM{AuSGX*0lTlGiv$|(x z*Cdr)tCd~Xy`JHr0!8YqC>m@%^ZAe06xW|q+1*-zoz?zh8?epszpHQCM$V8ng0-)$ zDmTMy3~tXxJ77usdT+rS9SmZ61r1W~zA&D#w(TI0j1if5E{7!BIv&pG>O5!18kt*4 z@MUl+X=m^lYMM+%(kRRf<&MOa&`d@I#HnV5()L)i0NYdo+gP=Y!Whu)ZnVZ0 zyq%Y}IpWmWC2YNIiLqV{3c#_r(b8ZHDkL?bmDFTYBC<-e)ZDW*_DWh~@9xd8Pxofn z*EYj`+HhM+r(3zj(K_nScDfxFhz?-<=qVv%bLg6_ zsOkT?E%$77^+~I1u*>pBZBsI(X(MW8BT5D^r)eZJn5k0LvD1TH0iSv&;RpmqT=}}zx>_rzkaT9kI3TDh`PC6}5p zZsw%Jwb^W%4rd>n_ULd!dv>^CNrxM*9c~0WT(`bumo43%{={?E|6ywCHYY`@yP00t z61n^+UEKDZrEHr)o%Lul|HXtQtyIV0ZFdjb8MZM)>C{^6(MzhjPce9Dpy0{t0^3b` z%sy5jsX!J&L(-_-b#sr*ZlgQc78&=jUL{40?xDS;kL@MS6ofS0N34D}r*R7}=@e3N z>?&@jD9H|%)>fq$Vu0JQW!fRc!0t22Agj4)*mg0!?aA{0@c6U)44@NAGRuIEw5T43I3nSK6;!wQAU%{^&ka7Y3-@yMj@S=VL{&z){{{sJg)1J2!|9`{(6np@= z2LBOWe;@M;b=fzg#_U$6Zxoc?r$lvYqq5te<~)4c^0&W1(c<56 zc5?RP?8q0gBZEHAi}nki&wd=${-2eP>R*oPQiE%wAca@e?Cy_8bxg5q%s$!Gh;sdo3yps{S7NM_a{T z5}k|BC*y=xQT@`W?o%*nd0mZiB5uPuc=-ZdvZ#mY!o5tcgD2<&bUC2U853iP1k?FJ zF4g9YV^UpFa8Bf}-z_S`&w{b?b4bb0f-zCUdgw4paOOutz||Brc19!4iJEa-8a2tO7G|2Ax4;EoD1B3$G*0$vZC9#R|)Qk)}F93cXB$dKbg z#Bm|baUsKTAon z$CQeL7+S!?{Ww%!!ovz4Zs1`p5BKr#a~@8{2MmQr5UO~EhpjvW7(y#*c^J$C$EXU9 zQ579Loyo(uc{qiKAMtP_59@f?$io&MazL)=&%;hU9KZv8C03lu!=*S>UdaQ$CoSB| zYk%b7H6H%SLl$2|Ry6Z4mWRE0_!bW*@vwvk4w@AlG%Gl0R&da);GkK-L9^mYh!k(9 z+#@_Z%fp*Ie91#K46ZA-<6#01d+~4}56AIv77xpKxR!@s@$eWAFYv(Oxaxg`s&i;; zHS{1@+rqyvp70!vu&XxHj`y^)(qd+o5S_SG}MEZY3 zWvQoqj8CbL{F-bs-p!BooiF;Xu?$-6d*TAJuh6jnkvr&M3Qg)RbM2oR{ql zsO@TwiwDl0Kn>?cmDy1kiypM1DSUOo@!i$22@2Fg=o$!}Q1c}4p6FhXaxQ~ws;xO5 za!v8d(7Jnk)yUQWt*s%x8r2I+pwslNGKpgTS-(E=w|2x*+fMucd$lpDbBA;`hT3TG zlBjlRG;nj&@05#%{Oy2Z+R1A`I{WkF`NhY3-904sgYV{*%o|)%)-M2 z#9yq0Z?j|kgJ_c+;}1h?+~ycBOZBJFuI(x>UaIFF##LVq$;wQY>Krz8sm>nYtk>z- zjekKOKPqDRQLz>F+|XzpW=giuicU_E#kEl$6D3wW=Zr@6q9{5MoE$~jNm2PVQM53s zVxsv``GP3BFshj!MF<}fjaV2Bm>>085>+pWq8Fm_qdBAdhm4ME$xQskPofH>6J}?U zjw-aKL7iB?qe^=FQMfhom&@Xx-;NV> z2mEdm6iWgHjhjQzs|B)h;6RrW^w$kK`d;=LDa%_?$XY;2XkAob(PyVeeevIb(dX7E zx-=@!elyB1h(tQObJPdvQ}}Iq6N6H9)PHqUi#6?%Xz1gSf4>yPJZMF&kN8hoOJiOR zJ$p3fvh%6E9FoSI+-7{tSZU1N?m$l;4=(%#Sh?l4M#Uvubm3le$Ty=gOQMlWqN>iQ zcV|>JA0G*#Dv>W;EUt^PEQqLb36$^nIO>OG*pg^WYDjg|wj?U>H+L!FqFGTdrd)>g zA*wzKPMIa;h)&HytBFQId`YxtCy(FijAr9>=cqV6s-zeeu2HfEHN!!5G(b{9J7|iw zh?OatSH)WNBP=ts3$w?dnC+}2-2yirSmzheJazh676mpRKC!aLKwy$6mb81^Dj*IE zZQL7djmox0nNd;o6Ig2=8Rc(?0^GI!Kla`O&aSJv7e3PX$fMpZcaM9OH8bj9*&0bB zd93Qu$XKRaO}R4~JWcoBQ85IBWO#&-ATSWBEjoPI;56gdCMb}AfdrI-ZORLJ1Ur;R zB1|Xnc>lH5f1P&E9mya&`M%#bKU?>nb=KZ{?bY|%`^{^&v%73uIOlLUtydW~8CgdqoCU>N-z@refspwSw$Jf#z|07o>xg+!$-o^2gA)UAK=~;UVR{(e_eQWCA<)(e^YoJ z@}HIi;l|$F+fRlYu9F+fZ<0vWa3H*o$-xJ8RpE0~>Uhm&B3(8Y`$nhHFs%0I`l z$%od5XFd|1kA*%a`*1k95}pC-D-wJW0#AEVAmi>4yR<~9{@Y-BUHz>#^gU08i`60C zaCf)@61q2RS|QHy`FDrQ^^*4ZbR-b)R{4I>L9s}%W&$Du3}h;}`h;p91gZ?U@Ibgg zSdIG^DQA&Hz^zUAyeI;wz2w$#>aAhjo3v3qsGKl<{%p7_xrRSOvUgTExqh~|-*YPX zSMd&o*XZZ9;Z^d9dCG^FU7n+}RW}^TfpFp7VUx}X>42mm2g0e_!x{I4Q!y|s{uvE4 z8Trn|5I%C6TVukSNmyeJVOHwMx93LX=llcVb3Qm7aELi_h`)<3423;pPLY)ND zI!zRA+aT?6b;CGhNGrm+$90j_% zMI{5!xeA)GJv`@e(GNt6$$f%1`LMW@89);PWWw2q9BW22(3v1v2SQWu0R!R7**4Uj z@ldlKmk}~U&9tE&SsB(tWV2rqUV+IYf*lM`huYo~PJbkv2-mhfJmbFby!#}tKlh8_ zWaQ_4;pBrz;ueJSJ}EaQ-UpfQ38z0?g?>1L{s_^h*KI>;gD`r*b>ZoCjfcc~$BoLA zv%-2ToDJ?`#rC6EipaTl0`;&$9+lhrt^T@%9P++CWWsqktp9j8Wx+wQcf@4)l+&gC zfpFUPC&GDuA*IAwL*bl9gr76|!s(BPR~`t@1#h98hB#+?h(c`L!_c%w%V>;+XKt5> zaQaQ*CHKgTAWGxk^!vgK4ultuh7%8jXFe$cJKfTsd$}@!GoE-|xI}utDO9P$P0Q7F z;XJG|{shcM%@-Z|; zX)%3|q$?Nh6S`(R8D4@xs>#0Sw<7zj_2I1h&>uW_ZKA=f--75-g!0g+0@3s8{^FGn z$uusP3HOHQ%LiOa-IoLC)P1qvcr=^>2F!_V;@ro>OR8+bOlRD|m6G$C0!+Cpoc@H>R)e>mr$uEzKDAp=<;uCvMkg=Z@d>*~$FtMP>sVFjqDsKPxOo~~+! zlzS4~7}C_45)Mpl4PA_;?m+#{ss>4~8(yUYL478HVoP_csS#(39LkL8QC+92oj`Es zZx5#&K+~B6AxaRF7KEq6Wj!d>bk&evTbrZ%ms;=iFU;lh^Z!RRn?!zws(m$cqPvOR z0q^dhM&UD=X?Gw}<}Ss3l*X}2iigqjU)B%~(YJfeKx%J- z=!1#TR`rr|^XZcv0<_j1qPiEKS^a{>VXmqTd2?C@HL+LwMOPn#|(*NgKV_nHJowngrBY=l;0lhvA(F%i4<-)VQs z_qhwid(T8oDZEY=0rAD`gR;<@Zg(ybHG8Ekq;JIuW+>GmE#;{HM?Z)H_*a6T&r5Jr zc~V7nLg!TNd1mKuvGQ;R)CJYm72(o^_@!6vr;_Mxs_vlXgdIB+qQ&5PHe;rJB{HDi zaN_kc^>dMQpm-)7emg3hsSBPf6FYN(q-XW_$~H>Fz2WR@HD{=sZ=nEdG--&vdPIc! zgZT?Zsrt zIo>T?7y6&LKDLcG(_41^~Om_v~iCr8*VVJ}(7V@H%{KOC5YhgE$jp5T`%+Jv-oKHehdf*@|#_ zIlS!paC#}6v=Yi9=>Q#pOkVL|FlQjjWL&?2`C;-xITaWQcs_gL+j;-=}1ayWH^2)aSPoLA8jU7Zjy-iR0{L}1_K|0m*voG>-Q`7P6C zi5u|o>ut9rVK)cE>4&AbhPa{i$HXlQDK&1X9_{v!GL|hP%Lujhx?PgsGpHiiz7l=7 zPX0k~h;Mj!IKhp2X@LT2sBRPSR(*8s47dF zJZvoyXR*&0Y}BOBL65VqljLcJ^{s9qi!HHkd|gGdmZa~)j&a@0yUjt+!H1Fx%?_s{ z6=aRb(Z zlj^^Uu(WXYd*S?}T}55j{aD?!re3J`VBd18$TKhXx_W`^16*$5W6Dhs*iE4Xjk@vI z&-64&!v@l?_e%Oz|K<#T<|O!YU3d;MJyhqg#z!p+75e%w%D3}Sm!tE$E#_7fn^^!l zYqw-tnvp`IT|UN0oP$IN^m`F!Y;R^}{TwkO%QY{+;TY5sP&b7?T_G97MPM1c*^2Oj zLm?`JGr&5~bm4YU;D!}~ed!AEfR`xtH7mjf)I9|ca(3Dw9?a`P?FnrVr>s_)Urcl<>Max^nY4;6uqW;oe+1>6 z2JfTf!y&K~#LA~HSx*EO31|`zexho3o*5;I(KE40b>a9Wx_d(?iVL$9dPxpc7eV6} zO?QLkGHBfU>)%`ds58h8P|d+Plk3MnZ#H%kpr^CZ>22XOY{hQSd13^tO0JQVZD>yY zFTxN-1-Y>&498zFBOG@zA}Ezh@g;C(&xm>^>`G`G`&iGYlgRhCBD&e(S*RjPda)#7 zzjM@nuZEF9*QQ*Lx|^Bq)wAD&h#bk@j?co(vHNYhOlv}Q(X46wO|xT_RPUqa1p46y zAoWCNcD(8eC<0thb#G@tzsI-d_J&2u_~)ayh)X=Gg<}KRuGGvSc$k-&e2$^7Z%&eB zyD%5YHi?rp9fkDqFH}<0WB6=!e&hRL33UiB<3QEw+4X;y@IIn?bHcgB@a*fuxutN{ z4dL8f;aMxgxg+7MmT>NHIBB&QSnALP$4A}Phd*z+8F~^6o$zvH3!1~@Ayj0?PX?1K6=Ncffa%eenHXp)B(ojE;1klY z2}M8oheu|;+RRaIAkT`qLbE^ zdYv&0sBTLA0V!0t=QWvtYgnSr#+rEJiJ5XCo&#{bj3g1~8hKj%Oqrqu2`Kl)*OmBsvRv;rxy#TK<$7;)ozM#0{h7_= zYSoRV>KJfwJ#qxMVT}R1q8KoKrGAwo9unU@`mK+@KHtBZiQfnlS8X1@es;Z_Sguvx zK~_*VU0gw=YQmy36D+$3*U0(d*VpLbr<}f+ufoNmP=Mouw<6e6b476;{t@Lj*)SqJGkrmY9aDHmi$JM*lI_>M_yz@}V#6Bck`M6Qt z0C*e^8^{KLytp=Za|}mV>~Ru5{kicHypJCLN5@qrUyrMC`cK{_fShYSO?Co8**BXY z`(_hl6JUbwo4w|*Z4=9H$s2=g`+hiy6g6*<%i+oEpa%VV2^-=bZ z-Kkm22_PA~O{N6V4OG@92&veqLDe+d7LimcTgmR`gjd`tX*ZtNk`|k*Zs)Wx(kHLC z;x&9(pBOw2sAw^}`kM1G`>H0pdW7^rA#Nwhs8RyizI}T<*81(?w0o@nQ3b~$O^P2` zxKg8)53nOk&75$;!R=C?bS}cHRpU%N==Whc#EiZvfqI%CAvt{_;>TFb&5#8h9-`3Q zodqb`$lAFfJnOn};ltsw1L4)UI45&)_9NlNID3E&*@kssa4#xWLS>PtA1Ur6yoSIwn5H$QjVxhz`@A%{G!?gU8#1HDy=!; zG;BC&R}*g&J;yFteLW62;J_VJ1#1VrQc)bH;}tgNbZ4FqL>O7ZI44mhlhY(K6XUhY z^){1MJ$y3-J63Z!uvViIoIAvv1;U*f@B+w&aqwz;c(DjiK}_)TtSIqWTf% z@66B3K}^&hl)p1^yfMBy8E)s!OISDUHch$O2K&O8YI@$H(1PB`Tu?XJx2UIx-@kxO>)Eb~4$`+Cr{Sf2Wz%O|9lJ zLLGAP*a*(R!|6$neZuREhsW9DRQt3V)ioz}LJ`hp3m>qF6eV<)^+BLx=3@8AYL{8o zJ;_8g&gl~0UgEjm$>e!PK>lJWqEbv9OTrZEuz*ObRURPC4QR_))0}iC7rkR5)n5;+ zx{F1!W8$4mXaAk%rE7%ea#HC40_i;?VI^4ybDt=R?HT}8$-hjEPe-qWIJNv{u2-VGwMX-iVILi1WUiuMEdLwol<$l`P{7$h>~rel)~J5jmH|#~p_)}=%OF43&h%|z z)=S#Ye9M5Z3*wf+No*LT$B!Hw0h2A%#da_DP_e^b4+6t9fwqrO`~Fo*&^0&Vm3_JGWvAhc@KSh-S$Bt*V&w64yRZ!- zB;fft`M|qY>Ske^|8bquodTLLepS5D04|BZQ6sb-N5`FznA!`#n3|JOVH45hDBw+wVtw8> z(5X+wpR72>Qsol}{RFO}hH6#WM-`yv+KdYgPnjc*=$sQaQwuM>WrZY^b)c%kPzoctGoD#b}DdRsWBo(99>g6#WoUha%mPxPmag&*PS_dZ;PgovG z$f`CUPrN~p7LGjLQGGrnvb7rOSf%4tfb=LQQ2LW%=~zfnP&yg2SH(X8MgK3EcO<8= zm=hsUDJ8A(ls-QMW!U*lbt=EpG#RI0CkyYmo{5q})ej`x9qQX+!@7@!6W%EGFIZN> zvCP75BLK0tItc*EIq4=@0M$LN?_5U*H%<<6e=(i6(gh}rn5PTnQOKRC@`+Y=?vm0A zsyi>LS>0K98QzbN|Gm2NFZdoETCTqUSL5`bxVjU)hxxP@T#mbPO4({UFS!|C@h7if zj-T9=^n7-Bk)-u_3oRb6#Ji`kxTya;D1IENIi3KE%2^V!ZonIZ>F({(vC|OV z@#Z@@x~&gm-i=4W!wI(~-q(z0(&gP2`eIu7)K^8|fqD#RZ)w1Hyh6^t<7E(X7B%N( z5U7sjxHWxl5NS429(hv?PmtHyd*0$#@!+^F=78rxeVnPI@+vvJdDGt#q36j5ibI9Y zomtaL_@VsoKftc;GJO=r1W zEL94s zys1zKBGm4koh2o#SkaH2JtgV9)ZdM9OS}8Ki-pkmP#T9+Up(5zVWoM~- z=Zd|{mdszWs9}De{#UN_tmqpWMWgw>N=wr%)=77J5g5^;COLJLOAHe`nv&aJkao9rP4;7fPaew|9+{1tXe9C1~BWrm$&Ud&{Q6rp;?w z3mx0Hv<4lk3tLvV7uIasxN&>1c60m2mX1Pmp|yGI#zOn*!s?Fp4Ta6yI)bjwa#2-x zq_fmHSgaIFW+w$5qo+7vKg4To3h$(ib8!3mHxqELG)~RPhqeeK&Njn z_7p~LuasrNH*ag*)>>HGwsv!nYF7!itXaLK(6X*|Q%9j~t4)h$9;AhGp60a%HEc zqyYh@F_e+emJO?AN;aEfH-gQuD6dI)!`8y)t!-4`zD=tNtG5N6yZZ{|;g=4NmWB$V zXVO~iA4ZN=;-!bXgN-c-{n)%dSl6*7W<0Ko%@BJj!R+4BV5QK64qz1B!$WYYpaJ$h z2xU-~N8**xu)7#QY6vx*1ESw(6M6Zlkqo)Dqou>uSDcopa8T(i^%X19u6zgL)h=O5 zLG3yykCwZ-OZ_7nd^$%E5nyI?Q}HwdNMiX?lLXXfaAY8M4dQu$X^y}QU--6WTL8`2 z)X}nPL+jQKd|Q`m*g}n(S-G;WFgR+?TUD;7NM;F}O*3OoDSOR9HtP3WHRvgI70P$S z!q$ExKjMb1E}3(cxZbr7D%)8qb?yTr)CTDt+FM)Jh>45!mj@)~M-IqbZCa~)%9X+h z5{_6E+FNg?XK^bv`-RN=haq;Ai{}naOE5hBb5pf{c6Zcbxr#ZTmNlU4j-+@+n+pBC zh0dX}QCke!H^6jVO*XV`?Wh)vMG9q16zIC3@x23xo2Eipp$Vrz)8@7{8UQ6giKEMm z9&47fPE>12TD636U{EP&-O##Gc)z|CS=k2o!j5*h@hVSY)F%3Aikt<2O^pjviYrPU zNFVBJ$%v*x!SMuLd?Ynv>y4Y+I|`c+*415f zmkNvf3SB*t%}AG#Mk;M^d73Y00vJf|9w>GWjgH9OVyRSA-xHa%&=JQg^Ush4Wat4F zU&XPca&LNes*PJk zJv?{Lgmca9g3YTtT04MMGtjt5Mxc0}pbqtEj;cI&v(|~rDnjT~CQ3FXemXK^wQhH+ zNRlOQ7xjSFM0UhQrD%4|DYk5BMeekw4ayJgSl!;XMVFmxH?(VlqA@}!F|9UJh|OdG zuC=#w09kr&D2P0g6RLI*^=WKSH@JCINBiat<_>RBN9gWBCmJ!tRvx*7t!dqmp1Qe@ zipCnxX;o(<2Dzg_ZVhYFJXItwWG8JaqZWE!R&cwj6RMaI=m3U@RZh@_C761x{Patf z=RP3@=R5-G`61&taDY<_Yl{}z7O~p+Rz(0Jsd!0UisDP0gdtKF1Bro1tUz3(KFUX0 zM=HA%o<)mCNwa-QsoY@W<~6mbPxcJT)t&khB9$OU`QfI`o3K7fCGlW7#$D4IDYC`5 zq-Gtf4YxG)yu&^4znYnChmlB=(}QOW44 z@`$x98@7&>NO-QhFiIp+hMeTraCZf%wFgIVkLwDo%X?$Dip*7{9SIqDY2&J1jQ~ZQ zig@m{=^7_QL9~%AVN*uNp}LU0V7Vgg`zon)AnBX8ZSi>AGdegZIWI<*Wuc57=iVC@ z#SSpln+Zi%b&;xOtd{nz;Q}jbD1d)mwR!V~R^(z;IUQ0QT_82v$3hdTqzh*CcX#S4 z3J50K!A_^Z`mY?D* zR5%y8KwBKj3PLKna?AAHSKa_bnM|C+xeF2=a3bV^6h-3|Hw7wc<;$9ky~&8t02caE z%V3lj(n~!{>8Pa|bX0$${(DNpBZ+By_skTsWu>e+L@~zQoYvF^kzmK+$YK~uEGgPe z^CI&VQ<7rd;&p{pYt)XTSz6T5^-p7J{bQ~>Q(D3VA~h#}63aZMY(j~uNtN)Twl*r# zux+b5wzb!Eu~z#?qznW7l3Qj0swwWpjW>~PXL*`TWi&RYqZ2GrYi|8RTD_8@DGWDG zGT}nf8iDYPYK^Ynxd>lay$E-PL{-qi((RxV;7Ik2SHAYs$%xsgV7M?{99bvDDFNNP z_GJLN)y5(VOAOSRnVLugQGYGSA+W~Ijtf51URryseyAo%J1dX2uEv@eW#SEtl*Q5K1(6E5-Ca|2&f?^O=3~RdCW=lUK4)i+nUcnl@lBSo2&9T)#Zf= zsly3GbyKsY#447)YE|2qixtbIP)d zT=Y;y&{RK?zDXXJ3AI8H6g^BYmbGA6^!+vwgSOzrpq|{uDp*;+&=gemmU~7Edn$#| zvgU+so3Q3-(Y4NvD5<|OPeTdov1ysecrjU_TytZzMG^kxxXszwH6t-!n{-z--GtPEwZt- z1%+MR4Us*-0`h>;FoZ7L!Cs_u9Krc$I48Or#2qpKe~jGaS$ussR%rVDN5trhGq_F@y|=C(rY160?9 zuic|Ka3Bg?Y{Isjbe0QM-S+vBa4C}pJ*XCmbWMDvuE1)YSTnY7R<_GX;JP3Vc9)y@ z*&Ofi7rNl9C3Wb9j_B3`@();%+BJ5cOw)xok!U$%ooAXA=tk?tvEq8rin#83PcgrA zxvHB7x}mPl$A*>M%($x&UwA4;Ze%(;;Bm1bTk7mV5xA?^S(0@cm@aGfXb(dSr1cgl z=%Y-x4qLrCMX+7nOoZm90o6u1VQpHW>FCgHYAz~<)=h0U|+w%=^Ki_z>B6{;=KJ$Ebzi-c-zBcP;m9iKpx2T4Pdec?>egqFY@)B9Nv)sK= zA1VRG{$OaBh>;I2sIN4$x>5G1#i_V<3vsf7)QpG{S%Wp1graKMW6QsLr_iF&LyWGL z1&g-1iH*Rz)?iCyYx~VvOOA;Y`l137HOAF&agLx6#MLbAv)Kw)9WCu0k}wv)lmK^z zMh6Qx1+**JTG#|9Smj7sHm+)qbXC2Q1S&{N`p~pS`6MShZfPzwHbuDV2W?>xbPo0w zMk+X4l%ot>v!7I25>=&QVGj)tts7n?#yKcTz7dIbn>t)kt6rrtVL6S5&xE!|tXen3{D*UD~25NOtOMG{2VS%x+u^q;A-J zClKIGp_7<86a=eFEQ;aAgz%*M)YFMf>p4p(lVkz3I}jbwD#EH}urVG-Ze*uW98yAx z%G9><>dp!lw$V~cp+)g+8WJGdq`gWa$B0-AHf^@$M^e&NLEEhY6Cdgx9H~yEkZt$( zAiZ)I=RQR;uAb4z&&zrW#!PN$$`2o`S(bzV3n~lq);ElUYP&cZr5oXiI)g}FmDe;E z0*M{Fx_oi>NghN#OSTpw=O$3;NmjPjle!JhkWxWg&h+K*)pIb@gKY>4y0{ZnjU(tb zjdfeH64SMCo_~nel0Jn<^JO@3yJ8ORdZV>^l*cTNid-T|@hDo$mQ6&&37Ltxpg6g} z)VNyYS`=rq25?doId`ejA;+asxd__J0?HB)sbzZzPurWsQIQ1>uS8o_zt+zC#T zP}iCzAbb75*CVPbqa%{F!nisIaP|bpV5*Y1L_#aXg0Q`E5HDe7zyKlG58bf&me#~s z#DoBFOn7VSrq!*iYsl#^J$)!=<@Tz*a#dt~nYLl6t_Rp+H?MPq`bFgf(Sz79O&KIV z-y5x~bFEEeq`Idr5z>JzYi04Jqo`fw$42(J}%LS!GE#4=`En0-%i5Or7;d=HjxvZ=t{oqFp$(x&Q_i=?4g;HY76A3!B6YwG5pXCM4A zI4Wm(EErf6;R#0{MV;7U<4_dz)ce3L1t}3-gpuM5ysfz@M}o^p^sUW2iL@NOqIhm1CWNX)o7_|}Wv!aJ9K{JnGxa>B5W#Cal28@dsYP`jruG%AOgf&K&qbbb zz>v18)8%uFQ!WgZ%bopR?im>vgjV;M4p)`j5=o&{-bVF=d{rWWKwIe)+Z4B$$dN_c zpdWDaVkOb~S`rLevDN})=N0Z`sMz1PvkOOIVci2bPLHy(_$@KWEjUE7rge4OR;fZ! zWn)hml)HyeYmD?0GtW7D80GaWz`%-ny(VLiROk-B?qMa8LaK7SL*ty+Js;^yS)G#! zN|QOma`8BcO;!7k_E%aWkrG+YUxziYI&Fx=hE@UKS`WB0Af1u_e_wW0=; z=Tls_0)}|^70o#dR276*wDKI0_~G)vF#f4NTQ2gp#lnWo>kv3`zP~+K)3$bPYrBN8 zT;kN>sbKvue!_6{<{+LnfDY^$?Z+WJ<$)!XC*;7q;Wt zEY~l_Hh4`>VIz}w>ruZWhi6UW5}p|v4Id-?$UL+ITyt|QIghZgnCd(7JEAj6mrDfK zo}$A@p&?o)P&>Cu3J}OI99pyhNoPm5U5T_wbq&d#bw@n3(Q#w)NJlIxltB;H6@Z3+VhgCuqg!O%8v^s+p?#No&Qc0F>_ABZhlN=C~C z_4#^b)qEs8J#xlI9Hweqq%@eB?}dRRJK`2C9>s;FN+2*Mb+oBeDcYI98Mzvp>XFOL z@op0MY-Y|}NybU=b2V(V0SfH=w=#{lvh3`XsmP7ltE<=gXlQ}{vx+8MeJUmWK zT_uE6NpI9-Q7(txswKtlFUq?tsidC7MH%<|>Aie0SV@Ie^$*?ZU$*DDQ9(qM#3qle zPom|X*uMGD$nRLuCobp3CTyzWdyB&*_v}r1D;MoR9*H@rx}L0vQY8Vk9*_nbjqH^^ zMcj9FYG+GUi)h#eL;Ilh=4}WH(Ztk`t0S;YDJj=@o!PV)8-^@)U<@7FpCXcFv1OO$ zb`y&`5s7TP(JEwZ;d?b!dVu1q1yEzvl(B4%Y7>@@_zD|yR<%&O>%kFRc}z(A+!w8O z%qe&pA#;Hi@qk@Aq@rp>w*QdR3zgjr86301osUxy*hKS;1((T0ZmD}IR zB{rrl53U3Jd^`ohn2tTojrmRSqt9T^O=hWiNNu_f?H3PAB5taL^U}U&l*ojP! zYg`*{!)DaksyDSv(s|y*cO4UTWzP$vn({z1YzM~cY;NuF!+ zg(ui1(aI0OAAMia&EK^k0|vDYl6w@c2Gwg9qARGLK+@IVjig=BVExFxbATZbyDbN#aeTp zq>Vw+dxtEbF=qQYsIjUJQC1{ICy_6gZ5mrzhkDu|FQFh+0+0>Vs2l9y-qeZ>(5i)% z_$ZJlPPq&-)q@PoQp!UD5SfeWfwMBix(%CGiFfA?v<#pXO{?D|v|Tr^&CnyR*><0n zQYG?$Z=LO1WNX1hh&tOalKGujbEF@_8;es(XD8GkYf^p4hA%z3Z#IkhMh9qaUR|~G zhC@py6+Gz+@w9zF=HjA!`e0Wg_+HV)Ul;Nc84+|h&{B;4ka4N z$dE6HvbzPr21>;RwVl&s3`p#i&u(13rN+0_UF{-R)X=Z_Qgpn8^v! zb{Ko~T5U_FGpe-G1=(V2j;7o-N8&i*7^We+%~k7!)-`LB36X44?JJfxZYykT+qfBF zvzuX)Nk{t70#h+=Az2QfPTp3#;4SnjrwpS`QM_1Ue!|Jb{B@PR2)PvMYfH44>TX>M ztM+|V;eMD3!joIkZS}klGw(bLu^LNq!JkSs z7ctfJWgR3bK~NPKv!+LL1xL*f!ixu2n~@Z6V0cduB}6sJu=7)~QCY>hbz4V!OFDuH zdwss1{a|^)r+Cwc|FlXG4XLksP_2^{J~punq7Qa{Xm$0WEq&;qh`phq{_;-sv1_&^ z&;8gor>CD8|65(c6t^OIP^1}RBe4i2ZAhL?*EGV{k+&tvhvtFMRwj{QH~dZHhjJF> zkuAgpPhF>r;Bq~+WPehT4EzzA$me;Q%yNPxBxa7T4!+Q`Ww@QE6n$%HZ`+EBWh9hX z;(;C2Ocj-~UKG6|Zo!{OL0Q~Fu%TsD>jtcOQBu;!z&+Kqnjy~P4HT)U@UL~Jm36X!qqYnctQ6tgu;X2p4XU+m z>+n!T;w(%oA$fU`?MkNfPTeyL7t8=-z1H}aT-8{OUvwXmsq?L78%9J|zYOpVS2wiT@m{kng+DsZxYxsW`=|XHL{@;K`hJy!b+W zOpXFu<2oAe z&;ulzwnVnh?7)^%dB-F5xi4mdR!WTJqKpRbylCRfPAV48OK^!2l@%q0$hcuIKgCrt zXKJ=FiaOD*R&=5ZZGb$=aRuF^%1&xW#t|)9w&TNDZyMn zeQce|$^tvS%*)udZv)9jCl(H1eWJn~S-UUq6Y`RcFnc$PzGes3XvL2~IT+pFhjat- zlEliqh$YTuD2e)vA1}~nOZ7!$+zr*ovh_s(ngCR9dc^if-;r3o!pE*@-Pi(2iFX%e zQa_%@(6#wG5kB7{WZY z27)Dabi!0)q|6*m6RbAV$O57hCGp<37iW{Fqce*qpZh%}oKNx1AWE`*uB;(+{$7fsB)|1?Jkpxklsjup}o!}^Cnyo1f7Zp5=2Tib5K!^Hh13_IN0i|T5G zV-o^51&EIf=|$Nu6gta0@fMWZ<7S{)^if{>q!pb)VqENq;-NZyR|Ya{8y9stqZi%a zG^nyw=+w*dp3ae=7bb;`f(RJPL_jK7(VrN>hA9pW$g7A6>Cwt0Z|J9E-43KX#%pFPLaV zR;5^>5)+&>hg46Zj#MpBXX@)8!lPO^T?R&(zt*k#a`K`Q?9~l?WH5a@Q6w408Ul}C zL0YgdMaL_lDtL_~8}Ul%+h;9%#WOe-B2xe0=pY;p-aM%mTnX+lJR?ELK{CV2i*>F7 z%YdNUB?vj$)(6L$zStWPJ8CGBAc8G;k^%*aDagJ4sfU1b; z`Klb#AeCVnq=Ez&hRUc*U{)wE>ES*dIgM)8n}!*onJHPwgr>j`!W7qt>n8c9S^in*s7il60Ze{deaBL?L7by%w{N0*t5HMK0;)IuK0#9qNFJRNE9q5*Yk*E zv$z+`!Wc?Mp|QOs#G6MCrbjG@d6}JSV$_HMpo$PrK21yv={A)fm4mr_c{v3Q)Yh>}>w%5E%av(rnZEXrP&y>JsQmr5<-kSiEv`u&hnr0fKrm6 zjLs)0mFZ{XoOCuR-HTq?{zYUkX8Nf~ZUBFWcOUp`JPo?EZ-?rLWfN2yYUEGr;PXa;E9rEk1WXi=3k zcGWb*<>fNU*_&dY=#O-vFWyoNdZ>to zyuNCU0s4KEG3N8^r>GtylST7?y< zV}?>uT*{@23Xz}b4F1hyoXmucs|4rFvreK}fKp+1Eme?-3vZCv(1sIZq)3MwP*KP3 zE{_DEzZOfpXQ5EuFXN0RZK=_A2~}ty{!w0)Slj>ByeGP8ddZok(h}=1J*MSkdF06C zA+DNZaY}wa)vfqXQXt%}#U1Iw4LPge&U^L#wRkaB2RT!(lAiDjZ?mnbD&9W?v&$!B zC0IKk<*!~B`Z8`c#g8N{sqEqdW7E|X+?aP zvgHN%A`y#dC+buTuhxLlZLX$4=S92ET|?MAOR4fY!DaVgN`vBXIYhAI6Gw?$$pU9rCoiM zR*d0zVo#hJ50&NEO{DIKP;#h`1Yqw>0FACPRjjPh1B^{L73-uAQ#BpwJck4g5Ns|g zYo6E17)u?{C^biM*g{K9#T>&RL^_fyV|3ZrB0UW4c~py$f=4LAX>MLMTTw0E%Vj0+ zWhWpb>w}E+$GptAsJzA^AHu7cCVXX23A(Ci7AC-$*h>}JGG(=Dc17-Z$IwaLG4uAR zsU_Vm`_CKRspJZe9@$5z8sFSvt^Oq9v#ID_Ewo)V?<827o{V=Z-ndq`u2H;S#m$6F zYK}v{&n{9lNb@{6g~D4^nvfQ8-fi!-ieR4*St>qztWmf)5){~v{ScFEBios}S}hYY ztBUSyQ$<)prStA3RJv-u3C`!Kqozb0HpTtKkX7vxf_0YFoUJ8ia$A!c`I44U)r8PH z^GbGFg%R~6OO9GdCm02{UB|`h$8ZYLQ9{&-$>*4S6|FKG$pc7cm3*Xkam&1)l7Y}z zt6gN%RU?XmX+=i1p2e-oGaJ>A!k$tm-oA}7UD1~4+D|MY| z6?MKq4&E*p>b$*k7)M{`_YE(=t4Br_4389tI{UGQh@A%PY!(;yIjDI-Yjew%1-%2s zy*OjEplh^$pl1Qz>A9dhxBzxp9t?ihF4-9li^xlY^<>4h-KVJ6V8Lj)v|ymWYe8Rk z_X7N4T4Bke`FQSVbnkrpSlWW7g$wY`ih)t=yD1_ssu6M8wVXo1-Y9-Kt@m2w%9S3l zZN9uFc}Omk)6lyZ9dPW7Vbcy@P7%A5_a*`Rsf<&`vr{Wpqx0Hp7ufH4^!F~n)^5)N z0Pvq27l$78gZ;mwUM4kCdd?e7&h9*Sqv-*Z=WaMV74_VWXL5axBqzt$p1Y~xK${M> zpua>tHEkWO;NZn`qNp(e&*^y2{$TpH`Db^SXRr5XnRuMC5OsU@M>*vj0@crkZJb&< zu|_|&9U>DvwH?M@W&xhl!?_^&@ce@5g+xp}d)i+_E+G*uzS4*jUs^%d3-PG& zReS_dI`t=g^$AQpBa;u+^SQXC=f1pA_PWpvh#%q9Y}%hTC^QjQp>om+cak6+WcME! ztl3BH-WQ?`Jb66H%x|p7Nd9YgrJm%~o|0T8fBc0%vzP@VTgQ(tCY!>BacQs4mxK0l zg;Dy4i8O!{h>nkAV)&|Y6suJGDupCnt_l)BWg)S1u*5%@A`zLydmk7M(Z(U@=Qq+Ef$hf zYHlO^*Z7^^b^`zPJbo@Hc(%uQcGjQJr=$x(o{U8S&hXot9+A!Rg_%f-@hdL4PQ8>v zuH{V+F`Um+YTg__yDCc;eWXC)y6Ujk`Na+G`8!-k%N+hf0v?X4f>r^rH6Xuo33&Xn zuq2W$nWEQ)=nDXnxAh?{ob0#9EpbPt-!sZD^3?GNzT4cQFNR=mPXI8c+uriRcQdB$ z$kTNZrEvo*SjX40LP5LiOBew11)HqO&R{S89zr~>Bmb3_^4zpiWVhq_`Yv6LnkVABe~2> z-4JS%EpWUxW|okw50BSEl5XuscLYOz=1Jc3kMHT1X$V&8Ew1|3FTBYWH)M?HH3-s6 z;=Q{OF+!yigJ6_xmRh6B^rD7YPDM8sqmNjqc=VEeCM6`ZbE)x7CV=D;xP;yL_L#on zfX$N+#S#>cEHjHYd-WY1#EGF)b7>TvYT%oNygz&M0*C}4g=f#y6OEHgT;;Sd1qV({ z0QR6oz1S`)cQ}6(pbP>=YIM27R`CPCF@P@%%eFEb1ylm5Bp~Tm%y8Sv-d7B)_jFEMrz8015-Jt*<+et36s>8>Qk~W zr1192DGd!ktKE-q*2zuwFKo%c9YXu@R}K=KRC9t7H8)j2fKNsGE9=e$iBjdMDZiew z)vS^i7j7588c?S@bwnnM!l#U#33?Go^jY&~fn=1jHBRBt@=m<;v!qvezWg~DN+XaA zbqaNmOv{~>)@G;b#VW$xoAa z5EO|2?-~4l!jBW~_;(NzOBLhDGxQyGtapzdG;O)eJ74c^qS;5DMzmoWKiT^jme=`T{ zBm7^9zk~P}5dI6||0nTpA^ddAL-mmOVxRJN9^k^an=i6!m*XFq5Z}jrJMrIsh2fuW-36|<$B6HC@c{7; zUupP{b6nTApwBvR{C^_;o~sP;0`~9v)>(z$;A@=Y3wvzY_gu7ntAl&t2 z>S?A|cU@zMuD7laBgA(6a z-ESYw;O`~=UB|8q6q-p_Z~&qVg~k2UbW0l3i9@KU4G?e0my#m~HK ziFM%eb-QRBNYCqO;x|ZQfIso?^5=H3GK25>za;~AJ>QXmyFQN)?s4Ds`L%?*pL{>Y zmU+D`(~s-thcj^3!v_hMJW2js-yS90<>K;vBGZq{_dA3;Jua7@XW+j9-}3ZlQfU1D zP>a#w@nt&U-ypovZU=J+pR&pTSCRkDpW{O&FV|b=PcUq7r_=c}BLjE-G!QOXsr)&A zmJ#m#JAZD>^yB=wH3N74?99L~#=P?Qvyk(>ZG+MAZu_LCj1WebBz7CzP*w74-o%*#QzBKgR+h5e(wn34TL-29w&Td2LA8t|M+I> znCu9CN&H)h@A^4&g6T=&<}|*?yPd>$|8qX^ztnE{pD=QRkK#;BKSe^~B}-Xa??be1dTI11_)Qg!_D*|1>23+L+=*!dmznAz^N`^=`9Xv?*Z*Fc{^t?@dF3?yYlwd)@!j9Pknl#rU5+KfR}$`W z|9!%DWZ;Jh-=FE{6NDck-1YWLgdfhpp9Eay``+C~|332bY2x2ceD{+RkqC(#j}Y#0 zt%2~P8TeAbrT;p1e37v)Xd(VF;yZtCCH|#%7$Kh}Jzc~HibL8wmILK1z6F2L4ULS7!S8DdAf(_*0Q^3g32Q;Fl0SlIiF9gzwMb zw-bIS1MesNFyZch_7naf;jRyVO!%XOJAXa~xXA0WgT_eb=Yzz5B7^^B;{TlZuO|P$ zO8oC+@Q)M!_wF=A$Nweqf1bgg3BrV)@kD$b_2(SGg`f4WOv|^K_^*GB^?x<-R};U1 z_--VSiHf-%GgDGeG#E z4E{mFKb*mT2jC*F`#)s--$;4gOZ+3mcltj={9hCQR^mTK{9~DMe?a_gA2vkCKMevB zJ_HAhKl9knIe-g2_Ywbxgf|kuf%q=Rb;Q5>BkBHMMEsQ*{4()>O8o!L{$ES{9U1(4 zi2tqs#}L0t{CkPNm-v{b{(Xk-1&B2O0Y3>+vmwdmQxj*9hUxXJ3CEBHa1r>#zGW@GD49;S)BV z^V8R5M>F`oE<2uq`?{?D)oDHPb=jF2xUb6^33obuU3Oyz|0u{4eZDmVe~R#tOh3LZ zdu;~a*JXz@_`WWCFoVAw{K;GY-Uzt*pFgnq`g(r{;SGd)eP%D=D+zab9RghRXYpr@ z|M%HPaG3Zzh>uJCdyx3wB7QsJj}m_*gMW>X^$>*6^A6&#CH^^p z%X|+J-}QeX@sDNtUq}4=i9d(^6o|j}Zw=u6mx+HggZ~G_e?9RZXFu;G{_za`e@6W7 z&!+qT4Do~4o4hc6{rfucKSKPCgnys-Gcx#f2xP*apAvtmbr;M8T=>&Ke7I%(yOQ|x zAGPb-313e9#!UY=6aQ_*_j;qR_g7}{JBh!Ti4x1N(RzOi@tyyp#DCuxj1XU+`g(t7 zhW>-ZKlh7<{{-px_5S`0J$DiR$HW)Al0RSXA0j@|KmEJg`~Q2p{vhGL-ankd{|NCv zO?-+W@H)W#8T>yd{%gNv9sByo>jOuK@ACRI@uz;-@V`TPyl!xm`0k(of%rcmzW49- zgku@}W5mDXsCC>;{&=0?c!vJ(6aRnygW(?|JzjqZ{?Iybe*TpBpZrHdd_D2K{xBnh zKMsa0`rP(UX?(9sG-U9n5dSO0_jQNYD;hKS^N7FsE7t$>IbW|=tR%kk|5D<=?y)q! z*DJOV-}%3Y_L{X zC;mH$?|#+mBs++IF8lu?@k{^90Jzk@?-Bm$415wCj_B3fGVrSi|0v-;uGeAqXU1Ji z`~%;z@tki_eTMkXw<7T$&ER|8=17JQuO|Mqe@*xA^_*iF{0|VngZNIr*K>k5jMI*s zZ+}hvtG{jiJHFR*8i?d~wUe8&X>HjChzcGXF^_(3U{FzW-k;@-t@V%b1 zKZ8G?`2S3Nr{C*2hcooI691!5rRn#2&Jp5A`4aJeNc?td7S(f(W$1Z1@mK$wAv(U- zd4fa6{|5H+F5q0Z$nAZQj#GlQDveWN%p~ejUn~A@j_|6}%7i}TF+snPgZ~TD~ z;{AKQXe87Bqr~5x!S{O6p$z`FiT_9j-|I;CXYl19naFGWzZ*SHzt@+JX6QeY_+KTy z)9-bs!5&we!NYB^nQ4Mc0{$E77+(P_f2H)#cJBZ&v{N2R=DDfX4-0N5SiSPRM zCgOJsp61_#Y$whlu|;`}ca+5#qaiKTG^q{Lm0r+w=mjgB>G2+@St_jrf24 zv|W4tZGVRT3i0nH{@X~W*WV6j=y@~o*ZeHqzt`W6Wbpr-_(zHF z^n3m7Scd+i#1DRv?%(Tg!JE_k{}1B7K7;S|w+7-ja9-2TGX9_W%XI%MR|F0zeA>#jn{dm3Z zNCy9%#Q**OlkVT^c*ipMpCJC+-x$8r@AbXlEyjPR|I5U0#p+f5UTw_+ulqF+-{bZ7 zi2nuRe~|cI4_rxn=jQ|jRugI^*3G2%NtUT-``e1r-8dmZs-OtAi4AH3ce zyw%{&pAQoMT^W3@H#QL8{ng(R|BBPCpMT=GUT<7U{0j+xiunIY{CaB^)f-1L^o)m~ zMP79i4bkcMI^>}Y{`tgz1M!_dUZ1?5_^xkDi2tcc>Ha^COR1k6&Gg?+{PmL!|C^kb z*E5f2@P~+h)s%GqUeBEI#|C%)zmE715Z~$dI%gyCo&NU{|Ea0z{=NRWCDZ?35x+Do zjqi2Q{lxcpeT?}3hxqq#zVFAU@aG8e8wd|D5s}wNr(6Fn--n2QEJM$^#Q(DyX?mm_ zA^it$Gr03-8S!tJX>jMyQ^ao|zVqi-gs&vr?P3<1OaD7EaId@W&%n1}+`PK0=lMI{ zZS?rKp3mC0|3}34dY9+(4a9f;e1-V8(2*bG zd_A9U%-|m*{*nuf9`{$C&#%njf1miD&ft4Kza@kJQ{r!Gu>QT?<@x-M4F0(D%pM=T z(C}B`N_+FoBBK~VHHGHpkc|QMP z;(NWz^Y$ZzyB&FcevEL075(!(Jh;dDcYA*x$9-tN!Cfw%hc^)4UjiT{Iz>Ha+rZy>(&e>3s- zEi(MKkq@4SuOxm0;lsqAxj5aw=ixgt_-`QoyNU1gdmg?&L;pvK|MyF*f5-Pc{4nv| z|9^(~zg}wi?)N+oKSF$jSN;1J;vZOM`0j^24?mWn=aA>uzw{PV3@;CcA54E{e8|Eo8o z>G3=~c(1|T|9p@56I%@K{PaA$f%wk<2@ssf>n$04&%;+{@Gm0%g{#v2dmg@n_#i|7 zS{khX`-o37j`HyR#J|wcgD&EKlLZgY>pTy?pZHGyD~SJZYm6S3m*?R}iSP1#EAi*8 zP1Emr`0-5te@guKW$--@pYcBH$LaYJ@fR?m_-0^#o`*MP@V`g=X*Z_n_dI+H@tywh z7aBjWZ%fnfdH6`C|MQ9eg$%yu;fFH#&m;aVH>LZJ@^IpN-u@!ue~b8@w|gEQyx;o4 zH1+T0#DBwjgS&h^4{sp8^Yd-Q|KkmY@ACCLd?oRnKfl5#nr{>CcHw#W{tR65R>41< zf!~jD^YU<~bN>g79?yrJoR;n^62>+&^Znd zo4@NRqlf7Fk%#|}z!!U;_Irl8iVgl3;jenY0PiCFX~K7W(f~t**FgcKpRdfdajzr% z9Kt{FCkF6-8VPUtQ-k}s>m1%@@RjxvY$JT`5d+*zcrW3@e_()hb@o{yeBSkTeUSLC zBK*hiG{CQn+~5s_Kgw}=>>zkI;rllm{s{ZIpYRpe7~mGdKSlVT4jSMt!oNuPOTT7- z{?qL97~$vav1^t;g8v}A?kxuY1nK-w!auvn;OE*$Fy&(7|0691XdwJ-!aqmCZzcR1 z!k@pzI_9=eu!`_se!vi&{%wSRs>k53Vn2O^e}#qTO@!|w{Jc*XU=`u7BYfXz8vYK# zKUy((AMrm*_&X@a0^y$^{Jwh(@HxUiPk7-51N@5c#|Zz}%MGxD@P8$|oBHW={*3S; z@@+fu>n|}rzi_1isJ_7&gdcyU!T*!^R}((*uMI#m3!X>#*Y7s?M&h>-{^q|hz>5ek z5dI&pFu*$qA0WJyhPaLJS15dXaK$#0FTuf`#Q)C63_^AXZy|ii8iRkI4c7+@cVe~R!0FE#jw2>&JFD~kp{z<#D(YJC3OMnkNOw;Pub z{>7~Z|6}4`NBAEyfxVdhuP1y*4M4XTEFt`A((n4Vmhe{* zaUJ{FM)-xK$Mb=1!vA)yA-A?d4v=AEuvi z`+A7*&omqU=j|i-9O1tqf!`(kF~VoDANNcDM)=!lzpIG3|KfTB^b&qI z;VbBXQ8v-P`v@PF1QLIjjMtCgGlVx&Kl=wldm*BU$))=UZ*;X@Jp8)-2KmGgn#`JgFAl~6F%z|2B+H%RuMj)e3)yugDr#~ zy2$|R2!Ao*-(p_$cFwDp@EbmDh^~jPAp9F|Gx(Wx_W4JI|32fm^X;94zvizE&-LvA zz{dr1gP;GJ1KRE2Q^fx&{p4E+{|Ca)eVzf15&kcP|K$n;v=RP&!fz%aFCqMAgn#lu z1N?+?pKz7QHb*!8Cu~MErYZTgNPK1@9w#1NFr1?#}@if9UVOB00ekh0hDV^P6=4 zkFlS}Z!*lyZ163@|A_s2T=*g3bGr=D`E&Z!#?Pzg8QkskBErA)NrSH=|C>q^3BazE<;`?;R*(=&dx!~6dWLv;T413rsw zlOJ~|d|L3viwx@d;UCn%zX$M!WC#5Ia1H#wWB<=zV2JOe`hL9z{=e6NPn@4m|HU=n zOKQMxssVp-4fqJ)dGmc`4g5FOfWN;6{J|RVzXLpP+@}DisK|MKbeAgS%4lyd-iX*! zEEW6kdjZ8#p)!b{PaYmBmV-i}XSl$hBIv0Mm&%3C(Y*oQtBCi}SBgE?Hs)g#di#g^ z3wS|o=RUk!vr^iJUk2_REb4;-B{>YU1(dwFIzj>`K`V67Kh!q zxxJ&Xsbym;0h=TAHLY7$x3_JP`31v6h13tV>APIAaKEF;M~drQLug@{lL z)703Q>T{8d)6|IfmnZ1VmqJscbXsgKl#7+ok+|19qLvG)ybc~eM7+DdyNDl~!0T>{ zV>eo6KU5u&o7eGD`AzD$o07a+-?=!9ci%@B_Qif=4z%P*4)^jIx-eHvf-W;kFcv>W zzyWh^%1;kQM0m>#_oUfajFS8MbKXL}y)ZcLQg6uX)b&Ne8MwaZU+AlOQ|QaPnM0!e zqIfN73X!QdrTfdsqqhUwE2`}s+#B0A6}GnB){6Jx@9QcSihD;ohkAHX-ibF_->z@b z=Bup-JIl8Ry`uvI_~n3s&Qf2o&;_@MA1=?ng<9Q}{2WWZNS6oQ_$g>8p#EY@sjE;a z6^q8&6eLgOnwCN5Mk+huJxlUiC&kVZel%Wql0Vkcd~m*VY+4))6niU$(r^Xj<+oe} z?djVY;nO)}Yv43RIP#nJxg2h6jBw-^?DKGzFON`02J#Je zdF()T<@b>Ddj8nQdfn+((U==H@aME=>3xAMA$W2#P4wQ_Twku@=R!n ztBB!s!&?sIBf6>>VzFCtiJHi+Hu*NUB%hs2^4Ym$VbE=Y0mi}DR`QNH0V$~U}4`G&VBf98wwHDyu0ds&n}%SHLKT$De{h555w7)c*L zl~lk_AJj6mg{}pfh2b}|Y8uZ%R|jH_ZE6b_QwchIdJ5J;EIwBg7rMg0Zcm8ox-&R@ z;(px#ZT$SP^SQmSk>qRBT1iE7KJ%ONRjfH*#hUX~tT|uBn)6kxIbX$^^Hr=lf6~oy zYNOK@^VEDA!QWU|fHbop2qFs-|C?@@z7&6lK9j$hkrxD-n(|rGgiJIaABxmDAUR-R zq;ucEaA!{qJSY+H;FnK;2L%Ei$UOlb969jB9hXjo3dJHSJonh$Hp0&nw_F)M0WJ)l zxMNPe5%vx|`1}*}2Wtl&T>lC1VCTTwbpk93X6K1UHNimfqW+|J{~<1)8FfeGjD$3C zHG&_WPh9&~3BayzvFe$){D#L;;>jhLp1A8@C8`lgA}7B}jPxO3HvCG*8(o=qjV@ys z>L6t%E+VhVDO0z`ye*l4aI02xna&vZ{a)l)^M}s8oftazeq!jn`#yB!|MQ2=y`303 z_kLpNy!$@1IXrV3!fc0E2|}DAxmY!o#J#Gq68BPL)q+!XI8>^MN9p`~u~OyV7Z;v? z2lJ_M2eZk)lM5BQtTl?<6TZQ2^N8@>4t+-cVg3;AeyZ2huO7FF(#l5`oP{r!5=}I8Iv2Ke^j&)yb za*X?aPiC7^e0pp)&Apu%I`@8J=)C(rG?+Ek(7Cr0L+9R444rr1htAH};n18kN#Z$b zu_AG=YOKV))L6CP^h6vg)xapT`>9JyX)mUQpwZ>9^p`K_g4Dm!` z-1mD`)FwtwQ7dpHFZgY@0xHA(@*``!q?{60p6Lm)aFn;0>1h{nZ}c0)kt?y)#HE;K zeCc1A=}BkI^yDiuJsrX_J^31EdUh3MdUhF`0*Bd!a`>43b*$S${aE*9I%C}TdsTuV zpV&0_c4Fw<`-!3R?)%VHrj{N$_jY3F-1~{4^X~i5DV4*pxp0b;oJcispO=3x7IZBE zh?}j&;l#bFDJ1Tt=t;nX@#q(+ILRbz&~f-b9q4qpoq7K{tc@ z3;6NeLb?BrqRd00SyXJ05ag9?PXw&)u6qqZ_dv082&W5l54lwAi8h0+yDsZ`cjtio z;5ar;olz>SE1;9?C10At~kBpXg7P>mS z?Er+RO|jHjF8Ykpww)eIP0jfU^-*3%0#g#>afF2U%En01DZ)wj&!Yl9XEQ&Wv+*p! z!LH5%>Z)Z_2NUdM)~Ca$AYrO$=7_qAVn!B%)=2@&&D1r{z?Fv!J8^nspy-6z(TJdX z5Z(uM=&nw`;b$H4-mkV2Er~X0tAI}Lqdo}9D3rZ6naI?rb!x+5UE)X2vzRe z*yH&; z{xNUtB%ZgiIfO0(tXqQ&)Jnh<5r{QGW4*yrFf)C5Br#I=o#j-xu*LeW z!(!zR@Ngm0aB!E}%ai<`z5EI85UwxN=ET`#j$D={OlADgx(c1MWDTG%>|!!&UM8^Q z^pUZ`wgoTa7KOLKIAA`1YBbJN605X!?W2$F$`w7qT)BKTxGh<%#hp^7-`dvZvYvMA z+QwFMX~S-^S z1vR*M`1ShIaB64lyUHg`G>dD2i-UK8E!8%sra{U2nOu0l!}5irUp)U|D|6#VtFb51 zk&B=5|D_REBO|JP1E(#lbFyFyx-YG5tT&hKh2lbS5%=)+>Pwo~+6~4!gxgAl?0Rbx z2Q=PG#=6U`>T<0jxrrmX^^=IffVQ>C_as z&OQ(mEOx8*eg}|p*umX*E%t`E<;1FX8yA6DlAFxTW73#)7o4N=WZ3EBSgxUC>HYiv zS$ZAv&*Afo;`sg0qz&bhJkN~M0yM@SYD1(5H%`*&q-4wz%Ksc6kEnmH8B&D1xI|p8 z|3lQih!63MDK z5$5C)`%gWjUDQ5~@YfYjddb60-hXAATgXV;Z11k(Nq<#A>d(jjx#F8DcUu+{iBw-# z;B-hxR*wG?JeeX@TgN47e3IB#oY)WYzCb3%Hxx%Yr=U`XJ&{UWQH($Psuv9ODR_ww zjqwe|lm7M;yu^LQc*Ec(rRSEVwodFfIo}`?lRle*Uf3PkQgY6i@ph_h)2Y#z({U%#p@*9{u6l$>mul zl{mLoBK%zO^Z$r{0Ib+9^)s&Yq2hnh=~TB6?4Ro>ai$_d{GrB09{%Zp4b;Vn{U)8G zGR8Bm@<{P_VleW=kv_`sj9(vXpfUX%n<0+nV{McDXT0Yf#pm)m9fX_HU>HTYdM!tHZ z=1DHcOB`*^6S>i69{=MFDb*>LZ7M#Wzl--hp6|6`3&fLdJ^}w=%Ri}m=ZN%_IMQp1 zPo<2xdduTA3^!C0M~0NMC300`#oycVl5c~2fFaSIXqPX5??>w8__Oho*MCY57*Wdg Si@QDv|JL6gKga-C(fkYhB!0O7 literal 0 HcmV?d00001 diff --git a/tools/scripts/traffic_runner.sh b/tools/scripts/traffic_runner.sh new file mode 100755 index 0000000000..30ba7f4a90 --- /dev/null +++ b/tools/scripts/traffic_runner.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +#!/bin/bash +# Continuous traffic generator for OAI UE containers (no intervals, runs virtually forever). +# Usage examples: +# ./traffic_runner.sh --profile boring +# PROFILE=bulk_download ./traffic_runner.sh + +set -euo pipefail + +PROFILE="${PROFILE:-}" + +# Fixed server coordinates +IPERF_HOST_UDP="192.168.71.129" +IPERF_HOST_TCP="192.168.71.130" +IPERF_PORT_UDP=5201 +IPERF_PORT_TCP=5202 +OAITUN_IP="$(ip -o -4 addr show dev oaitun_ue1 | awk '{print $4}' | cut -d/ -f1)" + +# Very long duration to approximate "forever" +LONG_T="${LONG_T:-31536000}" # 1 year in seconds + +# -------------- args -------------- +if [[ $# -gt 0 ]]; then + case "$1" in + --profile=*) PROFILE="${1#--profile=}"; shift ;; + --profile) PROFILE="${2-}"; shift 2 ;; + -p=*) PROFILE="${1#-p=}"; shift ;; + -p) PROFILE="${2-}"; shift 2 ;; + -*) ;; # ignore other flags + *) PROFILE="$1"; shift ;; + esac +fi + +[[ -n "$PROFILE" ]] || { echo "ERROR: --profile is required" >&2; exit 2; } + +iperf_tcp_dl_forever() { ( iperf -c "$IPERF_HOST_TCP" -B "$OAITUN_IP" -p "$IPERF_PORT_TCP" -R -t "$LONG_T" >/dev/null 2>&1 ); } +iperf_tcp_ul_forever() { ( iperf -c "$IPERF_HOST_TCP" -B "$OAITUN_IP" -p "$IPERF_PORT_TCP" -t "$LONG_T" >/dev/null 2>&1 ); } +# iperf_udp_dl_forever() { ( nohup iperf -u -c "$IPERF_HOST_UDP" -B "$OAITUN_IP" -p "$IPERF_PORT_UDP" -R -t "$LONG_T" >/dev/null 2>&1 ); } +iperf_udp_dl_forever() { ( iperf -u -c "$IPERF_HOST_UDP" -B "$OAITUN_IP" -p "$IPERF_PORT_UDP" -R -b $1 -t "$LONG_T" >/dev/null 2>&1 ); } +iperf_udp_ul_forever() { ( iperf -u -c "$IPERF_HOST_UDP" -B "$OAITUN_IP" -p "$IPERF_PORT_UDP" -b $1 -t "$LONG_T" >/dev/null 2>&1 ); } + +# ---------------- profiles ---------------- +do_boring() { + iperf_udp_dl_forever 500K + wait + # iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 25M -t "$LONG_T" +} + +do_browsing() { + iperf_tcp_dl_forever + iperf_tcp_ul_forever + wait +} + +do_video() { + iperf_udp_dl_forever 6M + wait +} + +do_bulk_download() { + iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -R -P 8 -w 1M -t "$LONG_T" + wait +} + +do_voip() { + iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -R -u -b 80K -t "$LONG_T"; + iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 80K -t "$LONG_T"; + wait +} + +do_iot_chatty() { + iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -R -u -b 40K -t "$LONG_T"; + iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 20K -t "$LONG_T"; + wait +} + +do_gaming() { + iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -R -u -b 120K -t "$LONG_T"; + iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 10K -t "$LONG_T"; + wait +} + +do_idle() { + tail -f /dev/null +} + +# -------------- dispatch -------------- +case "$PROFILE" in + boring) do_boring;; + browsing) do_browsing;; + video) do_video;; + bulk_download) do_bulk_download;; + voip) do_voip;; + iot_chatty) do_iot_chatty;; + gaming) do_gaming;; + idle) do_idle;; + *) echo "Unknown profile: $PROFILE" >&2; exit 1;; +esac From 01db9857c46be674e86cfaa994e5001c26fa9e91 Mon Sep 17 00:00:00 2001 From: maki Date: Wed, 3 Dec 2025 13:51:53 +0900 Subject: [PATCH 275/275] dec3 --- ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.yaml | 11 +++++++++-- common/platform_constants.h | 4 ++-- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 6 +++--- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 8 ++++++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.yaml b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.yaml index 918bdc9802..e493fab8ca 100644 --- a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.yaml +++ b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.yaml @@ -1,5 +1,12 @@ Active_gNBs: - gnb-rfsim + +e2_agent: + enabled: "yes" + near_ric_ip_addr: 192.168.71.21 + e2ap_server_port: 36422 + sm_dir: "/usr/local/lib/flexric/" + # Asn1_verbosity, choice in: none, info, annoying Asn1_verbosity: none gNBs: @@ -139,13 +146,13 @@ gNBs: GNB_IPV4_ADDRESS_FOR_NGU: 192.168.71.140 GNB_PORT_FOR_S1U: 2152 # Spec 2152 - MACRLCs: - num_cc: 1 tr_s_preference: local_L1 tr_n_preference: local_RRC pusch_TargetSNRx10: 200 pucch_TargetSNRx10: 200 + min_grant_prb: 5 stats_max_ue: 17 L1s: @@ -193,6 +200,6 @@ log_config: mac_log_level: info rlc_log_level: info pdcp_log_level: info - rrc_log_level: info + rrc_log_level: debug ngap_log_level: debug f1ap_log_level: debug diff --git a/common/platform_constants.h b/common/platform_constants.h index 4000ffb001..4cfd83a369 100644 --- a/common/platform_constants.h +++ b/common/platform_constants.h @@ -83,8 +83,8 @@ #define MAX_NUM_SLICES 1024 // RLC Entity -#define RLC_TX_MAXSIZE 10000000 -#define RLC_RX_MAXSIZE 10000000 +#define RLC_TX_MAXSIZE 100000000 +#define RLC_RX_MAXSIZE 100000000 #define SEND_MRW_ON 240 #define MAX_ANT 8 // CBA constant diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 86eed9d575..a1814d5715 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -793,10 +793,10 @@ static void pf_dl(gNB_MAC_INST *mac, while (rbStart < rbStop && (rballoc_mask[rbStart + bwp_start] & slbitmap)) rbStart++; - uint16_t max_rbSize = 1; + uint16_t max_rbSize = 5; - while (rbStart + max_rbSize <= rbStop && !(rballoc_mask[rbStart + max_rbSize + bwp_start] & slbitmap)) - max_rbSize++; + /* while (rbStart + max_rbSize <= rbStop && !(rballoc_mask[rbStart + max_rbSize + bwp_start] & slbitmap)) */ + /* max_rbSize++; */ if (max_rbSize < min_rbSize) { LOG_D(NR_MAC, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 9904067aa5..90e2e502a8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -2173,12 +2173,16 @@ static int pf_ul(gNB_MAC_INST *nrmac, bwp_info_t bi = get_pusch_bwp_start_size(iterator->UE); while (rbStart < bi.bwpSize && (rballoc_mask[rbStart + bi.bwpStart] & slbitmap)) rbStart++; + /* if it's for inactivity, min_grant_prb is enough, otherwise check what * would be the maximum */ - uint16_t max_rbSize = iterator->sched_inactive ? min_rb : bi.bwpSize; + /* uint16_t max_rbSize = iterator->sched_inactive ? min_rb : bi.bwpSize; */ + uint16_t max_rbSize = min_rb; uint16_t available_rb = 1; while (rbStart + available_rb < bi.bwpSize && !(rballoc_mask[rbStart + bi.bwpStart + available_rb] & slbitmap) && available_rb < max_rbSize) - available_rb++; + available_rb++; + + /* LOG_I(NR_MAC, "[UE %04x] RB size: %u\n", iterator->UE->rnti, available_rb); */ if (rbStart + min_rb > bi.bwpSize || available_rb < min_rb) { reset_beam_status(&nrmac->beam_info, frame, slot, iterator->UE->UE_beam_index, slots_per_frame, dci_beam.new_beam);

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Ⓢd5{9V8+AeA5ody=LZj;oc(g2A3S_=X3Kqk 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%0D(A)THZS&iABE`%RBXxY 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븪 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