From b59bcaf49453e9ab6f7d5615a8d3d50f9a739110 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 23 Sep 2025 14:07:14 +0200 Subject: [PATCH 01/10] NR UE: provide velocity vector down to nr-ue.c, preparing for Doppler calculation --- executables/nr-ue.c | 43 ++++++++++++++----- .../nfapi/public_inc/fapi_nr_ue_interface.h | 8 +++- openair1/SCHED_NR_UE/fapi_nr_ue_l1.c | 2 + openair2/LAYER2/NR_MAC_UE/config_ue.c | 42 ++++++++++++++---- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 8 +++- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 2 + 6 files changed, 81 insertions(+), 24 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index f57b663f1e..a1d7ef2888 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -732,15 +732,32 @@ static inline void apply_ntn_timing_advance(PHY_VARS_NR_UE *UE, const NR_DL_FRAM + ntn_config_params->epoch_hfn * 10240; const int ms_since_epoch = abs_subframe_tx - abs_subframe_epoch; - const double omega = ntn_config_params->omega; - const double cos_wt = cos(omega * ms_since_epoch); - const double sin_wt = sin(omega * ms_since_epoch); - const position_t pos_sat_0 = ntn_config_params->pos_sat_0; - const position_t pos_sat_90 = ntn_config_params->pos_sat_90; - const position_t pos_sat = {pos_sat_0.X * cos_wt + pos_sat_90.X * sin_wt, - pos_sat_0.Y * cos_wt + pos_sat_90.Y * sin_wt, - pos_sat_0.Z * cos_wt + pos_sat_90.Z * sin_wt}; + const position_t vel_sat_0 = ntn_config_params->vel_sat_0; + + position_t pos_sat; + position_t vel_sat; + + const double omega = ntn_config_params->omega; + if (omega) { + const double cos_wt = cos(omega * ms_since_epoch); + const double sin_wt = sin(omega * ms_since_epoch); + + const position_t pos_sat_90 = ntn_config_params->pos_sat_90; + pos_sat = (position_t){pos_sat_0.X * cos_wt + pos_sat_90.X * sin_wt, + pos_sat_0.Y * cos_wt + pos_sat_90.Y * sin_wt, + pos_sat_0.Z * cos_wt + pos_sat_90.Z * sin_wt}; + + const position_t vel_sat_90 = ntn_config_params->vel_sat_90; + vel_sat = (position_t){vel_sat_0.X * cos_wt + vel_sat_90.X * sin_wt, + vel_sat_0.Y * cos_wt + vel_sat_90.Y * sin_wt, + vel_sat_0.Z * cos_wt + vel_sat_90.Z * sin_wt}; + } else { + pos_sat = (position_t){pos_sat_0.X + vel_sat_0.X * ms_since_epoch / 1000, + pos_sat_0.Y + vel_sat_0.Y * ms_since_epoch / 1000, + pos_sat_0.Z + vel_sat_0.Z * ms_since_epoch / 1000}; + vel_sat = vel_sat_0; + } position_t pos_ue = {0}; get_position_coordinates(UE->Mod_id, &pos_ue); @@ -751,6 +768,9 @@ static inline void apply_ntn_timing_advance(PHY_VARS_NR_UE *UE, const NR_DL_FRAM // calculate distance between SAT and UE const double distance = sqrt(dir_sat_ue.X * dir_sat_ue.X + dir_sat_ue.Y * dir_sat_ue.Y + dir_sat_ue.Z * dir_sat_ue.Z); + // calculate projected velocity from SAT towards UE + const double vel_sat_ue = (vel_sat.X * dir_sat_ue.X + vel_sat.Y * dir_sat_ue.Y + vel_sat.Z * dir_sat_ue.Z) / distance; + // calculate round-trip-time (factor 2) between SAT and UE in ms (factor 1000) const double N_UE_TA_adj = 2000 * distance / SPEED_OF_LIGHT; @@ -764,14 +784,15 @@ static inline void apply_ntn_timing_advance(PHY_VARS_NR_UE *UE, const NR_DL_FRAM * fp->samples_per_subframe; LOG_D(PHY, - "N_UE_TA_adj = %f ms, N_common_ta_adj = %f ms, N_common_ta_drift = %f µs/s, N_common_ta_drift_variant = %f µs/s², ms_since_epoch = %d ms, " - "computed timing_advance_ntn = %d samples\n", + "N_UE_TA_adj = %f ms, N_common_ta_adj = %f ms, N_common_ta_drift = %f µs/s, N_common_ta_drift_variant = %f µs/s², " + "ms_since_epoch = %d ms, computed timing_advance_ntn = %d samples, satellite velocity towards UE = %f m/s\n", N_UE_TA_adj, N_common_ta_adj, N_common_ta_drift, N_common_ta_drift_variant, ms_since_epoch, - UE->timing_advance_ntn); + UE->timing_advance_ntn, + vel_sat_ue); } static inline void apply_ntn_config(PHY_VARS_NR_UE *UE, 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 37337e82d3..49752be093 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 @@ -569,10 +569,14 @@ typedef struct { // orbital angular velocity in rad/ms double omega; - // satellite position at epoch time + // satellite position vector at epoch time position_t pos_sat_0; - // satellite position at 90° orbit + // satellite position vector at 90° orbit position_t pos_sat_90; + // satellite velocity vector at epoch time + position_t vel_sat_0; + // satellite velocity vector at 90° orbit + position_t vel_sat_90; // N_common_ta_adj represents common round-trip-time between gNB and SAT received in SIB19 (ms) double N_common_ta_adj; diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c index 5c679630f4..92a0b043e7 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -102,6 +102,8 @@ static void configure_ntn_params(PHY_VARS_NR_UE *ue, fapi_nr_dl_ntn_config_comma ue->ntn_config_message->ntn_config_params.omega = ntn_params_message->omega; ue->ntn_config_message->ntn_config_params.pos_sat_0 = ntn_params_message->pos_sat_0; ue->ntn_config_message->ntn_config_params.pos_sat_90 = ntn_params_message->pos_sat_90; + ue->ntn_config_message->ntn_config_params.vel_sat_0 = ntn_params_message->vel_sat_0; + ue->ntn_config_message->ntn_config_params.vel_sat_90 = ntn_params_message->vel_sat_90; ue->ntn_config_message->ntn_config_params.N_common_ta_adj = ntn_params_message->N_common_ta_adj; ue->ntn_config_message->ntn_config_params.N_common_ta_drift = ntn_params_message->N_common_ta_drift; diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index c92c8225f6..b14097a3ec 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -279,29 +279,49 @@ static void prepare_ue_sat_ta(const NR_PositionVelocity_r17_t *sat_pos, ntn_timi const double vel_sat_2 = vel_sat.X * vel_sat.X + vel_sat.Y * vel_sat.Y + vel_sat.Z * vel_sat.Z; const double vel_mag = sqrt(vel_sat_2); - // calculate angular velocity in rad/ms - const double omega = vel_mag / (radius * 1000); + double omega; // angular velocity in rad/ms + position_t pos_sat_90; // sat position vector in 90° orbit + position_t vel_sat_90; // sat velocity vector in 90° orbit - // calculate sat position in 90° orbit - position_t pos_sat_90 = pos_sat; - if (vel_mag) { - const double scaling = radius / vel_mag; + // assuming circular orbit when satellite moves faster than 1000 m/s + // 1000 m/s are chosen because according to Wikipedia, this seems to be a reasonable minimal + // orbital velocity: https://en.wikipedia.org/wiki/Orbital_speed#Tangential_velocities_at_altitude + if (vel_mag > 1000) { + omega = vel_mag / (radius * 1000); + double scaling = radius / vel_mag; pos_sat_90 = (position_t){vel_sat.X * scaling, vel_sat.Y * scaling, vel_sat.Z * scaling}; + scaling = -vel_mag / radius; + vel_sat_90 = (position_t){pos_sat.X * scaling, pos_sat.Y * scaling, pos_sat.Z * scaling}; + } else { + omega = 0; + pos_sat_90 = pos_sat; + vel_sat_90 = vel_sat; } LOG_I(NR_MAC, - "Satellite angular velocity = %e rad/ms, sat_pos = {%f, %f, %f}, sat_pos_90 = {%f, %f, %f}\n", - omega, + "Satellite orbital radius %f m, pos_sat_0 = {%f, %f, %f}, pos_sat_90 = {%f, %f, %f}\n" + "Satellite velocity %f m/s, angular velocity = %e rad/ms, vel_sat_0 = {%f, %f, %f}, vel_sat_90 = {%f, %f, %f}\n", + radius, pos_sat.X, pos_sat.Y, pos_sat.Z, pos_sat_90.X, pos_sat_90.Y, - pos_sat_90.Z); + pos_sat_90.Z, + vel_mag, + omega, + vel_sat.X, + vel_sat.Y, + vel_sat.Z, + vel_sat_90.X, + vel_sat_90.Y, + vel_sat_90.Z); ntn_ta->omega = omega; ntn_ta->pos_sat_0 = pos_sat; ntn_ta->pos_sat_90 = pos_sat_90; + ntn_ta->vel_sat_0 = vel_sat; + ntn_ta->vel_sat_90 = vel_sat_90; } // populate ntn_ta structure from mac @@ -332,11 +352,15 @@ static void configure_ntn_ta(ntn_timing_advance_componets_t *ntn_ta, const NR_NT ntn_ta->omega = 0; ntn_ta->pos_sat_0 = (position_t){0, 0, 0}; ntn_ta->pos_sat_90 = (position_t){0, 0, 0}; + ntn_ta->vel_sat_0 = (position_t){0, 0, 0}; + ntn_ta->vel_sat_90 = (position_t){0, 0, 0}; } } else { // Need R - Release if not present ntn_ta->omega = 0; ntn_ta->pos_sat_0 = (position_t){0, 0, 0}; ntn_ta->pos_sat_90 = (position_t){0, 0, 0}; + ntn_ta->vel_sat_0 = (position_t){0, 0, 0}; + ntn_ta->vel_sat_90 = (position_t){0, 0, 0}; } // handle cellSpecificKoffset_r17 diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index 7fda4a4b6c..42b616c188 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -552,10 +552,14 @@ typedef struct ntn_timing_advance_components { // orbital angular velocity in rad/ms double omega; - // satellite position at epoch time + // satellite position vector at epoch time position_t pos_sat_0; - // satellite position at 90° orbit + // satellite position vector at 90° orbit position_t pos_sat_90; + // satellite velocity vector at epoch time + position_t vel_sat_0; + // satellite velocity vector at 90° orbit + position_t vel_sat_90; // N_common_ta_adj represents common round-trip-time between gNB and SAT received in SIB19 (ms) double N_common_ta_adj; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index c8d40e7f11..f7a763f41a 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -2855,6 +2855,8 @@ static void schedule_ntn_config_command(fapi_nr_dl_config_request_t *dl_config, ntn_config_command_pdu->omega = mac->ntn_ta.omega; ntn_config_command_pdu->pos_sat_0 = mac->ntn_ta.pos_sat_0; ntn_config_command_pdu->pos_sat_90 = mac->ntn_ta.pos_sat_90; + ntn_config_command_pdu->vel_sat_0 = mac->ntn_ta.vel_sat_0; + ntn_config_command_pdu->vel_sat_90 = mac->ntn_ta.vel_sat_90; ntn_config_command_pdu->N_common_ta_adj = mac->ntn_ta.N_common_ta_adj; ntn_config_command_pdu->N_common_ta_drift = mac->ntn_ta.N_common_ta_drift; From bc81662c2a7b31c0b0597b3941fed0ea42dee59d Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 23 Sep 2025 17:22:30 +0200 Subject: [PATCH 02/10] NR UE: consider computed DL/UL Doppler shift in continuous FO compensation --- executables/nr-ue.c | 77 +++++++++++++++++++-------- openair1/PHY/MODULATION/slot_fep_nr.c | 2 +- openair1/PHY/defs_nr_UE.h | 6 ++- 3 files changed, 59 insertions(+), 26 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index a1d7ef2888..f3769096f8 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -258,7 +258,7 @@ static void UE_synch(void *arg) { + round((float)((ret.rx_offset << 1) % fp->samples_per_subframe) / fp->samples_per_slot0); if (get_nrUE_params()->cont_fo_comp) { - UE->freq_offset = freq_offset; + UE->freq_offset = freq_offset - UE->dl_Doppler_shift; } else { // rerun with new cell parameters and frequency-offset nr_rf_card_config_freq(cfg0, ul_carrier, dl_carrier, freq_offset); @@ -400,7 +400,12 @@ static void RU_write(nr_rxtx_thread_data_t *rxtxD, bool sl_tx_action, c16_t **tx if (get_nrUE_params()->cont_fo_comp == 2) // different from LO frequency error compensation, Doppler UL pre-compensation has to be negative ul_freq_offset = -ul_freq_offset; for (int i = 0; i < fp->nb_antennas_tx; i++) - nr_fo_compensation(ul_freq_offset, fp->samples_per_subframe, writeTimestamp, txp[i], txp[i], writeBlockSize); + nr_fo_compensation(UE->ul_Doppler_shift + ul_freq_offset, + fp->samples_per_subframe, + writeTimestamp, + txp[i], + txp[i], + writeBlockSize); } int tmp = openair0_write_reorder(&UE->rfdevice, writeTimestamp, (void **)txp, writeBlockSize, fp->nb_antennas_tx, flags); @@ -723,10 +728,18 @@ static inline int get_readBlockSize(uint16_t slot, NR_DL_FRAME_PARMS *fp) { } #define SPEED_OF_LIGHT 299792458 -static inline void apply_ntn_timing_advance(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_PARMS *fp, int abs_subframe_tx) +static void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_PARMS *fp, int abs_subframe_tx) { const fapi_nr_dl_ntn_config_command_pdu *ntn_config_params = &UE->ntn_config_message->ntn_config_params; + // Handle terrestrial networks + if (ntn_config_params->cell_specific_k_offset == 0) { + UE->timing_advance_ntn = 0; + UE->dl_Doppler_shift = 0; + UE->ul_Doppler_shift = 0; + return; + } + const int abs_subframe_epoch = ntn_config_params->epoch_subframe + ntn_config_params->epoch_sfn * 10 + ntn_config_params->epoch_hfn * 10240; @@ -771,6 +784,12 @@ static inline void apply_ntn_timing_advance(PHY_VARS_NR_UE *UE, const NR_DL_FRAM // calculate projected velocity from SAT towards UE const double vel_sat_ue = (vel_sat.X * dir_sat_ue.X + vel_sat.Y * dir_sat_ue.Y + vel_sat.Z * dir_sat_ue.Z) / distance; + // calculate DL Doppler shift (moving source) + const double dl_Doppler_shift = (vel_sat_ue / (SPEED_OF_LIGHT - vel_sat_ue)) * fp->dl_CarrierFreq; + + // calculate UL Doppler shift (moving target) + const double ul_Doppler_shift = (vel_sat_ue / SPEED_OF_LIGHT) * fp->ul_CarrierFreq; + // calculate round-trip-time (factor 2) between SAT and UE in ms (factor 1000) const double N_UE_TA_adj = 2000 * distance / SPEED_OF_LIGHT; @@ -783,32 +802,33 @@ static inline void apply_ntn_timing_advance(PHY_VARS_NR_UE *UE, const NR_DL_FRAM + N_common_ta_drift_variant * ((int64_t)ms_since_epoch * ms_since_epoch) / 1e9) * fp->samples_per_subframe; + UE->freq_offset -= dl_Doppler_shift - UE->dl_Doppler_shift; + UE->dl_Doppler_shift = dl_Doppler_shift; + UE->ul_Doppler_shift = ul_Doppler_shift; + LOG_D(PHY, - "N_UE_TA_adj = %f ms, N_common_ta_adj = %f ms, N_common_ta_drift = %f µs/s, N_common_ta_drift_variant = %f µs/s², " - "ms_since_epoch = %d ms, computed timing_advance_ntn = %d samples, satellite velocity towards UE = %f m/s\n", - N_UE_TA_adj, - N_common_ta_adj, - N_common_ta_drift, - N_common_ta_drift_variant, - ms_since_epoch, - UE->timing_advance_ntn, - vel_sat_ue); + "satellite velocity towards UE = %f m/s, DL Doppler shift = %f kHz, UL Doppler shift = %f kHz\n", + vel_sat_ue, + dl_Doppler_shift / 1000, + ul_Doppler_shift / 1000); } -static inline void apply_ntn_config(PHY_VARS_NR_UE *UE, - NR_DL_FRAME_PARMS *fp, - int hfn_rx, - int frame_rx, - int slot_rx, - int *duration_rx_to_tx, - int *timing_advance, - int *ntn_koffset) +static void apply_ntn_config(PHY_VARS_NR_UE *UE, + NR_DL_FRAME_PARMS *fp, + int hfn_rx, + int frame_rx, + int slot_rx, + int *duration_rx_to_tx, + int *timing_advance, + int *ntn_koffset) { if (UE->ntn_config_message->update) { UE->ntn_config_message->update = false; + const fapi_nr_dl_ntn_config_command_pdu *ntn_config_params = &UE->ntn_config_message->ntn_config_params; + const int mu = fp->numerology_index; - const int koffset = UE->ntn_config_message->ntn_config_params.cell_specific_k_offset; + const int koffset = ntn_config_params->cell_specific_k_offset; *duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + (koffset << mu); if (koffset > *ntn_koffset) @@ -818,7 +838,18 @@ static inline void apply_ntn_config(PHY_VARS_NR_UE *UE, *ntn_koffset = koffset; const int abs_subframe_tx = 10240 * hfn_rx + 10 * frame_rx + ((slot_rx + *duration_rx_to_tx) >> mu); - apply_ntn_timing_advance(UE, fp, abs_subframe_tx); + apply_ntn_timing_advance_and_doppler(UE, fp, abs_subframe_tx); + + LOG_I(PHY, + "k_offset: %dms, N_Common_Ta: %fms, drift: %fµs/s, variant: %fµs/s², " + "timing_advance_ntn: %d samples, DL Doppler shift: %fkHz, UL Doppler shift: %fkHz\n", + koffset << mu, + ntn_config_params->N_common_ta_adj, + ntn_config_params->N_common_ta_drift, + ntn_config_params->N_common_ta_drift_variant, + UE->timing_advance_ntn, + UE->dl_Doppler_shift / 1000, + UE->ul_Doppler_shift / 1000); } } @@ -1050,7 +1081,7 @@ void *UE_thread(void *arg) // Calculate new TA based on SIB19 information for each subframe in NTN mode, if "autonomous_ta" is not enabled if (ntn_koffset && !get_nrUE_params()->autonomous_ta && (absolute_slot + duration_rx_to_tx) % fp->slots_per_subframe == 0) { const int abs_subframe_tx = (absolute_slot + duration_rx_to_tx) / fp->slots_per_subframe; - apply_ntn_timing_advance(UE, fp, abs_subframe_tx); + apply_ntn_timing_advance_and_doppler(UE, fp, abs_subframe_tx); } const int readBlockSize = get_readBlockSize(slot_nr, fp) - iq_shift_to_apply; diff --git a/openair1/PHY/MODULATION/slot_fep_nr.c b/openair1/PHY/MODULATION/slot_fep_nr.c index 9045e50cb0..58e47fc13a 100644 --- a/openair1/PHY/MODULATION/slot_fep_nr.c +++ b/openair1/PHY/MODULATION/slot_fep_nr.c @@ -119,7 +119,7 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue, if (ue && ue->cont_fo_comp) { start_meas_nr_ue_phy(ue, RX_FO_COMPENSATION_STATS); - nr_fo_compensation(ue->freq_offset, + nr_fo_compensation(ue->dl_Doppler_shift + ue->freq_offset, frame_parms->samples_per_subframe, rx_offset, rxdata_symb_ptr[aa], diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index f266d7aad5..5e2e169c1a 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -462,8 +462,10 @@ typedef struct PHY_VARS_NR_UE_s { double initial_fo; /// initial frequency offset provided by the user int cont_fo_comp; /// flag enabling the continuous frequency offset estimation and compensation - double freq_offset; /// currently compensated frequency offset - double freq_off_acc; /// accumulated frequency error (for PI controller) + double freq_offset; /// currently compensated DL frequency offset (without dl_Doppler_shift) + double freq_off_acc; /// accumulated DL frequency error (for PI controller) + double dl_Doppler_shift; /// calculated DL Doppler shift + double ul_Doppler_shift; /// calculated UL Doppler shift /// Timing Advance updates variables /// Timing advance update computed from the TA command signalled from gNB From ae12190a9e573588d64c3ae74c097d9c441e55dd Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Wed, 24 Sep 2025 14:17:33 +0200 Subject: [PATCH 03/10] NR UE: improve log output in MAC --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 62 ++++++++++++++------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index b14097a3ec..ded7c2a754 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -299,15 +299,17 @@ static void prepare_ue_sat_ta(const NR_PositionVelocity_r17_t *sat_pos, ntn_timi } LOG_I(NR_MAC, - "Satellite orbital radius %f m, pos_sat_0 = {%f, %f, %f}, pos_sat_90 = {%f, %f, %f}\n" - "Satellite velocity %f m/s, angular velocity = %e rad/ms, vel_sat_0 = {%f, %f, %f}, vel_sat_90 = {%f, %f, %f}\n", + "Satellite orbital radius %f m, pos_sat_0 = {%f, %f, %f}, pos_sat_90 = {%f, %f, %f}\n", radius, pos_sat.X, pos_sat.Y, pos_sat.Z, pos_sat_90.X, pos_sat_90.Y, - pos_sat_90.Z, + pos_sat_90.Z); + + LOG_I(NR_MAC, + "Satellite velocity %f m/s, angular velocity = %e rad/ms, vel_sat_0 = {%f, %f, %f}, vel_sat_90 = {%f, %f, %f}\n", vel_mag, omega, vel_sat.X, @@ -340,29 +342,6 @@ static void configure_ntn_ta(ntn_timing_advance_componets_t *ntn_ta, const NR_NT ntn_ta->epoch_sfn = epoch_time_r17->sfn_r17; ntn_ta->epoch_subframe = epoch_time_r17->subFrameNR_r17; - // handle ephemerisInfo_r17 - const NR_EphemerisInfo_r17_t *ephemeris_info = ntn_Config_r17->ephemerisInfo_r17; - if (ephemeris_info) { - if (ephemeris_info->present == NR_EphemerisInfo_r17_PR_positionVelocity_r17) { - const NR_PositionVelocity_r17_t *position_velocity = ephemeris_info->choice.positionVelocity_r17; - AssertFatal(position_velocity, "position_velocity should not be NULL here\n"); - prepare_ue_sat_ta(position_velocity, ntn_ta); - } else { - LOG_W(NR_MAC, "NR UE currently supports only ephemerisInfo_r17 of type positionVelocity_r17\n"); - ntn_ta->omega = 0; - ntn_ta->pos_sat_0 = (position_t){0, 0, 0}; - ntn_ta->pos_sat_90 = (position_t){0, 0, 0}; - ntn_ta->vel_sat_0 = (position_t){0, 0, 0}; - ntn_ta->vel_sat_90 = (position_t){0, 0, 0}; - } - } else { // Need R - Release if not present - ntn_ta->omega = 0; - ntn_ta->pos_sat_0 = (position_t){0, 0, 0}; - ntn_ta->pos_sat_90 = (position_t){0, 0, 0}; - ntn_ta->vel_sat_0 = (position_t){0, 0, 0}; - ntn_ta->vel_sat_90 = (position_t){0, 0, 0}; - } - // handle cellSpecificKoffset_r17 if (ntn_Config_r17->cellSpecificKoffset_r17) ntn_ta->cell_specific_k_offset = *ntn_Config_r17->cellSpecificKoffset_r17; @@ -389,11 +368,9 @@ static void configure_ntn_ta(ntn_timing_advance_componets_t *ntn_ta, const NR_NT ntn_ta->N_common_ta_drift_variant = 0; } - ntn_ta->ntn_params_changed = true; - LOG_I(NR_MAC, - "SIB19 Rxd. Epoch HFN: %d, Epoch SFN: %d, Epoch Subframe: %d, k_offset: %ldms, " - "N_Common_Ta: %fms, drift: %fµs/s, variant %fµs/s²\n", + "NTN Config Rxd. Epoch HFN: %d, Epoch SFN: %d, Epoch Subframe: %d, k_offset: %ldms, " + "N_Common_Ta: %fms, drift: %fµs/s, variant: %fµs/s²\n", ntn_ta->epoch_hfn, ntn_ta->epoch_sfn, ntn_ta->epoch_subframe, @@ -401,6 +378,31 @@ static void configure_ntn_ta(ntn_timing_advance_componets_t *ntn_ta, const NR_NT ntn_ta->N_common_ta_adj, ntn_ta->N_common_ta_drift, ntn_ta->N_common_ta_drift_variant); + + // handle ephemerisInfo_r17 + const NR_EphemerisInfo_r17_t *ephemeris_info = ntn_Config_r17->ephemerisInfo_r17; + if (ephemeris_info) { + if (ephemeris_info->present == NR_EphemerisInfo_r17_PR_positionVelocity_r17) { + const NR_PositionVelocity_r17_t *position_velocity = ephemeris_info->choice.positionVelocity_r17; + AssertFatal(position_velocity, "position_velocity should not be NULL here\n"); + prepare_ue_sat_ta(position_velocity, ntn_ta); + } else { + LOG_W(NR_MAC, "NR UE currently supports only ephemerisInfo_r17 of type positionVelocity_r17\n"); + ntn_ta->omega = 0; + ntn_ta->pos_sat_0 = (position_t){0, 0, 0}; + ntn_ta->pos_sat_90 = (position_t){0, 0, 0}; + ntn_ta->vel_sat_0 = (position_t){0, 0, 0}; + ntn_ta->vel_sat_90 = (position_t){0, 0, 0}; + } + } else { // Need R - Release if not present + ntn_ta->omega = 0; + ntn_ta->pos_sat_0 = (position_t){0, 0, 0}; + ntn_ta->pos_sat_90 = (position_t){0, 0, 0}; + ntn_ta->vel_sat_0 = (position_t){0, 0, 0}; + ntn_ta->vel_sat_90 = (position_t){0, 0, 0}; + } + + ntn_ta->ntn_params_changed = true; } static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t *scc, int cc_idP, int hfn, int frame) From 834a6fc1d91bc983982906d10a3b3dce5ce42a3e Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Wed, 24 Sep 2025 14:27:45 +0200 Subject: [PATCH 04/10] NR UE: improve physical layer for NTN (esp. LEO) reestablishment and HO --- executables/nr-ue.c | 99 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 24 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index f3769096f8..d3595087e8 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -497,6 +497,8 @@ static uint64_t get_carrier_frequency(const int N_RB, const int mu, const uint32 return carrier_freq; } +static void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_PARMS *fp, int abs_subframe_tx); + static int handle_sync_req_from_mac(PHY_VARS_NR_UE *UE) { NR_DL_FRAME_PARMS *fp = &UE->frame_parms; @@ -521,6 +523,12 @@ static int handle_sync_req_from_mac(PHY_VARS_NR_UE *UE) init_symbol_rotation(fp); } + // Apply Doppler based on NTN-Config for target cell + if (UE->target_Nid_cell != fp->Nid_cell) + apply_ntn_timing_advance_and_doppler(UE, fp, -1); + // Apply NTN DL Doppler as initial FO + UE->initial_fo = UE->dl_Doppler_shift; + /* Clearing UE harq while DL actors are active causes race condition. So we let the current execution to complete here.*/ for (int i = 0; i < get_nrUE_params()->num_dl_actors; i++) { @@ -727,14 +735,40 @@ static inline int get_readBlockSize(uint16_t slot, NR_DL_FRAME_PARMS *fp) { return rem_samples + next_slot_first_symbol; } +static void fix_ntn_epoch_hfn(PHY_VARS_NR_UE *UE, int hfn, int frame) +{ + const int epoch_frame = UE->ntn_config_message->ntn_config_params.epoch_sfn; + const int diff1 = (frame - epoch_frame + 1024) % 1024; + const int diff2 = (epoch_frame - frame + 1024) % 1024; + + int *epoch_hfn = &UE->ntn_config_message->ntn_config_params.epoch_hfn; + if (diff1 < diff2) { // epoch_frame in the past + LOG_I(PHY, "epoch is %d frames in the past\n", diff1); + if (epoch_frame <= frame) + *epoch_hfn = hfn; + else + *epoch_hfn = hfn - 1; + } else { // epoch_frame in the future + LOG_I(PHY, "epoch is %d frames in the future\n", diff2); + if (epoch_frame >= frame) + *epoch_hfn = hfn; + else + *epoch_hfn = hfn + 1; + } + LOG_I(PHY, "setting epoch_hfn = %d\n", *epoch_hfn); +} + #define SPEED_OF_LIGHT 299792458 + +// calculate TA and Doppler based on the NTN-Config, if abs_subframe_tx < 0 then apply only Doppler static void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_PARMS *fp, int abs_subframe_tx) { const fapi_nr_dl_ntn_config_command_pdu *ntn_config_params = &UE->ntn_config_message->ntn_config_params; // Handle terrestrial networks if (ntn_config_params->cell_specific_k_offset == 0) { - UE->timing_advance_ntn = 0; + if (abs_subframe_tx >= 0) + UE->timing_advance_ntn = 0; UE->dl_Doppler_shift = 0; UE->ul_Doppler_shift = 0; return; @@ -743,7 +777,7 @@ static void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL const int abs_subframe_epoch = ntn_config_params->epoch_subframe + ntn_config_params->epoch_sfn * 10 + ntn_config_params->epoch_hfn * 10240; - const int ms_since_epoch = abs_subframe_tx - abs_subframe_epoch; + const int ms_since_epoch = abs_subframe_tx < 0 ? 0 : abs_subframe_tx - abs_subframe_epoch; const position_t pos_sat_0 = ntn_config_params->pos_sat_0; const position_t vel_sat_0 = ntn_config_params->vel_sat_0; @@ -797,22 +831,26 @@ static void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL const double N_common_ta_drift = ntn_config_params->N_common_ta_drift; const double N_common_ta_drift_variant = ntn_config_params->N_common_ta_drift_variant; - UE->timing_advance_ntn = (N_UE_TA_adj + N_common_ta_adj - + N_common_ta_drift * ms_since_epoch / 1e6 - + N_common_ta_drift_variant * ((int64_t)ms_since_epoch * ms_since_epoch) / 1e9) - * fp->samples_per_subframe; + if (abs_subframe_tx >= 0) { + UE->timing_advance_ntn = (N_UE_TA_adj + N_common_ta_adj + N_common_ta_drift * ms_since_epoch / 1e6 + + N_common_ta_drift_variant * ((int64_t)ms_since_epoch * ms_since_epoch) / 1e9) + * fp->samples_per_subframe; + } UE->freq_offset -= dl_Doppler_shift - UE->dl_Doppler_shift; UE->dl_Doppler_shift = dl_Doppler_shift; UE->ul_Doppler_shift = ul_Doppler_shift; - LOG_D(PHY, - "satellite velocity towards UE = %f m/s, DL Doppler shift = %f kHz, UL Doppler shift = %f kHz\n", - vel_sat_ue, - dl_Doppler_shift / 1000, - ul_Doppler_shift / 1000); + if (abs_subframe_tx < 0) { + LOG_I(PHY, + "satellite velocity towards UE = %f m/s, DL Doppler shift = %f kHz, UL Doppler shift = %f kHz\n", + vel_sat_ue, + dl_Doppler_shift / 1000, + ul_Doppler_shift / 1000); + } } +// apply values based on the NTN-Config static void apply_ntn_config(PHY_VARS_NR_UE *UE, NR_DL_FRAME_PARMS *fp, int hfn_rx, @@ -885,9 +923,14 @@ 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; + const double ntn_init_time_drift = get_nrUE_params()->ntn_init_time_drift; + 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 + UE->max_pos_acc = ntn_init_time_drift * 1e-6 * fp->samples_per_frame / get_nrUE_params()->time_sync_I; + else + UE->max_pos_acc = 0; + int ntn_koffset = 0; int duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX; int timing_advance = UE->timing_advance + UE->timing_advance_ntn; UE->N_TA_offset = determine_N_TA_offset(UE); @@ -983,10 +1026,6 @@ void *UE_thread(void *arg) syncMsg->UE = UE; memset(&syncMsg->proc, 0, sizeof(syncMsg->proc)); pushNotifiedFIFO(&UE->sync_actor.fifo, Msg); - if (ntn_koffset == 0) { // reset timing_advance for TN - timing_advance = 0; - UE->timing_advance = 0; - } trashed_frames = 0; syncRunning = true; continue; @@ -996,13 +1035,8 @@ void *UE_thread(void *arg) stream_status = STREAM_STATUS_SYNCING; 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 - UE->max_pos_acc = ntn_init_time_drift * 1e-6 * fp->samples_per_frame / get_nrUE_params()->time_sync_I; - else - UE->max_pos_acc = 0; shiftForNextFrame = -(UE->init_sync_frame + trashed_frames + 2) * UE->max_pos_acc * get_nrUE_params()->time_sync_I; // compensate for the time drift that happened during initial sync - LOG_D(PHY, "max_pos_acc = %d, shiftForNextFrame = %d\n", UE->max_pos_acc, shiftForNextFrame); + LOG_I(PHY, "max_pos_acc = %d, shiftForNextFrame = %d\n", UE->max_pos_acc, shiftForNextFrame); // read in first symbol AssertFatal(fp->ofdm_symbol_size + fp->nb_prefix_samples0 == UE->rfdevice.trx_read_func(&UE->rfdevice, @@ -1020,6 +1054,18 @@ void *UE_thread(void *arg) // Set to the slot where the SL-SSB was decoded absolute_slot += UE->SL_UE_PHY_PARAMS.sync_params.slot_offset; } + // With the correct frame and slot numbers, we can now fix the UL timing + const int frame_rx = (absolute_slot / nb_slot_frame) % 1024; + const int hfn_rx = (absolute_slot / nb_slot_frame) / 1024; + fix_ntn_epoch_hfn(UE, hfn_rx, frame_rx); + if (UE->ntn_config_message->update) { + apply_ntn_config(UE, fp, hfn_rx, frame_rx, 0, &duration_rx_to_tx, &timing_advance, &ntn_koffset); + } else { + const int mu = fp->numerology_index; + const int abs_subframe_tx = 10240 * hfn_rx + 10 * frame_rx + (duration_rx_to_tx >> mu); + apply_ntn_timing_advance_and_doppler(UE, fp, abs_subframe_tx); + } + UE->timing_advance = 0; // We have resynchronized, maybe after RF loss so we need to purge any existing context memset(tx_wait_for_dlsch, 0, sizeof(tx_wait_for_dlsch)); for (int i = 0; i < NUM_PROCESS_SLOT_TX_BARRIERS; i++) { @@ -1134,6 +1180,11 @@ void *UE_thread(void *arg) writeBlockSize -= new_N_TA_offset - UE->N_TA_offset; 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; + } if (curMsg.proc.nr_slot_rx == 0) nr_ue_rrc_timer_trigger(UE->Mod_id, curMsg.proc.hfn_rx, curMsg.proc.frame_rx, curMsg.proc.gNB_id); @@ -1166,7 +1217,7 @@ void *UE_thread(void *arg) curMsgTx->absolute_deadline_us = absolute_deadline_us; int slot = curMsgTx->proc.nr_slot_tx; - int slot_and_frame = slot + curMsgTx->proc.frame_tx * UE->frame_parms.slots_per_frame; + int slot_and_frame = slot + curMsgTx->proc.frame_tx * nb_slot_frame; int next_tx_slot_and_frame = absolute_slot + duration_rx_to_tx + 1; int wait_for_prev_slot = stream_status == STREAM_STATUS_SYNCED ? 1 : 0; From b5a025931c5dbaff87aa59e160a4960ea0e07c55 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Wed, 24 Sep 2025 14:38:09 +0200 Subject: [PATCH 05/10] verify reestablishment in 5g_rfsimulator_ntn_leo CI test --- .../xml_files/container_5g_rfsim_ntn_leo.xml | 32 +++++++++++++++++++ .../docker-compose.yaml | 4 ++- 2 files changed, 35 insertions(+), 1 deletion(-) 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 cde84b00e7..f00e6b26f1 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_ntn_leo.xml @@ -32,6 +32,11 @@ 000003 020001 020002 + 040001 + 020022 + 040002 + 020001 + 020002 100001 222222 @@ -94,6 +99,33 @@ 5 + + Custom_Command + Trigger Reestablishment + 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 + + + + Ping + Ping ext-dn from NR-UE + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + -c 20 -i0.5 -w25 + 80 + + + + Custom_Command + Verify Reestablishment + localhost + echo ci get_reestab_count | ncat 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_leo/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml index 3b5e7170ba..a3f8ec2a7e 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 @@ -93,7 +93,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 3dcaacb63bb1f476a38e9366fc6e25c8a99777ce Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 26 Sep 2025 11:57:27 +0200 Subject: [PATCH 06/10] NR UE FAPI: provide ntn_config as part of phy_config.config_req instead of a dedicated scheduled_response dl_config PDU We need the ntn_config when reconfiguring the PHY layer for a new initial sync, e.g. for calculating the DL Doppler frequency. As a dedicated scheduled_response dl_config PDU it reaches the PHY layer too late, as part of the phy_config.config_req it reaches the PHY layer in time with the other configuration values. --- executables/nr-ue.c | 17 +++--- .../nfapi/public_inc/fapi_nr_ue_constants.h | 4 +- .../nfapi/public_inc/fapi_nr_ue_interface.h | 6 +- openair1/PHY/INIT/nr_init_ue.c | 2 - openair1/PHY/defs_nr_UE.h | 6 -- openair1/SCHED_NR_UE/fapi_nr_ue_l1.c | 28 ---------- openair2/LAYER2/NR_MAC_UE/config_ue.c | 14 +++-- openair2/LAYER2/NR_MAC_UE/mac_defs.h | 35 +----------- openair2/LAYER2/NR_MAC_UE/main_ue_nr.c | 2 +- openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c | 4 +- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 22 ++++---- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 55 ++++--------------- 12 files changed, 50 insertions(+), 145 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index d3595087e8..41b347772c 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -177,8 +177,7 @@ void init_nr_ue_vars(PHY_VARS_NR_UE *ue, uint8_t UE_id) ue->dci_thres = 0; ue->target_Nid_cell = -1; - ue->ntn_config_message = CALLOC(1, sizeof(*ue->ntn_config_message)); - ue->ntn_config_message->update = false; + ue->nrUE_config.ntn_config.params_changed = false; // initialize all signal buffers init_nr_ue_signal(ue, nb_connected_gNB); @@ -737,11 +736,11 @@ static inline int get_readBlockSize(uint16_t slot, NR_DL_FRAME_PARMS *fp) { static void fix_ntn_epoch_hfn(PHY_VARS_NR_UE *UE, int hfn, int frame) { - const int epoch_frame = UE->ntn_config_message->ntn_config_params.epoch_sfn; + const int epoch_frame = UE->nrUE_config.ntn_config.epoch_sfn; const int diff1 = (frame - epoch_frame + 1024) % 1024; const int diff2 = (epoch_frame - frame + 1024) % 1024; - int *epoch_hfn = &UE->ntn_config_message->ntn_config_params.epoch_hfn; + int *epoch_hfn = &UE->nrUE_config.ntn_config.epoch_hfn; if (diff1 < diff2) { // epoch_frame in the past LOG_I(PHY, "epoch is %d frames in the past\n", diff1); if (epoch_frame <= frame) @@ -763,7 +762,7 @@ static void fix_ntn_epoch_hfn(PHY_VARS_NR_UE *UE, int hfn, int frame) // calculate TA and Doppler based on the NTN-Config, if abs_subframe_tx < 0 then apply only Doppler static void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_PARMS *fp, int abs_subframe_tx) { - const fapi_nr_dl_ntn_config_command_pdu *ntn_config_params = &UE->ntn_config_message->ntn_config_params; + const fapi_nr_ntn_config_t *ntn_config_params = &UE->nrUE_config.ntn_config; // Handle terrestrial networks if (ntn_config_params->cell_specific_k_offset == 0) { @@ -860,10 +859,10 @@ static void apply_ntn_config(PHY_VARS_NR_UE *UE, int *timing_advance, int *ntn_koffset) { - if (UE->ntn_config_message->update) { - UE->ntn_config_message->update = false; + if (UE->nrUE_config.ntn_config.params_changed) { + UE->nrUE_config.ntn_config.params_changed = false; - const fapi_nr_dl_ntn_config_command_pdu *ntn_config_params = &UE->ntn_config_message->ntn_config_params; + const fapi_nr_ntn_config_t *ntn_config_params = &UE->nrUE_config.ntn_config; const int mu = fp->numerology_index; const int koffset = ntn_config_params->cell_specific_k_offset; @@ -1058,7 +1057,7 @@ void *UE_thread(void *arg) const int frame_rx = (absolute_slot / nb_slot_frame) % 1024; const int hfn_rx = (absolute_slot / nb_slot_frame) / 1024; fix_ntn_epoch_hfn(UE, hfn_rx, frame_rx); - if (UE->ntn_config_message->update) { + if (UE->nrUE_config.ntn_config.params_changed) { apply_ntn_config(UE, fp, hfn_rx, frame_rx, 0, &duration_rx_to_tx, &timing_advance, &ntn_koffset); } else { const int mu = fp->numerology_index; diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h index 57292ecee5..04bb77f5de 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_constants.h @@ -51,9 +51,7 @@ #define FAPI_NR_DL_CONFIG_TYPE_CSI_RS 0x06 #define FAPI_NR_DL_CONFIG_TYPE_CSI_IM 0x07 #define FAPI_NR_CONFIG_TA_COMMAND 0x08 -#define FAPI_NR_DL_NTN_CONFIG_PARAMS 0x09 -#define FAPI_NR_DL_CONFIG_TYPES 0x09 - +#define FAPI_NR_DL_CONFIG_TYPES 0x08 #define FAPI_NR_CCE_REG_MAPPING_TYPE_INTERLEAVED 0x01 #define FAPI_NR_CCE_REG_MAPPING_TYPE_NON_INTERLEAVED 0x02 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 49752be093..b1f9fdae6d 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 @@ -587,7 +587,9 @@ typedef struct { // cell scheduling offset expressed in terms of 15kHz SCS long cell_specific_k_offset; -} fapi_nr_dl_ntn_config_command_pdu; + + bool params_changed; +} fapi_nr_ntn_config_t; typedef struct { uint8_t pdu_type; @@ -597,7 +599,6 @@ typedef struct { fapi_nr_dl_config_csirs_pdu csirs_config_pdu; fapi_nr_dl_config_csiim_pdu csiim_config_pdu; fapi_nr_ta_command_pdu ta_command_pdu; - fapi_nr_dl_ntn_config_command_pdu ntn_config_command_pdu; }; } fapi_nr_dl_config_request_pdu_t; @@ -729,6 +730,7 @@ typedef struct { fapi_nr_ssb_table_t ssb_table; fapi_nr_tdd_table_t tdd_table; fapi_nr_prach_config_t prach_config; + fapi_nr_ntn_config_t ntn_config; } fapi_nr_config_request_t; diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index 7ab8a8863d..48cc73cf79 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -338,8 +338,6 @@ void term_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB) free_and_zero(ue->prs_vars[idx]); } - free_and_zero(ue->ntn_config_message); - sl_ue_free(ue); } diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index 5e2e169c1a..a166dc13cd 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -329,11 +329,6 @@ typedef struct UE_NR_SCAN_INFO_s { int32_t freq_offset_Hz[3][10]; } UE_NR_SCAN_INFO_t; -typedef struct { - bool update; - fapi_nr_dl_ntn_config_command_pdu ntn_config_params; -} ntn_config_message_t; - /// Top-level PHY Data Structure for UE typedef struct PHY_VARS_NR_UE_s { openair0_config_t openair0_cfg[MAX_CARDS]; @@ -527,7 +522,6 @@ typedef struct PHY_VARS_NR_UE_s { Actor_t sync_actor; Actor_t *dl_actors; Actor_t *ul_actors; - ntn_config_message_t* ntn_config_message; pthread_t main_thread; pthread_t stat_thread; } PHY_VARS_NR_UE; diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c index 92a0b043e7..885ba1381a 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -89,31 +89,6 @@ static void configure_dlsch(NR_UE_DLSCH_t *dlsch0, } } -static void configure_ntn_params(PHY_VARS_NR_UE *ue, fapi_nr_dl_ntn_config_command_pdu* ntn_params_message) -{ - if (!ue->ntn_config_message) { - ue->ntn_config_message = CALLOC(1, sizeof(*ue->ntn_config_message)); - } - - ue->ntn_config_message->ntn_config_params.epoch_hfn = ntn_params_message->epoch_hfn; - ue->ntn_config_message->ntn_config_params.epoch_sfn = ntn_params_message->epoch_sfn; - ue->ntn_config_message->ntn_config_params.epoch_subframe = ntn_params_message->epoch_subframe; - - ue->ntn_config_message->ntn_config_params.omega = ntn_params_message->omega; - ue->ntn_config_message->ntn_config_params.pos_sat_0 = ntn_params_message->pos_sat_0; - ue->ntn_config_message->ntn_config_params.pos_sat_90 = ntn_params_message->pos_sat_90; - ue->ntn_config_message->ntn_config_params.vel_sat_0 = ntn_params_message->vel_sat_0; - ue->ntn_config_message->ntn_config_params.vel_sat_90 = ntn_params_message->vel_sat_90; - - ue->ntn_config_message->ntn_config_params.N_common_ta_adj = ntn_params_message->N_common_ta_adj; - ue->ntn_config_message->ntn_config_params.N_common_ta_drift = ntn_params_message->N_common_ta_drift; - ue->ntn_config_message->ntn_config_params.N_common_ta_drift_variant = ntn_params_message->N_common_ta_drift_variant; - - ue->ntn_config_message->ntn_config_params.cell_specific_k_offset = ntn_params_message->cell_specific_k_offset; - - ue->ntn_config_message->update = true; -} - static void configure_ta_command(PHY_VARS_NR_UE *ue, fapi_nr_ta_command_pdu *ta_command_pdu) { /* Time Alignment procedure @@ -230,9 +205,6 @@ static void nr_ue_scheduled_response_dl(NR_UE_MAC_INST_t *mac, case FAPI_NR_CONFIG_TA_COMMAND: configure_ta_command(phy, &pdu->ta_command_pdu); break; - case FAPI_NR_DL_NTN_CONFIG_PARAMS: - configure_ntn_params(phy, &pdu->ntn_config_command_pdu); - break; default: LOG_W(PHY, "unhandled dl pdu type %d \n", pdu->pdu_type); } diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index ded7c2a754..2b43883e3d 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -263,7 +263,7 @@ static void config_common_ue_sa(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommo } // prepare data for orbit propagation based on SIB19 ephemeris data to be able to compute the round-trip-time between ue and sat -static void prepare_ue_sat_ta(const NR_PositionVelocity_r17_t *sat_pos, ntn_timing_advance_componets_t *ntn_ta) +static void prepare_ue_sat_ta(const NR_PositionVelocity_r17_t *sat_pos, fapi_nr_ntn_config_t *ntn_ta) { // get sat position coordinates const position_t pos_sat = {sat_pos->positionX_r17 * 1.3, sat_pos->positionY_r17 * 1.3, sat_pos->positionZ_r17 * 1.3}; @@ -327,7 +327,7 @@ static void prepare_ue_sat_ta(const NR_PositionVelocity_r17_t *sat_pos, ntn_timi } // populate ntn_ta structure from mac -static void configure_ntn_ta(ntn_timing_advance_componets_t *ntn_ta, const NR_NTN_Config_r17_t *ntn_Config_r17, int hfn, int frame) +static void configure_ntn_ta(fapi_nr_ntn_config_t *ntn_ta, const NR_NTN_Config_r17_t *ntn_Config_r17, int hfn, int frame) { if (!ntn_Config_r17) return; @@ -402,7 +402,7 @@ static void configure_ntn_ta(ntn_timing_advance_componets_t *ntn_ta, const NR_NT ntn_ta->vel_sat_90 = (position_t){0, 0, 0}; } - ntn_ta->ntn_params_changed = true; + ntn_ta->params_changed = true; } static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t *scc, int cc_idP, int hfn, int frame) @@ -579,7 +579,7 @@ static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t // NTN Config if (scc->ext2) { UPDATE_IE(mac->sc_info.ntn_Config_r17, scc->ext2->ntn_Config_r17, NR_NTN_Config_r17_t); - configure_ntn_ta(&mac->ntn_ta, mac->sc_info.ntn_Config_r17, hfn, frame); + configure_ntn_ta(&mac->phy_config.config_req.ntn_config, mac->sc_info.ntn_Config_r17, hfn, frame); } else { asn1cFreeStruc(asn_DEF_NR_NTN_Config_r17, mac->sc_info.ntn_Config_r17); } @@ -1977,6 +1977,7 @@ void nr_rrc_mac_config_req_sib1(module_id_t module_id, int cc_idP, NR_SIB1_t *si mac->state = UE_PERFORMING_RA; mac->if_module->phy_config_request(&mac->phy_config); + mac->phy_config.config_req.ntn_config.params_changed = false; ret = pthread_mutex_unlock(&mac->if_mutex); AssertFatal(!ret, "mutex failed %d\n", ret); } @@ -1990,7 +1991,9 @@ void nr_rrc_mac_config_other_sib(module_id_t module_id, NR_SIB19_r17_t *sib19, i if (sib19) { // update ntn_Config_r17 with received values UPDATE_IE(mac->sc_info.ntn_Config_r17, sib19->ntn_Config_r17, NR_NTN_Config_r17_t); - configure_ntn_ta(&mac->ntn_ta, mac->sc_info.ntn_Config_r17, hfn, frame); + configure_ntn_ta(&mac->phy_config.config_req.ntn_config, mac->sc_info.ntn_Config_r17, hfn, frame); + mac->if_module->phy_config_request(&mac->phy_config); + mac->phy_config.config_req.ntn_config.params_changed = false; } if (mac->state == UE_RECEIVING_SIB && can_start_ra) mac->state = UE_PERFORMING_RA; @@ -2053,6 +2056,7 @@ static void handle_reconfiguration_with_sync(NR_UE_MAC_INST_t *mac, mac->synch_request.synch_req.target_Nid_cell = mac->physCellId; mac->if_module->synch_request(&mac->synch_request); mac->if_module->phy_config_request(&mac->phy_config); + mac->phy_config.config_req.ntn_config.params_changed = false; } static void configure_physicalcellgroup(NR_UE_MAC_INST_t *mac, diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index 42b616c188..65bf66505b 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -545,35 +545,6 @@ typedef struct { A_SEQUENCE_OF(si_schedinfo_config_t) si_SchedInfo_list; } si_schedInfo_t; -typedef struct ntn_timing_advance_components { - int epoch_hfn; - int epoch_sfn; - int epoch_subframe; - - // orbital angular velocity in rad/ms - double omega; - // satellite position vector at epoch time - position_t pos_sat_0; - // satellite position vector at 90° orbit - position_t pos_sat_90; - // satellite velocity vector at epoch time - position_t vel_sat_0; - // satellite velocity vector at 90° orbit - position_t vel_sat_90; - - // N_common_ta_adj represents common round-trip-time between gNB and SAT received in SIB19 (ms) - double N_common_ta_adj; - // drift rate of common ta in µs/s - double N_common_ta_drift; - // change rate of common ta drift in µs/s² - double N_common_ta_drift_variant; - - // cell scheduling offset expressed in terms of 15kHz SCS - long cell_specific_k_offset; - - bool ntn_params_changed; -} ntn_timing_advance_componets_t; - /*!\brief Top level UE MAC structure */ typedef struct NR_UE_MAC_INST_s { module_id_t ue_id; @@ -637,8 +608,6 @@ typedef struct NR_UE_MAC_INST_s { int p_Max; int p_Max_alt; - ntn_timing_advance_componets_t ntn_ta; - long pdsch_HARQ_ACK_Codebook; NR_Type0_PDCCH_CSS_config_t type0_PDCCH_CSS_config; @@ -679,12 +648,12 @@ typedef struct NR_UE_MAC_INST_s { notifiedFIFO_t input_nf; } NR_UE_MAC_INST_t; -static inline int GET_NTN_UE_K_OFFSET(const ntn_timing_advance_componets_t *ntn_ta, int scs) +static inline int GET_NTN_UE_K_OFFSET(const fapi_nr_ntn_config_t *ntn_ta, int scs) { return (int)ntn_ta->cell_specific_k_offset << scs; } -static inline long GET_DURATION_RX_TO_TX(const ntn_timing_advance_componets_t *ntn_ta, int scs) +static inline long GET_DURATION_RX_TO_TX(const fapi_nr_ntn_config_t *ntn_ta, int scs) { return NR_UE_CAPABILITY_SLOT_RX_TO_TX + (ntn_ta->cell_specific_k_offset << scs); } diff --git a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c index 4cf96b46bc..461d21f3aa 100644 --- a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c +++ b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c @@ -69,7 +69,7 @@ void nr_ue_init_mac(NR_UE_MAC_INST_t *mac) mac->p_Max = INT_MIN; mac->p_Max_alt = INT_MIN; mac->msg3_C_RNTI = false; - mac->ntn_ta.ntn_params_changed = false; + mac->phy_config.config_req.ntn_config.params_changed = false; initNotifiedFIFO(&mac->input_nf); reset_mac_inst(mac); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index ecf8edb022..dfe16de822 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -813,7 +813,7 @@ static void setup_ra_response_window(NR_UE_MAC_INST_t *mac, LOG_E(NR_MAC, "RA-ResponseWindow need to be configured to a value lower than or equal to 10 ms\n"); } - const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->ntn_ta, mac->current_UL_BWP->scs); + const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs); ra->response_window_setup_time = respwind_value + ntn_ue_koffset; } @@ -1027,7 +1027,7 @@ void nr_Msg3_transmitted(NR_UE_MAC_INST_t *mac, uint8_t CC_id, frame_t frameP, s { RA_config_t *ra = &mac->ra; NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon; - const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->ntn_ta, mac->current_UL_BWP->scs); + const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs); const int slots_per_ms = mac->frame_structure.numb_slots_frame / 10; // start contention resolution timer diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 86d7b9254f..241a56b180 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -563,7 +563,7 @@ static int nr_ue_process_dci_ul_00(NR_UE_MAC_INST_t *mac, frame_t frame_tx; int slot_tx; - const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->ntn_ta, mac->current_UL_BWP->scs); + const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs); if (-1 == nr_ue_pusch_scheduler(mac, 0, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset)) { LOG_E(MAC, "Cannot schedule PUSCH\n"); return -1; @@ -661,7 +661,7 @@ static int nr_ue_process_dci_ul_01(NR_UE_MAC_INST_t *mac, tda_info.k2 = csi_K2; } - const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->ntn_ta, mac->current_UL_BWP->scs); + const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs); if (-1 == nr_ue_pusch_scheduler(mac, 0, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset)) { LOG_E(MAC, "Cannot schedule PUSCH\n"); return -1; @@ -998,7 +998,7 @@ static int nr_ue_process_dci_dl_10(NR_UE_MAC_INST_t *mac, /* PDSCH_TO_HARQ_FEEDBACK_TIME_IND */ // according to TS 38.213 9.2.3 - const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing); + const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing); uint16_t feedback_ti = 0; if (rnti_type == TYPE_RA_RNTI_) { @@ -1006,17 +1006,17 @@ static int nr_ue_process_dci_dl_10(NR_UE_MAC_INST_t *mac, // that can process the MSG3. Also assume that RAR is sent in the same slot as DCI (k0 == 0) // This is not perfect as the MSG3 might end up being scheduled later, so we could be // halting the UL scheduler for a longer time than necessary. - feedback_ti = max(1 + GET_DURATION_RX_TO_TX(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing), + feedback_ti = max(1 + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing), get_delta_for_k2(mac->current_UL_BWP->scs) + get_j_for_k2(mac->current_UL_BWP->scs)); } if (rnti_type != TYPE_RA_RNTI_ && rnti_type != TYPE_SI_RNTI_) { if (!get_FeedbackDisabled(mac->sc_info.downlinkHARQ_FeedbackDisabled_r17, dci->harq_pid.val)) { feedback_ti = 1 + dci->pdsch_to_harq_feedback_timing_indicator.val + ntn_ue_koffset; - AssertFatal(feedback_ti >= GET_DURATION_RX_TO_TX(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing), + AssertFatal(feedback_ti >= GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing), "PDSCH to HARQ feedback time (%d) needs to be higher than DURATION_RX_TO_TX (%ld).\n", feedback_ti, - GET_DURATION_RX_TO_TX(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing)); + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing)); // set the harq status at MAC for feedback const int tpc[] = {-1, 0, 1, 3}; set_harq_status(mac, @@ -1312,16 +1312,16 @@ static int nr_ue_process_dci_dl_11(NR_UE_MAC_INST_t *mac, /* PDSCH_TO_HARQ_FEEDBACK_TIME_IND */ // according to TS 38.213 Table 9.2.3-1 - const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing); + const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing); uint16_t feedback_ti = 0; if (!get_FeedbackDisabled(mac->sc_info.downlinkHARQ_FeedbackDisabled_r17, dci->harq_pid.val)) { feedback_ti = pucch_Config->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0] + ntn_ue_koffset; - AssertFatal(feedback_ti >= GET_DURATION_RX_TO_TX(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing), + AssertFatal(feedback_ti >= GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing), "PDSCH to HARQ feedback time (%d) needs to be higher than DURATION_RX_TO_TX (%ld). Min feedback time set in config " "file (min_rxtxtime).\n", feedback_ti, - GET_DURATION_RX_TO_TX(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing)); + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, dlsch_pdu->SubcarrierSpacing)); // set the harq status at MAC for feedback const int tpc[] = {-1, 0, 1, 3}; @@ -3620,7 +3620,7 @@ static void set_time_alignment(NR_UE_MAC_INST_t *mac, int ta, ta_type_t type, in NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &mac->ul_time_alignment; ul_time_alignment->ta_command = ta; ul_time_alignment->ta_apply = type; - const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->ntn_ta, mac->current_UL_BWP->scs); + const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs); const int n_slots_frame = mac->frame_structure.numb_slots_frame; ul_time_alignment->frame = (frame + (slot + ntn_ue_koffset) / n_slots_frame) % MAX_FRAME_NUMBER; ul_time_alignment->slot = (slot + ntn_ue_koffset) % n_slots_frame; @@ -4095,7 +4095,7 @@ static void handle_rar_reception(NR_UE_MAC_INST_t *mac, NR_MAC_RAR *rar, frame_t } frame_t frame_tx = 0; int slot_tx = 0; - const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->ntn_ta, mac->current_UL_BWP->scs); + const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs); int ret = nr_ue_pusch_scheduler(mac, 1, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset); // TA command diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index f7a763f41a..c21a0ce3bd 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -59,7 +59,6 @@ static void nr_ue_prach_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, slot_t slotP); static void schedule_ta_command(fapi_nr_dl_config_request_t *dl_config, NR_UE_MAC_INST_t *mac); -static void schedule_ntn_config_command(fapi_nr_dl_config_request_t *dl_config, NR_UE_MAC_INST_t *mac); static void nr_ue_fill_phr(NR_UE_MAC_INST_t *mac, NR_SINGLE_ENTRY_PHR_MAC_CE *phr, @@ -1056,10 +1055,10 @@ void nr_ue_aperiodic_srs_scheduling(NR_UE_MAC_INST_t *mac, long resource_trigger } int scs = mac->current_UL_BWP->scs; - AssertFatal(slot_offset > GET_DURATION_RX_TO_TX(&mac->ntn_ta, scs), + AssertFatal(slot_offset > GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, scs), "Slot offset between DCI and aperiodic SRS (%d) needs to be higher than DURATION_RX_TO_TX (%ld)\n", slot_offset, - GET_DURATION_RX_TO_TX(&mac->ntn_ta, scs)); + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, scs)); int n_slots_frame = mac->frame_structure.numb_slots_frame; int sched_slot = (slot + slot_offset) % n_slots_frame; if (!is_ul_slot(sched_slot, &mac->frame_structure)) { @@ -1157,11 +1156,6 @@ void nr_ue_dl_scheduler(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info if (mac->ul_time_alignment.ta_apply != no_ta) schedule_ta_command(dl_config, mac); - - if (mac->ntn_ta.ntn_params_changed) { - mac->ntn_ta.ntn_params_changed = false; - schedule_ntn_config_command(dl_config, mac); - } nr_scheduled_response_t scheduled_response = {.dl_config = dl_config, .module_id = mac->ue_id, @@ -1570,24 +1564,24 @@ int nr_ue_pusch_scheduler(const NR_UE_MAC_INST_t *mac, delta = get_delta_for_k2(mu); - AssertFatal((k2 + delta) > GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu), + AssertFatal((k2 + delta) > GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), "Slot offset (%ld) for Msg3 needs to be higher than DURATION_RX_TO_TX (%ld). Please set min_rxtxtime at least to " "%ld in gNB config file or gNBs.[0].min_rxtxtime=%ld via command line.\n", k2, - GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu), - GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu), - GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu)); + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu)); *slot_tx = (current_slot + k2 + delta) % slots_per_frame; *frame_tx = (current_frame + (current_slot + k2 + delta) / slots_per_frame) % MAX_FRAME_NUMBER; } else { - AssertFatal(k2 >= GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu), + AssertFatal(k2 >= GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), "Slot offset K2 (%ld) needs to be higher than DURATION_RX_TO_TX (%ld). Please set min_rxtxtime at least to %ld in " "gNB config file or gNBs.[0].min_rxtxtime=%ld via command line.\n", k2, - GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu), - GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu), - GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu)); + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu)); if (k2 < 0) { // This can happen when a false DCI is received LOG_W(PHY, "%d.%d. Received k2 %ld\n", current_frame, current_slot, k2); @@ -2210,9 +2204,8 @@ static void nr_ue_prach_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, slot_t next_slot = temp_slot; add_slots++; } - nr_timer_setup(&ra->response_window_timer, - ra->response_window_setup_time + add_slots + GET_DURATION_RX_TO_TX(&mac->ntn_ta, mac->current_DL_BWP->scs), - 1); + add_slots += GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mac->current_DL_BWP->scs); + nr_timer_setup(&ra->response_window_timer, ra->response_window_setup_time + add_slots, 1); nr_timer_start(&ra->response_window_timer); } else if (ra->ra_type == RA_2_STEP) { NR_MsgA_PUSCH_Resource_r16_t *msgA_PUSCH_Resource = @@ -2844,30 +2837,6 @@ static uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, return mac_ce_info.num_sdus > 0; // success if we got at least one sdu } -static void schedule_ntn_config_command(fapi_nr_dl_config_request_t *dl_config, NR_UE_MAC_INST_t *mac) -{ - fapi_nr_dl_ntn_config_command_pdu *ntn_config_command_pdu = &dl_config->dl_config_list[dl_config->number_pdus].ntn_config_command_pdu; - - ntn_config_command_pdu->epoch_hfn = mac->ntn_ta.epoch_hfn; - ntn_config_command_pdu->epoch_sfn = mac->ntn_ta.epoch_sfn; - ntn_config_command_pdu->epoch_subframe = mac->ntn_ta.epoch_subframe; - - ntn_config_command_pdu->omega = mac->ntn_ta.omega; - ntn_config_command_pdu->pos_sat_0 = mac->ntn_ta.pos_sat_0; - ntn_config_command_pdu->pos_sat_90 = mac->ntn_ta.pos_sat_90; - ntn_config_command_pdu->vel_sat_0 = mac->ntn_ta.vel_sat_0; - ntn_config_command_pdu->vel_sat_90 = mac->ntn_ta.vel_sat_90; - - ntn_config_command_pdu->N_common_ta_adj = mac->ntn_ta.N_common_ta_adj; - ntn_config_command_pdu->N_common_ta_drift = mac->ntn_ta.N_common_ta_drift; - ntn_config_command_pdu->N_common_ta_drift_variant = mac->ntn_ta.N_common_ta_drift_variant; - - ntn_config_command_pdu->cell_specific_k_offset = mac->ntn_ta.cell_specific_k_offset; - - dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_NTN_CONFIG_PARAMS; - dl_config->number_pdus += 1; -} - static void schedule_ta_command(fapi_nr_dl_config_request_t *dl_config, NR_UE_MAC_INST_t *mac) { NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &mac->ul_time_alignment; From 1f8fe224a0e60423a1b58f1968c33b58bfa11ff1 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 26 Sep 2025 12:07:01 +0200 Subject: [PATCH 07/10] NR UE: correctly apply Doppler and TA for NTN target cell Make sure to apply Doppler and TA not directly when receiving the target cell ntn_config, but apply Doppler just before the initial sync, and the TA after the initial sync when we have tha actuel cell frame and slot numbers. --- executables/nr-ue.c | 79 ++++++++++++------- .../nfapi/public_inc/fapi_nr_ue_interface.h | 1 + openair2/LAYER2/NR_MAC_UE/config_ue.c | 11 ++- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 41b347772c..231db3b91d 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -523,7 +523,7 @@ static int handle_sync_req_from_mac(PHY_VARS_NR_UE *UE) } // Apply Doppler based on NTN-Config for target cell - if (UE->target_Nid_cell != fp->Nid_cell) + if (UE->nrUE_config.ntn_config.is_targetcell) apply_ntn_timing_advance_and_doppler(UE, fp, -1); // Apply NTN DL Doppler as initial FO UE->initial_fo = UE->dl_Doppler_shift; @@ -857,37 +857,46 @@ static void apply_ntn_config(PHY_VARS_NR_UE *UE, int slot_rx, int *duration_rx_to_tx, int *timing_advance, - int *ntn_koffset) + int *ntn_koffset, + bool *ntn_targetcell) { - if (UE->nrUE_config.ntn_config.params_changed) { - UE->nrUE_config.ntn_config.params_changed = false; + // return if there is no pending update + if (!UE->nrUE_config.ntn_config.params_changed) + return; - const fapi_nr_ntn_config_t *ntn_config_params = &UE->nrUE_config.ntn_config; + *ntn_targetcell = UE->nrUE_config.ntn_config.is_targetcell; - const int mu = fp->numerology_index; - const int koffset = ntn_config_params->cell_specific_k_offset; + // skip update if it is for the target cell + if (UE->nrUE_config.ntn_config.is_targetcell) + return; - *duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + (koffset << mu); - if (koffset > *ntn_koffset) - *timing_advance += get_samples_slot_duration(fp, slot_rx, (koffset - *ntn_koffset) << mu); - else if (koffset < *ntn_koffset) - *timing_advance -= get_samples_slot_duration(fp, slot_rx, (*ntn_koffset - koffset) << mu); - *ntn_koffset = koffset; + UE->nrUE_config.ntn_config.params_changed = false; - const int abs_subframe_tx = 10240 * hfn_rx + 10 * frame_rx + ((slot_rx + *duration_rx_to_tx) >> mu); - apply_ntn_timing_advance_and_doppler(UE, fp, abs_subframe_tx); + const fapi_nr_ntn_config_t *ntn_config_params = &UE->nrUE_config.ntn_config; - LOG_I(PHY, - "k_offset: %dms, N_Common_Ta: %fms, drift: %fµs/s, variant: %fµs/s², " - "timing_advance_ntn: %d samples, DL Doppler shift: %fkHz, UL Doppler shift: %fkHz\n", - koffset << mu, - ntn_config_params->N_common_ta_adj, - ntn_config_params->N_common_ta_drift, - ntn_config_params->N_common_ta_drift_variant, - UE->timing_advance_ntn, - UE->dl_Doppler_shift / 1000, - UE->ul_Doppler_shift / 1000); - } + const int mu = fp->numerology_index; + const int koffset = ntn_config_params->cell_specific_k_offset; + + *duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + (koffset << mu); + if (koffset > *ntn_koffset) + *timing_advance += get_samples_slot_duration(fp, slot_rx, (koffset - *ntn_koffset) << mu); + else if (koffset < *ntn_koffset) + *timing_advance -= get_samples_slot_duration(fp, slot_rx, (*ntn_koffset - koffset) << mu); + *ntn_koffset = koffset; + + const int abs_subframe_tx = 10240 * hfn_rx + 10 * frame_rx + ((slot_rx + *duration_rx_to_tx) >> mu); + apply_ntn_timing_advance_and_doppler(UE, fp, abs_subframe_tx); + + LOG_I(PHY, + "k_offset: %dms, N_Common_Ta: %fms, drift: %fµs/s, variant: %fµs/s², " + "timing_advance_ntn: %d samples, DL Doppler shift: %fkHz, UL Doppler shift: %fkHz\n", + koffset << mu, + ntn_config_params->N_common_ta_adj, + ntn_config_params->N_common_ta_drift, + ntn_config_params->N_common_ta_drift_variant, + UE->timing_advance_ntn, + UE->dl_Doppler_shift / 1000, + UE->ul_Doppler_shift / 1000); } void *UE_thread(void *arg) @@ -929,6 +938,7 @@ void *UE_thread(void *arg) else UE->max_pos_acc = 0; + bool ntn_targetcell = false; int ntn_koffset = 0; int duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX; int timing_advance = UE->timing_advance + UE->timing_advance_ntn; @@ -1057,12 +1067,14 @@ void *UE_thread(void *arg) const int frame_rx = (absolute_slot / nb_slot_frame) % 1024; const int hfn_rx = (absolute_slot / nb_slot_frame) / 1024; fix_ntn_epoch_hfn(UE, hfn_rx, frame_rx); + UE->nrUE_config.ntn_config.is_targetcell = false; if (UE->nrUE_config.ntn_config.params_changed) { - apply_ntn_config(UE, fp, hfn_rx, frame_rx, 0, &duration_rx_to_tx, &timing_advance, &ntn_koffset); + apply_ntn_config(UE, fp, hfn_rx, frame_rx, 0, &duration_rx_to_tx, &timing_advance, &ntn_koffset, &ntn_targetcell); } else { const int mu = fp->numerology_index; const int abs_subframe_tx = 10240 * hfn_rx + 10 * frame_rx + (duration_rx_to_tx >> mu); apply_ntn_timing_advance_and_doppler(UE, fp, abs_subframe_tx); + ntn_targetcell = false; } UE->timing_advance = 0; // We have resynchronized, maybe after RF loss so we need to purge any existing context @@ -1124,7 +1136,8 @@ void *UE_thread(void *arg) } // Calculate new TA based on SIB19 information for each subframe in NTN mode, if "autonomous_ta" is not enabled - if (ntn_koffset && !get_nrUE_params()->autonomous_ta && (absolute_slot + duration_rx_to_tx) % fp->slots_per_subframe == 0) { + if (ntn_koffset && !ntn_targetcell && !get_nrUE_params()->autonomous_ta + && (absolute_slot + duration_rx_to_tx) % fp->slots_per_subframe == 0) { const int abs_subframe_tx = (absolute_slot + duration_rx_to_tx) / fp->slots_per_subframe; apply_ntn_timing_advance_and_doppler(UE, fp, abs_subframe_tx); } @@ -1202,7 +1215,15 @@ void *UE_thread(void *arg) } // apply new NTN timing information - apply_ntn_config(UE, fp, curMsg.proc.hfn_rx, curMsg.proc.frame_rx, curMsg.proc.nr_slot_rx, &duration_rx_to_tx, &timing_advance, &ntn_koffset); + apply_ntn_config(UE, + fp, + curMsg.proc.hfn_rx, + curMsg.proc.frame_rx, + curMsg.proc.nr_slot_rx, + &duration_rx_to_tx, + &timing_advance, + &ntn_koffset, + &ntn_targetcell); // Start TX slot processing here. It runs in parallel with RX slot processing // in current code, DURATION_RX_TO_TX constant is the limit to get UL data to encode from a RX slot 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 b1f9fdae6d..6b4648f572 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 @@ -588,6 +588,7 @@ typedef struct { // cell scheduling offset expressed in terms of 15kHz SCS long cell_specific_k_offset; + bool is_targetcell; bool params_changed; } fapi_nr_ntn_config_t; diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 2b43883e3d..4077e793ab 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -327,7 +327,11 @@ static void prepare_ue_sat_ta(const NR_PositionVelocity_r17_t *sat_pos, fapi_nr_ } // populate ntn_ta structure from mac -static void configure_ntn_ta(fapi_nr_ntn_config_t *ntn_ta, const NR_NTN_Config_r17_t *ntn_Config_r17, int hfn, int frame) +static void configure_ntn_ta(fapi_nr_ntn_config_t *ntn_ta, + const NR_NTN_Config_r17_t *ntn_Config_r17, + int hfn, + int frame, + bool is_targetcell) { if (!ntn_Config_r17) return; @@ -402,6 +406,7 @@ static void configure_ntn_ta(fapi_nr_ntn_config_t *ntn_ta, const NR_NTN_Config_r ntn_ta->vel_sat_90 = (position_t){0, 0, 0}; } + ntn_ta->is_targetcell = is_targetcell; ntn_ta->params_changed = true; } @@ -579,7 +584,7 @@ static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t // NTN Config if (scc->ext2) { UPDATE_IE(mac->sc_info.ntn_Config_r17, scc->ext2->ntn_Config_r17, NR_NTN_Config_r17_t); - configure_ntn_ta(&mac->phy_config.config_req.ntn_config, mac->sc_info.ntn_Config_r17, hfn, frame); + configure_ntn_ta(&mac->phy_config.config_req.ntn_config, mac->sc_info.ntn_Config_r17, hfn, frame, true); } else { asn1cFreeStruc(asn_DEF_NR_NTN_Config_r17, mac->sc_info.ntn_Config_r17); } @@ -1991,7 +1996,7 @@ void nr_rrc_mac_config_other_sib(module_id_t module_id, NR_SIB19_r17_t *sib19, i if (sib19) { // update ntn_Config_r17 with received values UPDATE_IE(mac->sc_info.ntn_Config_r17, sib19->ntn_Config_r17, NR_NTN_Config_r17_t); - configure_ntn_ta(&mac->phy_config.config_req.ntn_config, mac->sc_info.ntn_Config_r17, hfn, frame); + configure_ntn_ta(&mac->phy_config.config_req.ntn_config, mac->sc_info.ntn_Config_r17, hfn, frame, false); mac->if_module->phy_config_request(&mac->phy_config); mac->phy_config.config_req.ntn_config.params_changed = false; } From 30afbc41a67adaccbb0f801771254ad78e147d15 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Mon, 27 Oct 2025 17:20:11 +0100 Subject: [PATCH 08/10] update FEATURE_SET.md --- doc/FEATURE_SET.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index a29176ddd7..34a3f17987 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -355,7 +355,8 @@ These modes of operation are supported: - Different TA adjustemt algorithms between SIB19 receptions: - Autonomous TA adjustemt based on DL time tracking - Standard compliant epoch time based TA adjustment including orbital propagation - - UL Doppler pre-compensation based on continuous DL FO estimation + - DL Doppler compensation based on ntn-Config-r17 information + - UL Doppler pre-compensation based on ntn-Config-r17 information and residual 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 From f6fd7b61ece582ccc38b17d34da74aaaf20ea5b8 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Mon, 24 Nov 2025 16:55:09 +0100 Subject: [PATCH 09/10] NR UE: improve NTN reestablishment by continuing with previous HFN --- executables/nr-ue.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 231db3b91d..3b0e1c5ca7 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -736,6 +736,10 @@ static inline int get_readBlockSize(uint16_t slot, NR_DL_FRAME_PARMS *fp) { static void fix_ntn_epoch_hfn(PHY_VARS_NR_UE *UE, int hfn, int frame) { + if (!UE->nrUE_config.ntn_config.is_targetcell) + return; + UE->nrUE_config.ntn_config.is_targetcell = false; + const int epoch_frame = UE->nrUE_config.ntn_config.epoch_sfn; const int diff1 = (frame - epoch_frame + 1024) % 1024; const int diff2 = (epoch_frame - frame + 1024) % 1024; @@ -1057,22 +1061,31 @@ void *UE_thread(void *arg) // we have the decoded frame index in the return of the synch process // and we shifted above to the first slot of next frame decoded_frame_rx = (decoded_frame_rx + 1) % MAX_FRAME_NUMBER; + const int prev_frame_rx = (absolute_slot / nb_slot_frame) % MAX_FRAME_NUMBER; + const int prev_hfn_rx = (absolute_slot / nb_slot_frame) / MAX_FRAME_NUMBER; + int decoded_hfn_rx = prev_hfn_rx; + if (decoded_frame_rx <= prev_frame_rx) + decoded_hfn_rx++; // we do ++ first in the regular processing, so it will be begin of frame; - absolute_slot = decoded_frame_rx * nb_slot_frame - 1; + absolute_slot = (decoded_hfn_rx * MAX_FRAME_NUMBER + decoded_frame_rx) * nb_slot_frame - 1; if (UE->sl_mode == 2) { // Set to the slot where the SL-SSB was decoded absolute_slot += UE->SL_UE_PHY_PARAMS.sync_params.slot_offset; } // With the correct frame and slot numbers, we can now fix the UL timing - const int frame_rx = (absolute_slot / nb_slot_frame) % 1024; - const int hfn_rx = (absolute_slot / nb_slot_frame) / 1024; - fix_ntn_epoch_hfn(UE, hfn_rx, frame_rx); - UE->nrUE_config.ntn_config.is_targetcell = false; + fix_ntn_epoch_hfn(UE, decoded_hfn_rx, decoded_frame_rx); if (UE->nrUE_config.ntn_config.params_changed) { - apply_ntn_config(UE, fp, hfn_rx, frame_rx, 0, &duration_rx_to_tx, &timing_advance, &ntn_koffset, &ntn_targetcell); + apply_ntn_config(UE, + fp, + decoded_hfn_rx, + decoded_frame_rx, + 0, + &duration_rx_to_tx, + &timing_advance, + &ntn_koffset, + &ntn_targetcell); } else { - const int mu = fp->numerology_index; - const int abs_subframe_tx = 10240 * hfn_rx + 10 * frame_rx + (duration_rx_to_tx >> mu); + const int abs_subframe_tx = (absolute_slot + 1 + duration_rx_to_tx) / fp->slots_per_subframe; apply_ntn_timing_advance_and_doppler(UE, fp, abs_subframe_tx); ntn_targetcell = false; } From aa3a20984239b6d6f32d93a086254b8c4d886dd7 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Mon, 24 Nov 2025 17:37:43 +0100 Subject: [PATCH 10/10] NR UE: add option "--cont-fo-comp 3" to not consider measured residual DL FO for UL FO pre-compensation --- doc/RUNMODEM.md | 2 +- executables/nr-ue.c | 2 ++ executables/nr-uesoftmodem.h | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/RUNMODEM.md b/doc/RUNMODEM.md index 37b51b96fa..3345dc114d 100644 --- a/doc/RUNMODEM.md +++ b/doc/RUNMODEM.md @@ -135,7 +135,7 @@ Here are some useful command line options for the NR UE: |--------------------------|---------------------------------------------------------------------------------------------------------------| | `--ue-scan-carrier` | Scan for cells in current bandwidth. This option can be used if the SSB position of the gNB is unknown. If multiple cells are detected, the UE will try to connect to the first cell. By default, this option is disabled and the UE attempts to only decode SSB given by `--ssb`. | | `--ue-fo-compensation` | Enables the initial frequency offset compensation at the UE. Useful when running over the air and/or without an external clock/time source. | -| `--cont-fo-comp` | Enables the continuous frequency offset (FO) estimation and compensation. Parameter value `1` specifies that the main FO contribution comes from the local oscillator's (LO) accuracy. Parameter value `2` specifies that the main FO contribution comes from Doppler shift. | +| `--cont-fo-comp` | Enables the continuous frequency offset (FO) estimation and compensation. Parameter value `1` specifies that the main FO contribution comes from the local oscillator's (LO) accuracy. Parameter value `2` specifies that the main FO contribution comes from Doppler shift. Parameter value `3` specifies that no measured residual DL FO is considered for UL FO pre-compensation. | | `--initial-fo` | Sets the known initial frequency offset. Useful especially with large Doppler frequency, e.g. LEO satellite. | | `--freq-sync-P` | Sets the coefficient for the Proportional part of the PI-controller for the continuous frequency offset compensation. Default value 0.01. | | `--freq-sync-I` | Sets the coefficient for the Integrating part of the PI-controller for the continuous frequency offset compensation. Default value 0.001. | diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 3b0e1c5ca7..676c318333 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -398,6 +398,8 @@ static void RU_write(nr_rxtx_thread_data_t *rxtxD, bool sl_tx_action, c16_t **tx double ul_freq_offset = -UE->freq_offset * ((double)fp->ul_CarrierFreq / (double)fp->dl_CarrierFreq); if (get_nrUE_params()->cont_fo_comp == 2) // different from LO frequency error compensation, Doppler UL pre-compensation has to be negative ul_freq_offset = -ul_freq_offset; + else if (get_nrUE_params()->cont_fo_comp == 3) // do not consider residual DL FO for UL pre-compensation at all + ul_freq_offset = 0; for (int i = 0; i < fp->nb_antennas_tx; i++) nr_fo_compensation(UE->ul_Doppler_shift + ul_freq_offset, fp->samples_per_subframe, diff --git a/executables/nr-uesoftmodem.h b/executables/nr-uesoftmodem.h index df13e2e02e..f14594b0dd 100644 --- a/executables/nr-uesoftmodem.h +++ b/executables/nr-uesoftmodem.h @@ -21,7 +21,7 @@ extern uint16_t ue_id_g; #define CONFIG_HLP_INITIAL_FO "Initially compensated DL frequency offset (e.g. known Doppler shift in NTN LEO scenario)\n" #define CONFIG_HLP_FREQ_SYNC_P "coefficient for Proportional part of continuous frequency offset compensation PI controller\n" #define CONFIG_HLP_FREQ_SYNC_I "coefficient for Integrating part of continuous frequency offset compensation PI controller\n" -#define CONFIG_HLP_CONT_FO_COMP "Enable continuous frequency offset (FO) estimation and compensation and specify main FO source (1 = local oscillator, 2 = Doppler shift)\n" +#define CONFIG_HLP_CONT_FO_COMP "Enable continuous frequency offset (FO) estimation and (pre-)compensation and specify main FO source (1 = local oscillator, 2 = Doppler shift, 3 = Don't consider residual DL FO in UL FO pre-compensation)\n" #define CONFIG_HLP_AGC "Rx Gain control used for UE\n" #define CONFIG_HLP_NUM_UL_ACTORS "Number of UL actors to use. Set to 0 to disable UL actor framework and do processing inline\n" #define CONFIG_HLP_NUM_DL_ACTORS "Number of DL actors to use. Set to 0 to disable DL actor framework and do processing inline\n"