From 1707aafc2ca8ab46ca5d0b8ea9cd07de27f6ad68 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 13 Jan 2026 16:15:59 +0100 Subject: [PATCH 1/4] NR UE: make fp pointer to a _constant_ NR_DL_FRAME_PARMS in nr-ue.c --- executables/nr-ue.c | 22 ++++++++++--------- openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c | 2 +- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 0d1ae421f0..daacc443a3 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -216,7 +216,7 @@ static void UE_synch(void *arg) { LOG_I(PHY, "[UE thread Synch] Running Initial Synch \n"); uint64_t dl_carrier, ul_carrier; - NR_DL_FRAME_PARMS *fp = &UE->frame_parms; + const NR_DL_FRAME_PARMS *fp = &UE->frame_parms; nr_initial_sync_t ret = {false, 0, 0}; if (UE->sl_mode == 2) { fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params; @@ -298,7 +298,7 @@ static void RU_write(nr_rxtx_thread_data_t *rxtxD, bool sl_tx_action, c16_t **tx const fapi_nr_config_request_t *cfg = &UE->nrUE_config; const UE_nr_rxtx_proc_t *proc = &rxtxD->proc; - NR_DL_FRAME_PARMS *fp = &UE->frame_parms; + const NR_DL_FRAME_PARMS *fp = &UE->frame_parms; if (UE->sl_mode == 2) fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params; @@ -399,7 +399,7 @@ void processSlotTX(void *arg) LOG_D(PHY, "SlotTx %d.%d => slot type %d\n", proc->frame_tx, proc->nr_slot_tx, proc->tx_slot_type); - NR_DL_FRAME_PARMS *fp = &UE->frame_parms; + const NR_DL_FRAME_PARMS *fp = &UE->frame_parms; c16_t *txp[fp->nb_antennas_tx]; for (int i = 0; i < fp->nb_antennas_tx; i++) { txp[i] = UE->common_vars.txData[i] + get_samples_slot_timestamp(fp, proc->nr_slot_tx); @@ -533,7 +533,7 @@ static int UE_dl_preprocessing(PHY_VARS_NR_UE *UE, { TracyCZone(ctx, true); int sampleShift = INT_MAX; - NR_DL_FRAME_PARMS *fp = &UE->frame_parms; + const NR_DL_FRAME_PARMS *fp = &UE->frame_parms; if (UE->sl_mode == 2) fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params; @@ -621,7 +621,7 @@ void UE_dl_processing(void *arg) { void dummyWrite(PHY_VARS_NR_UE *UE, openair0_timestamp_t timestamp, int writeBlockSize) { - NR_DL_FRAME_PARMS *fp = &UE->frame_parms; + const NR_DL_FRAME_PARMS *fp = &UE->frame_parms; if (UE->sl_mode == 2) fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params; @@ -638,7 +638,7 @@ void dummyWrite(PHY_VARS_NR_UE *UE, openair0_timestamp_t timestamp, int writeBlo void readFrame(PHY_VARS_NR_UE *UE, openair0_timestamp_t *timestamp, int duration_rx_to_tx, bool toTrash) { - NR_DL_FRAME_PARMS *fp = &UE->frame_parms; + const NR_DL_FRAME_PARMS *fp = &UE->frame_parms; // two frames for initial sync int num_frames = 2; // In Sidelink worst case SL-SSB can be sent once in 16 frames @@ -681,7 +681,7 @@ void readFrame(PHY_VARS_NR_UE *UE, openair0_timestamp_t *timestamp, int duration static void syncInFrame(PHY_VARS_NR_UE *UE, openair0_timestamp_t *timestamp, int duration_rx_to_tx, openair0_timestamp_t rx_offset) { - NR_DL_FRAME_PARMS *fp = &UE->frame_parms; + const NR_DL_FRAME_PARMS *fp = &UE->frame_parms; if (UE->sl_mode == 2) fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params; @@ -704,11 +704,13 @@ static void syncInFrame(PHY_VARS_NR_UE *UE, openair0_timestamp_t *timestamp, int } } -static inline int get_firstSymSamp(uint16_t slot, NR_DL_FRAME_PARMS *fp) { +static inline int get_firstSymSamp(uint16_t slot, const NR_DL_FRAME_PARMS *fp) +{ return get_samples_symbol_duration(fp, slot, 0, 1); } -static inline int get_readBlockSize(uint16_t slot, NR_DL_FRAME_PARMS *fp) { +static inline int get_readBlockSize(uint16_t slot, const NR_DL_FRAME_PARMS *fp) +{ int rem_samples = get_samples_per_slot(slot, fp) - get_firstSymSamp(slot, fp); int next_slot_first_symbol = 0; if (slot < (fp->slots_per_frame-1)) @@ -720,11 +722,11 @@ void *UE_thread(void *arg) { //this thread should be over the processing thread to keep in real time PHY_VARS_NR_UE *UE = (PHY_VARS_NR_UE *)arg; + const NR_DL_FRAME_PARMS *fp = &UE->frame_parms; // int tx_enabled = 0; void *rxp[NB_ANTENNAS_RX]; enum stream_status_e stream_status = STREAM_STATUS_UNSYNC; fapi_nr_config_request_t *cfg = &UE->nrUE_config; - NR_DL_FRAME_PARMS *fp = &UE->frame_parms; sl_nr_phy_config_request_t *sl_cfg = NULL; if (UE->sl_mode == 2) { fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params; diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c index deae817a36..9a95bae820 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c @@ -140,7 +140,7 @@ void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_ // apply values based on the NTN-Config void apply_ntn_config(PHY_VARS_NR_UE *UE, - NR_DL_FRAME_PARMS *fp, + const NR_DL_FRAME_PARMS *fp, int hfn_rx, int frame_rx, int slot_rx, 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 b0df82c467..3daaaf0eee 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -331,7 +331,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t slot, c16_t **txData); void apply_ntn_config(PHY_VARS_NR_UE *UE, - NR_DL_FRAME_PARMS *fp, + const NR_DL_FRAME_PARMS *fp, int hfn_rx, int frame_rx, int slot_rx, From 09b3fc03c75b989e6ccab57ea74a9597b6e92645 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 13 Jan 2026 16:30:48 +0100 Subject: [PATCH 2/4] NR UE: make RX and TX pointer arrays of type c16_t instead of void in nr-ue.c --- executables/nr-ue.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index daacc443a3..2ed9de6c96 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -625,14 +625,13 @@ void dummyWrite(PHY_VARS_NR_UE *UE, openair0_timestamp_t timestamp, int writeBlo if (UE->sl_mode == 2) fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params; - void *dummy_tx[fp->nb_antennas_tx]; - // 2 because the function we call use pairs of int16_t implicitly as complex numbers - int16_t dummy_tx_data[2 * writeBlockSize]; + c16_t *dummy_tx[fp->nb_antennas_tx]; + c16_t dummy_tx_data[writeBlockSize]; memset(dummy_tx_data, 0, sizeof(dummy_tx_data)); for (int i = 0; i < fp->nb_antennas_tx; i++) dummy_tx[i] = dummy_tx_data; - int tmp = nrue_ru_write(UE, timestamp, dummy_tx, writeBlockSize, fp->nb_antennas_tx, 4); + int tmp = nrue_ru_write(UE, timestamp, (void **)dummy_tx, writeBlockSize, fp->nb_antennas_tx, 4); AssertFatal(writeBlockSize == tmp, ""); } @@ -647,9 +646,9 @@ void readFrame(PHY_VARS_NR_UE *UE, openair0_timestamp_t *timestamp, int duration num_frames = SL_NR_PSBCH_REPETITION_IN_FRAMES; } - void *rxp[NB_ANTENNAS_RX]; + c16_t *rxp[fp->nb_antennas_rx]; if (toTrash) { - rxp[0] = malloc16(get_samples_per_slot(0, fp) * 4); + rxp[0] = malloc16(get_samples_per_slot(0, fp) * sizeof(c16_t)); for (int i = 1; i < fp->nb_antennas_rx; i++) rxp[i] = rxp[0]; } @@ -658,11 +657,10 @@ void readFrame(PHY_VARS_NR_UE *UE, openair0_timestamp_t *timestamp, int duration for (int slot = 0; slot < fp->slots_per_subframe; slot++) { if (!toTrash) for (int i = 0; i < fp->nb_antennas_rx; i++) - rxp[i] = - ((void *)&UE->common_vars.rxdata[i][0]) + 4 * ((x * fp->samples_per_subframe) + get_samples_slot_timestamp(fp, slot)); + rxp[i] = &UE->common_vars.rxdata[i][x * fp->samples_per_subframe + get_samples_slot_timestamp(fp, slot)]; int read_block_size = get_samples_per_slot(slot, fp); - int tmp = nrue_ru_read(UE, timestamp, rxp, read_block_size, fp->nb_antennas_rx); + int tmp = nrue_ru_read(UE, timestamp, (void **)rxp, read_block_size, fp->nb_antennas_rx); UEscopeCopy(UE, ueTimeDomainSamplesBeforeSync, rxp[0], sizeof(c16_t), 1, read_block_size, 0); AssertFatal(read_block_size == tmp, ""); @@ -724,7 +722,7 @@ void *UE_thread(void *arg) PHY_VARS_NR_UE *UE = (PHY_VARS_NR_UE *)arg; const NR_DL_FRAME_PARMS *fp = &UE->frame_parms; // int tx_enabled = 0; - void *rxp[NB_ANTENNAS_RX]; + c16_t *rxp[fp->nb_antennas_rx]; enum stream_status_e stream_status = STREAM_STATUS_UNSYNC; fapi_nr_config_request_t *cfg = &UE->nrUE_config; sl_nr_phy_config_request_t *sl_cfg = NULL; @@ -945,7 +943,7 @@ void *UE_thread(void *arg) int firstSymSamp = get_firstSymSamp(slot_nr, fp); for (int i = 0; i < fp->nb_antennas_rx; i++) - rxp[i] = (void *)&UE->common_vars.rxdata[i][firstSymSamp + get_samples_slot_timestamp(fp, slot_nr)]; + rxp[i] = &UE->common_vars.rxdata[i][firstSymSamp + get_samples_slot_timestamp(fp, slot_nr)]; int iq_shift_to_apply = 0; if (slot_nr == nb_slot_frame - 1) { @@ -966,9 +964,9 @@ void *UE_thread(void *arg) const int readBlockSize = get_readBlockSize(slot_nr, fp) - iq_shift_to_apply; openair0_timestamp_t rx_timestamp; - int tmp = nrue_ru_read(UE, &rx_timestamp, rxp, readBlockSize, fp->nb_antennas_rx); + int tmp = nrue_ru_read(UE, &rx_timestamp, (void **)rxp, readBlockSize, fp->nb_antennas_rx); metadata meta = {.slot = curMsg.proc.nr_slot_rx, .frame = curMsg.proc.frame_rx}; - UEscopeCopyWithMetadata(UE, ueTimeDomainSamples, rxp[0] - firstSymSamp * sizeof(c16_t), sizeof(c16_t), 1, readBlockSize, 0, &meta); + UEscopeCopyWithMetadata(UE, ueTimeDomainSamples, rxp[0] - firstSymSamp, sizeof(c16_t), 1, readBlockSize, 0, &meta); AssertFatal(readBlockSize == tmp, ""); struct timespec current_time; if (clock_gettime(CLOCK_REALTIME, ¤t_time)) { From ba0ead6809aad15975ba4d5078275ceee0d931e2 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 13 Jan 2026 16:52:06 +0100 Subject: [PATCH 3/4] NR UE: correctly compute the writeBlockSize in readFrame() and the writeTimestamp in syncInFrame() --- executables/nr-ue.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 2ed9de6c96..ef53351047 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -654,21 +654,23 @@ void readFrame(PHY_VARS_NR_UE *UE, openair0_timestamp_t *timestamp, int duration } for (int x = 0; x < num_frames * NR_NUMBER_OF_SUBFRAMES_PER_FRAME; x++) { // two frames for initial sync - for (int slot = 0; slot < fp->slots_per_subframe; slot++) { + for (int slot_rx = 0; slot_rx < fp->slots_per_subframe; slot_rx++) { if (!toTrash) for (int i = 0; i < fp->nb_antennas_rx; i++) - rxp[i] = &UE->common_vars.rxdata[i][x * fp->samples_per_subframe + get_samples_slot_timestamp(fp, slot)]; + rxp[i] = &UE->common_vars.rxdata[i][x * fp->samples_per_subframe + get_samples_slot_timestamp(fp, slot_rx)]; - int read_block_size = get_samples_per_slot(slot, fp); - int tmp = nrue_ru_read(UE, timestamp, (void **)rxp, read_block_size, fp->nb_antennas_rx); - UEscopeCopy(UE, ueTimeDomainSamplesBeforeSync, rxp[0], sizeof(c16_t), 1, read_block_size, 0); - AssertFatal(read_block_size == tmp, ""); + int readBlockSize = get_samples_per_slot(slot_rx, fp); + int tmp = nrue_ru_read(UE, timestamp, (void **)rxp, readBlockSize, fp->nb_antennas_rx); + UEscopeCopy(UE, ueTimeDomainSamplesBeforeSync, rxp[0], sizeof(c16_t), 1, readBlockSize, 0); + AssertFatal(readBlockSize == tmp, ""); if (IS_SOFTMODEM_RFSIM) { + int slot_tx = (slot_rx + duration_rx_to_tx) % fp->slots_per_frame; + int writeBlockSize = get_samples_per_slot(slot_tx, fp); int ta = UE->timing_advance + UE->timing_advance_ntn; const openair0_timestamp_t writeTimestamp = - *timestamp + get_samples_slot_duration(fp, slot, duration_rx_to_tx) - UE->N_TA_offset - ta; - dummyWrite(UE, writeTimestamp, get_samples_per_slot(slot, fp)); + *timestamp + get_samples_slot_duration(fp, slot_rx, duration_rx_to_tx) - UE->N_TA_offset - ta; + dummyWrite(UE, writeTimestamp, writeBlockSize); } } } @@ -685,19 +687,18 @@ static void syncInFrame(PHY_VARS_NR_UE *UE, openair0_timestamp_t *timestamp, int LOG_I(PHY, "Resynchronizing RX by %ld samples\n", rx_offset); - int slot = 0; int size = rx_offset; while (size > 0) { - const int unitTransfer = min(get_samples_per_slot(slot, fp), size); + // Set a maximum transfer size. As we usually read/write single slots, we use the size of slot 0 as maximum here. + const int unitTransfer = min(get_samples_per_slot(0, fp), size); const int res = nrue_ru_read(UE, timestamp, (void **)UE->common_vars.rxdata, unitTransfer, fp->nb_antennas_rx); DevAssert(unitTransfer == res); if (IS_SOFTMODEM_RFSIM) { int ta = UE->timing_advance + UE->timing_advance_ntn; const openair0_timestamp_t writeTimestamp = - *timestamp + get_samples_slot_duration(fp, slot, duration_rx_to_tx) - UE->N_TA_offset - ta; + *timestamp + get_samples_slot_duration(fp, 0, duration_rx_to_tx) - UE->N_TA_offset - ta; dummyWrite(UE, writeTimestamp, unitTransfer); } - slot = (slot + 1) % fp->slots_per_subframe; size -= unitTransfer; } } From f7265977ad94ff12399e8acae64030201dfceaf4 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 13 Jan 2026 16:56:56 +0100 Subject: [PATCH 4/4] NR UE: when compensating for large timing_advance changes, set writeBlockSize to 0 instead of 1 --- executables/nr-ue.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index ef53351047..f66d11f4c3 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -294,6 +294,10 @@ static int nr_ue_slot_select(const fapi_nr_config_request_t *cfg, int nr_slot) static void RU_write(nr_rxtx_thread_data_t *rxtxD, bool sl_tx_action, c16_t **txp) { + int writeBlockSize = rxtxD->writeBlockSize; + if (writeBlockSize == 0) + return; + PHY_VARS_NR_UE *UE = rxtxD->UE; const fapi_nr_config_request_t *cfg = &UE->nrUE_config; const UE_nr_rxtx_proc_t *proc = &rxtxD->proc; @@ -353,7 +357,6 @@ static void RU_write(nr_rxtx_thread_data_t *rxtxD, bool sl_tx_action, c16_t **tx } openair0_timestamp_t writeTimestamp = proc->timestamp_tx; - int writeBlockSize = rxtxD->writeBlockSize; // if writeBlockSize gets longer that slot size, fill with dummy const int maxWriteBlockSize = get_samples_per_slot(proc->nr_slot_tx, fp); while (writeBlockSize > maxWriteBlockSize) { @@ -1010,9 +1013,9 @@ void *UE_thread(void *arg) UE->N_TA_offset = new_N_TA_offset; } if (writeBlockSize < 0) { - timing_advance += writeBlockSize - 1; - LOG_I(PHY, "writeBlockSize is %d, setting it to 1 and changing timing_advance to %d\n", writeBlockSize, timing_advance); - writeBlockSize = 1; + timing_advance += writeBlockSize; + LOG_I(PHY, "writeBlockSize is %d, setting it to 0 and changing timing_advance to %d\n", writeBlockSize, timing_advance); + writeBlockSize = 0; } if (curMsg.proc.nr_slot_rx == 0)