From 400926115e79ad900c1d1e6d1868989ef376e2e7 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 3 Apr 2025 13:30:28 +0000 Subject: [PATCH 1/6] NR UE: fix reestablishment for NTN During reestablishment the UE goes back to not synchronized state. While the synchronizatin is running, we trash frames using the function readFrame(). And when we successfully synchronize, we skip samples to align with the start of the frame using syncInFrame(). In rfsimulator mode, both of these functions send dummy data using the function dummyWrite(). Unfortunately, the writeTimestamp provided to that function was always assuming a duration from RX to TX of NR_UE_CAPABILITY_SLOT_RX_TO_TX. But in NTN mode, this value is changed when receiving SIB19. Therefore we have to consider this changed value instead of the static one. --- executables/nr-ue.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index d0a8f27a0f..345a12d99a 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -825,7 +825,8 @@ void dummyWrite(PHY_VARS_NR_UE *UE,openair0_timestamp timestamp, int writeBlockS AssertFatal(writeBlockSize == tmp, ""); } -void readFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp, bool toTrash) { +void readFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp, int duration_rx_to_tx, bool toTrash) +{ NR_DL_FRAME_PARMS *fp = &UE->frame_parms; // two frames for initial sync int num_frames = 2; @@ -853,7 +854,8 @@ void readFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp, bool toTrash) AssertFatal(read_block_size == tmp, ""); if (IS_SOFTMODEM_RFSIM) { - const openair0_timestamp writeTimestamp = *timestamp + fp->get_samples_slot_timestamp(slot, fp, NR_UE_CAPABILITY_SLOT_RX_TO_TX) - UE->N_TA_offset; + const openair0_timestamp writeTimestamp = + *timestamp + fp->get_samples_slot_timestamp(slot, fp, duration_rx_to_tx) - UE->N_TA_offset - UE->timing_advance; dummyWrite(UE, writeTimestamp, fp->get_samples_per_slot(slot, fp)); } } @@ -864,7 +866,7 @@ void readFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp, bool toTrash) free(rxp[i]); } -static void syncInFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp, openair0_timestamp rx_offset) +static void syncInFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp, int duration_rx_to_tx, openair0_timestamp rx_offset) { NR_DL_FRAME_PARMS *fp = &UE->frame_parms; if (UE->sl_mode == 2) @@ -879,7 +881,8 @@ static void syncInFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp, opena const int res = UE->rfdevice.trx_read_func(&UE->rfdevice, timestamp, (void **)UE->common_vars.rxdata, unitTransfer, fp->nb_antennas_rx); DevAssert(unitTransfer == res); if (IS_SOFTMODEM_RFSIM) { - const openair0_timestamp writeTimestamp = *timestamp + fp->get_samples_slot_timestamp(slot, fp, NR_UE_CAPABILITY_SLOT_RX_TO_TX) - UE->N_TA_offset; + const openair0_timestamp writeTimestamp = + *timestamp + fp->get_samples_slot_timestamp(slot, fp, duration_rx_to_tx) - UE->N_TA_offset - UE->timing_advance; dummyWrite(UE, writeTimestamp, unitTransfer); } slot = (slot + 1) % fp->slots_per_subframe; @@ -965,6 +968,10 @@ void *UE_thread(void *arg) notifiedFIFO_t freeBlocks; initNotifiedFIFO_nothreadSafe(&freeBlocks); + double ntn_init_time_drift = get_nrUE_params()->ntn_init_time_drift; + int ntn_koffset = 0; + + int duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX; int timing_advance = UE->timing_advance; UE->N_TA_offset = determine_N_TA_offset(UE); NR_UE_MAC_INST_t *mac = get_mac_inst(UE->Mod_id); @@ -988,13 +995,9 @@ void *UE_thread(void *arg) //warm up the RF board int64_t tmp; for (int i = 0; i < 50; i++) - readFrame(UE, &tmp, true); + readFrame(UE, &tmp, duration_rx_to_tx, true); } - double ntn_init_time_drift = get_nrUE_params()->ntn_init_time_drift; - int ntn_koffset = 0; - int duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX; - while (!oai_exit) { if (syncRunning) { notifiedFIFO_elt_t *res = pollNotifiedFIFO(&nf); @@ -1034,11 +1037,11 @@ void *UE_thread(void *arg) */ const unsigned int sync_in_frames = UE->rfdevice.openair0_cfg->recplay_conf->u_f_sync; while (trashed_frames != sync_in_frames) { - readFrame(UE, &sync_timestamp, true); + readFrame(UE, &sync_timestamp, duration_rx_to_tx, true); trashed_frames += 2; } } else { - readFrame(UE, &sync_timestamp, true); + readFrame(UE, &sync_timestamp, duration_rx_to_tx, true); trashed_frames += ((UE->sl_mode == 2) ? SL_NR_PSBCH_REPETITION_IN_FRAMES : 2); } continue; @@ -1048,7 +1051,7 @@ void *UE_thread(void *arg) AssertFatal(!syncRunning, "At this point synchronization can't be running\n"); if (!UE->is_synchronized) { - readFrame(UE, &sync_timestamp, false); + readFrame(UE, &sync_timestamp, duration_rx_to_tx, false); notifiedFIFO_elt_t *Msg = newNotifiedFIFO_elt(sizeof(syncData_t), 0, &nf, UE_synch); syncData_t *syncMsg = (syncData_t *)NotifiedFifoData(Msg); *syncMsg = (syncData_t){0}; @@ -1065,8 +1068,10 @@ void *UE_thread(void *arg) syncMsg->UE = UE; memset(&syncMsg->proc, 0, sizeof(syncMsg->proc)); pushNotifiedFIFO(&UE->sync_actor.fifo, Msg); - timing_advance = 0; - UE->timing_advance = 0; + if (ntn_koffset == 0) { // reset timing_advance for TN + timing_advance = 0; + UE->timing_advance = 0; + } trashed_frames = 0; syncRunning = true; continue; @@ -1074,7 +1079,7 @@ void *UE_thread(void *arg) if (stream_status == STREAM_STATUS_UNSYNC) { stream_status = STREAM_STATUS_SYNCING; - syncInFrame(UE, &sync_timestamp, intialSyncOffset); + syncInFrame(UE, &sync_timestamp, duration_rx_to_tx, intialSyncOffset); openair0_write_reorder_clear_context(&UE->rfdevice); if (get_nrUE_params()->time_sync_I) // ntn_init_time_drift is in µs/s, max_pos_acc * time_sync_I is in samples/frame @@ -1200,7 +1205,7 @@ void *UE_thread(void *arg) int writeBlockSize = fp->get_samples_per_slot((slot_nr + duration_rx_to_tx) % nb_slot_frame, fp) - iq_shift_to_apply; int new_timing_advance = UE->timing_advance; if (new_timing_advance != timing_advance) { - writeBlockSize -= new_timing_advance- timing_advance; + writeBlockSize -= new_timing_advance - timing_advance; timing_advance = new_timing_advance; } int new_N_TA_offset = determine_N_TA_offset(UE); From ffc0cd43a2d3ac0e6c0fc34b0982944f17ae0220 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 17 Jun 2025 14:11:57 +0000 Subject: [PATCH 2/6] for NTN GEO scenario increase ul_AM_RLC.t_PollRetransmit timer to 2 seconds --- .../conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf | 9 +++++++++ 1 file changed, 9 insertions(+) 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 fa1bcc20c8..7fbaac5f5b 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 @@ -195,6 +195,15 @@ gNBs = } ); +rlc = { + srb = { + t_poll_retransmit = "ms2000" + } + drb_am = { + t_poll_retransmit = "ms2000" + } +} + MACRLCs = ( { num_cc = 1; From 0ddba908748d83d62451d8ef9599bb96acd2d72e Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 17 Jun 2025 14:59:12 +0000 Subject: [PATCH 3/6] ci: test reestablishment for NTN GEO --- .../xml_files/container_5g_rfsim_ntn_geo.xml | 32 +++++++++++++++++++ .../docker-compose.yaml | 4 ++- 2 files changed, 35 insertions(+), 1 deletion(-) 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 72a6836882..32cc3b1f58 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_ntn_geo.xml @@ -33,6 +33,11 @@ 000003 020001 020002 + 040001 + 020022 + 040002 + 020001 + 020002 100001 222222 @@ -98,6 +103,33 @@ 5 + + Custom_Command + Trigger Reestablishment + cacofonix + echo ci force_reestab | nc 192.168.71.140 9090 | grep -E 'Reset RLC counters of UE RNTI [0-9a-f]{4} to trigger reestablishment' + yes + + + + Ping + Ping ext-dn from NR-UE + rfsim5g_ue + cacofonix + rfsim5g_ext_dn + cacofonix + -c 20 -i0.5 -w25 + 80 + + + + Custom_Command + Verify Reestablishment + cacofonix + echo ci get_reestab_count | nc 192.168.71.140 9090 | grep -E 'UE RNTI [0-9a-f]{4} reestab 1' + yes + + Undeploy_Object true 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 31390cde9c..eefcb37ba1 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 @@ -87,7 +87,9 @@ services: - ALL environment: USE_ADDITIONAL_OPTIONS: --rfsim --log_config.global_log_options level,nocolor,time - ASAN_OPTIONS: detect_leaks=0 + --telnetsrv --telnetsrv.listenaddr 192.168.71.140 + --telnetsrv.shrmod ci + ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 depends_on: - oai-ext-dn networks: From c3a11c4717fccbf943ef02e6058b07b72e7fc34b Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 24 Jun 2025 14:28:09 +0000 Subject: [PATCH 4/6] NR UE: fix condition when to print the error message in write_reorder_clear_context() We should print the error message when the mutex cannot be locked because it is already locked. Unfortunately, the if() condition was inverted... --- radio/COMMON/common_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/COMMON/common_lib.c b/radio/COMMON/common_lib.c index 14b66d7060..670a9cc852 100644 --- a/radio/COMMON/common_lib.c +++ b/radio/COMMON/common_lib.c @@ -276,7 +276,7 @@ void openair0_write_reorder_clear_context(openair0_device *device) re_order_t *ctx = &device->reOrder; if (!ctx->initDone) return; - if (pthread_mutex_trylock(&ctx->mutex_write) == 0) + if (pthread_mutex_trylock(&ctx->mutex_write) != 0) LOG_E(HW, "write_reorder_clear_context call while still writing on the device\n"); pthread_mutex_destroy(&ctx->mutex_write); pthread_mutex_lock(&ctx->mutex_store); From 5856c897412261606ffab015ac5298bbbbbc8563 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 24 Jun 2025 14:33:35 +0000 Subject: [PATCH 5/6] NR UE: do not select a suspended LCID for scheduling This fixes a lifelock in nr_ue_get_sdu(), because select_logical_channels() would select a suspended LCID for scheduling, but fill_mac_sdu() would not provide data for it. --- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 2c37b21f5b..23e9cb04e8 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -2539,10 +2539,13 @@ static uint select_logical_channels(NR_UE_MAC_INST_t *mac, nr_lcordered_info_t * int nb = 0; // selection of logical channels with Bj > 0 for (int i = 0; i < mac->lc_ordered_list.count; i++) { - int lcid = mac->lc_ordered_list.array[i]->lcid; + nr_lcordered_info_t *lc_info = mac->lc_ordered_list.array[i]; + if (lc_info->rb_suspended) + continue; + int lcid = lc_info->lcid; NR_LC_SCHEDULING_INFO *sched_info = get_scheduling_info_from_lcid(mac, lcid); if (sched_info->Bj > 0) { - active_lcids[nb++] = *mac->lc_ordered_list.array[i]; + active_lcids[nb++] = *lc_info; LOG_D(NR_MAC, "The available lcid is %d with total active channels count = %d\n", lcid, nb); } } From ed54e0bbd120e8abf2084577f9c0926cb6aa6676 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 24 Jun 2025 14:38:27 +0000 Subject: [PATCH 6/6] NR_UE: make lcids_bj_pos an array of nr_lcordered_info_t pointers instead of structure elements This makes the array itself smaller and allows to store just a pointer in select_logical_channels() instead of copying the complete structure element. --- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 23e9cb04e8..5539ce08ef 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -2466,13 +2466,13 @@ static void nr_ue_get_sdu_mac_ce_post(NR_UE_MAC_INST_t *mac, mac_ce_p->cur_ptr += size; } -uint32_t get_count_lcids_same_priority(uint8_t start, uint8_t total_active_lcids, nr_lcordered_info_t *lcid_ordered_array) +static uint32_t get_count_lcids_same_priority(uint8_t start, uint8_t total_active_lcids, nr_lcordered_info_t *lcid_ordered_array[]) { // count number of logical channels with same priority as curr_lcid uint8_t same_priority_count = 0; - uint8_t curr_lcid = lcid_ordered_array[start].lcid; + uint8_t curr_lcid = lcid_ordered_array[start]->lcid; for (uint8_t index = start; index < total_active_lcids; index++) { - if (lcid_ordered_array[start].priority == lcid_ordered_array[index].priority) { + if (lcid_ordered_array[start]->priority == lcid_ordered_array[index]->priority) { same_priority_count++; } } @@ -2520,11 +2520,13 @@ static long get_num_bytes_to_reqlc(NR_UE_MAC_INST_t *mac, return num_bytes_requested; } -bool get_dataavailability_buffers(uint8_t total_active_lcids, nr_lcordered_info_t *lcid_ordered_array, bool *data_status_lcbuffers) +static bool get_dataavailability_buffers(uint8_t total_active_lcids, + nr_lcordered_info_t *lcid_ordered_array[], + bool *data_status_lcbuffers) { // check whether there is any data in the rlc buffer corresponding to active lcs for (uint8_t id = 0; id < total_active_lcids; id++) { - int lcid = lcid_ordered_array[id].lcid; + int lcid = lcid_ordered_array[id]->lcid; if (data_status_lcbuffers[lcid_buffer_index(lcid)]) { return true; } @@ -2532,7 +2534,7 @@ bool get_dataavailability_buffers(uint8_t total_active_lcids, nr_lcordered_info_ return false; } -static uint select_logical_channels(NR_UE_MAC_INST_t *mac, nr_lcordered_info_t *active_lcids) +static uint select_logical_channels(NR_UE_MAC_INST_t *mac, nr_lcordered_info_t *active_lcids[]) { // (TODO: selection of logical channels for logical channel prioritization procedure as per 5.4.3.1.2 Selection of logical // channels, TS38.321) @@ -2545,7 +2547,7 @@ static uint select_logical_channels(NR_UE_MAC_INST_t *mac, nr_lcordered_info_t * int lcid = lc_info->lcid; NR_LC_SCHEDULING_INFO *sched_info = get_scheduling_info_from_lcid(mac, lcid); if (sched_info->Bj > 0) { - active_lcids[nb++] = *lc_info; + active_lcids[nb++] = lc_info; LOG_D(NR_MAC, "The available lcid is %d with total active channels count = %d\n", lcid, nb); } } @@ -2734,7 +2736,7 @@ static uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, LOG_E(NR_MAC, "Failed to init lcids_bj_pos: mac->lc_ordered_list.count = 0\n"); return 0; } - nr_lcordered_info_t lcids_bj_pos[mac->lc_ordered_list.count]; + nr_lcordered_info_t *lcids_bj_pos[mac->lc_ordered_list.count]; int avail_lcids_count = select_logical_channels(mac, lcids_bj_pos); // multiplex in the order of highest priority @@ -2755,7 +2757,7 @@ static uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, still space availble in the MAC PDU, then from the next run all the remaining data from the higher priority logical channel is placed in the MAC PDU before going on to next high priority logical channel */ - int lcid = lcids_bj_pos[id].lcid; + int lcid = lcids_bj_pos[id]->lcid; int idx = lcid_buffer_index(lcid); // skip the logical channel if no data in the buffer initially or the data in the buffer was zero because it was written in to // MAC PDU