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