From f1d612c01bbfd3fb1ce2c6de42cf726fedf944ed Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Wed, 23 Apr 2025 12:49:16 +0000 Subject: [PATCH] NR UE: improve initial timing advance calculation from SIB19 Before, we only considered ta_Common_r17 and the SAT position from SIB19 to compute the initial timing advance value. This is good enough if the satellite does not move too fast (e.g. GEO satellite). Now we also consider ta_CommonDrift_r17 and the SAT velocity together with the epoch time from SIB19. This improves the accuracy of the initial TA computation, esp. for LEO satellite scenarios. --- executables/nr-ue.c | 36 ++++--- .../nfapi/public_inc/fapi_nr_ue_interface.h | 10 +- openair1/SCHED_NR_UE/fapi_nr_ue_l1.c | 7 +- openair2/LAYER2/NR_MAC_UE/config_ue.c | 97 +++++++++++++------ openair2/LAYER2/NR_MAC_UE/mac_defs.h | 24 +++-- openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c | 4 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 12 ++- 7 files changed, 135 insertions(+), 55 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index d0a8f27a0f..ff3b5c2110 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -905,7 +905,8 @@ static inline int get_readBlockSize(uint16_t slot, NR_DL_FRAME_PARMS *fp) { static inline void apply_ntn_config(PHY_VARS_NR_UE *UE, NR_DL_FRAME_PARMS *fp, - int slot_nr, + int frame_rx, + int slot_rx, int *duration_rx_to_tx, int *timing_advance, int *ntn_koffset) @@ -913,22 +914,33 @@ static inline void apply_ntn_config(PHY_VARS_NR_UE *UE, if (UE->ntn_config_message->update) { UE->ntn_config_message->update = false; - double total_ta_ms = UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms; - UE->timing_advance = fp->samples_per_subframe * total_ta_ms; + const int mu = fp->numerology_index; + const int koffset = UE->ntn_config_message->ntn_config_params.cell_specific_k_offset; - int mu = fp->numerology_index; - int koffset = UE->ntn_config_message->ntn_config_params.cell_specific_k_offset; - if (*ntn_koffset != koffset) { - *duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + (koffset << mu); - *timing_advance += fp->get_samples_slot_timestamp(slot_nr, fp, (koffset - *ntn_koffset) << mu); - *ntn_koffset = koffset; - } + *duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + (koffset << mu); + if (koffset > *ntn_koffset) + *timing_advance += fp->get_samples_slot_timestamp(slot_rx, fp, (koffset - *ntn_koffset) << mu); + else if (koffset < *ntn_koffset) + *timing_advance -= fp->get_samples_slot_timestamp(slot_rx, fp, (*ntn_koffset - koffset) << mu); + *ntn_koffset = koffset; + + const int abs_subframe_tx = 10 * frame_rx + ((slot_rx + *duration_rx_to_tx) >> mu); + const int abs_subframe_epoch = + 10 * UE->ntn_config_message->ntn_config_params.epoch_sfn + UE->ntn_config_message->ntn_config_params.epoch_subframe; + const int ms_since_epoch = (abs_subframe_tx - abs_subframe_epoch + 10240) % 10240; + + const double total_ta_ms = UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms; + const double total_ta_drift = UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_drift; // µs/s + UE->timing_advance = fp->samples_per_subframe * (total_ta_ms + (total_ta_drift / 1000.0) * (ms_since_epoch / 1000.0)); LOG_I(PHY, - "k_offset = %d ms (%d slots), ntn_total_time_advance_ms = %f ms (%d samples)\n", + "k_offset = %d ms (%d slots), total_ta_ms = %f ms, total_ta_drift = %f µs/s, ms_since_epoch = %d ms, computed " + "timing_advance = %d samples\n", *ntn_koffset, *ntn_koffset << mu, total_ta_ms, + total_ta_drift, + ms_since_epoch, UE->timing_advance); } } @@ -1227,7 +1239,7 @@ void *UE_thread(void *arg) } // apply new NTN timing information - apply_ntn_config(UE, fp, slot_nr, &duration_rx_to_tx, &timing_advance, &ntn_koffset); + apply_ntn_config(UE, fp, curMsg.proc.frame_rx, curMsg.proc.nr_slot_rx, &duration_rx_to_tx, &timing_advance, &ntn_koffset); // 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 140ed130a4..b40c062f03 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 @@ -557,16 +557,22 @@ typedef struct { } fapi_nr_ta_command_pdu; typedef struct { + int epoch_sfn; + int epoch_subframe; + // N_common_ta_adj represents common round-trip-time between gNB and SAT received in SIB19 (ms) double N_common_ta_adj; - // N_UE_TA_adj calculated round-trip-time between UE and SAT (ms) - double N_UE_TA_adj; // drift rate of common ta in µs/s double ntn_ta_commondrift; + // N_UE_TA_adj calculated round-trip-time between UE and SAT (ms) + double N_UE_TA_adj; + // drift rate of N_UE_TA in µs/s + double N_UE_TA_drift; // cell scheduling offset expressed in terms of 15kHz SCS long cell_specific_k_offset; double ntn_total_time_advance_ms; + double ntn_total_time_advance_drift; } fapi_nr_dl_ntn_config_command_pdu; typedef struct { diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c index 6bb7ed3e4c..c7235e807b 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -352,10 +352,15 @@ static void configure_ntn_params(PHY_VARS_NR_UE *ue, fapi_nr_dl_ntn_config_comma ue->ntn_config_message = CALLOC(1, sizeof(*ue->ntn_config_message)); } - ue->ntn_config_message->ntn_config_params.cell_specific_k_offset = ntn_params_message->cell_specific_k_offset; + 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.N_common_ta_adj = ntn_params_message->N_common_ta_adj; ue->ntn_config_message->ntn_config_params.ntn_ta_commondrift = ntn_params_message->ntn_ta_commondrift; + ue->ntn_config_message->ntn_config_params.N_UE_TA_adj = ntn_params_message->N_UE_TA_adj; + ue->ntn_config_message->ntn_config_params.N_UE_TA_drift = ntn_params_message->N_UE_TA_drift; + ue->ntn_config_message->ntn_config_params.cell_specific_k_offset = ntn_params_message->cell_specific_k_offset; ue->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms = ntn_params_message->ntn_total_time_advance_ms; + ue->ntn_config_message->ntn_config_params.ntn_total_time_advance_drift = ntn_params_message->ntn_total_time_advance_drift; ue->ntn_config_message->update = true; } diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index bfea78d540..8bf20428c2 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -267,59 +267,102 @@ static void config_common_ue_sa(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommo } // computes round-trip-time between ue and sat based on SIB19 ephemeris data -static double calculate_ue_sat_ta(const position_t *position_params, NR_PositionVelocity_r17_t *sat_pos) +static void calculate_ue_sat_ta(const position_t *position_params, + const NR_PositionVelocity_r17_t *sat_pos, + ntn_timing_advance_componets_t *ntn_ta) { // get UE position coordinates - double posx = position_params->positionX; - double posy = position_params->positionY; - double posz = position_params->positionZ; + double pos_ue_x = position_params->positionX; + double pos_ue_y = position_params->positionY; + double pos_ue_z = position_params->positionZ; // get sat position coordinates - double posx_0 = (double)sat_pos->positionX_r17 * 1.3; - double posy_0 = (double)sat_pos->positionY_r17 * 1.3; - double posz_0 = (double)sat_pos->positionZ_r17 * 1.3; + double pos_sat_x = (double)sat_pos->positionX_r17 * 1.3; + double pos_sat_y = (double)sat_pos->positionY_r17 * 1.3; + double pos_sat_z = (double)sat_pos->positionZ_r17 * 1.3; - double distance = 2 * sqrt(pow(posx - posx_0, 2) + pow(posy - posy_0, 2) + pow(posz - posz_0, 2)); - double ta_ms = (distance / SPEED_OF_LIGHT) * 1000; + // calculate directional vector from SAT to UE + double dir_sat_ue_x = pos_ue_x - pos_sat_x; + double dir_sat_ue_y = pos_ue_y - pos_sat_y; + double dir_sat_ue_z = pos_ue_z - pos_sat_z; - return ta_ms; + // calculate distance between SAT and UE + 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); + + // get sat velocity vector + double vel_sat_x = (double)sat_pos->velocityVX_r17 * 0.06; + double vel_sat_y = (double)sat_pos->velocityVY_r17 * 0.06; + double vel_sat_z = (double)sat_pos->velocityVZ_r17 * 0.06; + + // calculate SAT velocity towards UE + double velocity = (vel_sat_x * dir_sat_ue_x + vel_sat_y * dir_sat_ue_y + vel_sat_z * dir_sat_ue_z) / distance; + + ntn_ta->N_UE_TA_adj = (2 * distance / SPEED_OF_LIGHT) * 1e3; // in ms + ntn_ta->N_UE_TA_drift = (2 * -velocity / SPEED_OF_LIGHT) * 1e6; // in µs/s } // populate ntn_ta structure from mac -void configure_ntn_ta(module_id_t module_id, ntn_timing_advance_componets_t *ntn_ta, NR_NTN_Config_r17_t *ntn_Config_r17) +static void configure_ntn_ta(module_id_t module_id, + ntn_timing_advance_componets_t *ntn_ta, + const NR_NTN_Config_r17_t *ntn_Config_r17) { position_t position_params = {0}; get_position_coordinates(module_id, &position_params); - // if ephemerisInfo_r17 present in SIB19 - NR_EphemerisInfo_r17_t *ephemeris_info = ntn_Config_r17->ephemerisInfo_r17; + // epochTime_r17 must be present (this is assured by function `eval_epoch_time()`) + const NR_EpochTime_r17_t *epoch_time_r17 = ntn_Config_r17->epochTime_r17; + AssertFatal(epoch_time_r17, "epoch_time_r17 should not be NULL here\n"); + 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) { - NR_PositionVelocity_r17_t *position_velocity = ephemeris_info->choice.positionVelocity_r17; - if (position_velocity - && (position_velocity->positionX_r17 != 0 || position_velocity->positionY_r17 != 0 - || position_velocity->positionZ_r17 != 0)) { - ntn_ta->N_UE_TA_adj = calculate_ue_sat_ta(&position_params, position_velocity); + 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"); + calculate_ue_sat_ta(&position_params, position_velocity, ntn_ta); + } else { + LOG_W(NR_MAC, "NR UE currently supports only ephemerisInfo_r17 of type positionVelocity_r17\n"); + ntn_ta->N_UE_TA_adj = 0; + ntn_ta->N_UE_TA_drift = 0; } + } else { // Need R - Release if not present + ntn_ta->N_UE_TA_adj = 0; + ntn_ta->N_UE_TA_drift = 0; } - // if cellSpecificKoffset_r17 is present - if (ntn_Config_r17->cellSpecificKoffset_r17) { + + // handle cellSpecificKoffset_r17 + if (ntn_Config_r17->cellSpecificKoffset_r17) ntn_ta->cell_specific_k_offset = *ntn_Config_r17->cellSpecificKoffset_r17; - } - // Check if ta_Info_r17 is present and convert directly ta_Common_r17 (is in units of 4.072e-3 µs) + else // Need R - Release if not present + ntn_ta->cell_specific_k_offset = 0; + + // Check if ta_Info_r17 is present and convert the time units if (ntn_Config_r17->ta_Info_r17) { + // ta_Common_r17 (is in units of 4.072e-3 µs) ntn_ta->N_common_ta_adj = ntn_Config_r17->ta_Info_r17->ta_Common_r17 * 4.072e-6; // ta_CommonDrift_r17 (is in units of 0.2e-3 µs/s) if (ntn_Config_r17->ta_Info_r17->ta_CommonDrift_r17) ntn_ta->ntn_ta_commondrift = *ntn_Config_r17->ta_Info_r17->ta_CommonDrift_r17 * 0.2e-3; + else // Need R - Release if not present + ntn_ta->ntn_ta_commondrift = 0; + } else { // Need R - Release if not present + ntn_ta->N_common_ta_adj = 0; + ntn_ta->ntn_ta_commondrift = 0; } + ntn_ta->ntn_params_changed = true; LOG_D(NR_MAC, - "SIB19 Rxd. k_offset:%ld, N_Common_Ta:%f,drift:%f,N_UE_TA:%f \n", + "SIB19 Rxd. Epoch SFN: %d, Epoch Subframe: %d, k_offset: %ldms, N_Common_Ta: %fms, drift: %fµs/s, N_UE_TA: %fms, drift: %fµs/s\n", + ntn_ta->epoch_sfn, + ntn_ta->epoch_subframe, ntn_ta->cell_specific_k_offset, ntn_ta->N_common_ta_adj, ntn_ta->ntn_ta_commondrift, - ntn_ta->N_UE_TA_adj); + ntn_ta->N_UE_TA_adj, + ntn_ta->N_UE_TA_drift); } static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t *scc, int cc_idP) @@ -1863,10 +1906,8 @@ void nr_rrc_mac_config_other_sib(module_id_t module_id, NR_SIB19_r17_t *sib19, b if (sib19) { // update ntn_Config_r17 with received values - NR_NTN_Config_r17_t *ntn_Config_r17 = mac->sc_info.ntn_Config_r17; - UPDATE_IE(ntn_Config_r17, sib19->ntn_Config_r17, NR_NTN_Config_r17_t); - - configure_ntn_ta(mac->ue_id, &mac->ntn_ta, ntn_Config_r17); + UPDATE_IE(mac->sc_info.ntn_Config_r17, sib19->ntn_Config_r17, NR_NTN_Config_r17_t); + configure_ntn_ta(mac->ue_id, &mac->ntn_ta, mac->sc_info.ntn_Config_r17); } if (mac->state == UE_RECEIVING_SIB && can_start_ra) mac->state = UE_PERFORMING_RA; diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index dee2a30c08..13f42ca9e3 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -526,12 +526,17 @@ typedef struct { } si_schedInfo_t; typedef struct ntn_timing_advance_components { + int epoch_sfn; + int epoch_subframe; + // N_common_ta_adj represents common round-trip-time between gNB and SAT received in SIB19 (ms) double N_common_ta_adj; - // N_UE_TA_adj calculated round-trip-time between UE and SAT (ms) - double N_UE_TA_adj; // drift rate of common ta in µs/s double ntn_ta_commondrift; + // N_UE_TA_adj calculated round-trip-time between UE and SAT (ms) + double N_UE_TA_adj; + // drift rate of N_UE_TA in µs/s + double N_UE_TA_drift; // cell scheduling offset expressed in terms of 15kHz SCS long cell_specific_k_offset; @@ -650,15 +655,20 @@ static inline int GET_NTN_UE_K_OFFSET(const ntn_timing_advance_componets_t *ntn_ return (int)ntn_ta->cell_specific_k_offset << scs; } -static inline double GET_COMPLETE_TIME_ADVANCE_MS(const ntn_timing_advance_componets_t *ntn_ta) -{ - return ntn_ta->N_common_ta_adj + ntn_ta->N_UE_TA_adj; -} - static inline long GET_DURATION_RX_TO_TX(const ntn_timing_advance_componets_t *ntn_ta, int scs) { return NR_UE_CAPABILITY_SLOT_RX_TO_TX + (ntn_ta->cell_specific_k_offset << scs); } +static inline double get_total_TA_ms(const ntn_timing_advance_componets_t *ntn_ta) +{ + return ntn_ta->N_common_ta_adj + ntn_ta->N_UE_TA_adj; +} + +static inline double get_total_TA_drift(const ntn_timing_advance_componets_t *ntn_ta) +{ + return ntn_ta->ntn_ta_commondrift + ntn_ta->N_UE_TA_drift; +} + /*@}*/ #endif /*__LAYER2_MAC_DEFS_H__ */ diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index 2f717a7466..58269f769a 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -818,7 +818,7 @@ static void setup_ra_response_window(RA_config_t *ra, int ta_Common_slots = 0; if (ntn_Config_r17) { - const double ta_Common_ms = GET_COMPLETE_TIME_ADVANCE_MS(ntn_ta); + const double ta_Common_ms = get_total_TA_ms(ntn_ta); ta_Common_slots = (int)ceil(ta_Common_ms * slots_per_frame / 10); } @@ -1066,7 +1066,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 double ta_Common_ms = GET_COMPLETE_TIME_ADVANCE_MS(&mac->ntn_ta); + const double ta_Common_ms = get_total_TA_ms(&mac->ntn_ta); 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_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 2c37b21f5b..a818096a6f 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -2843,11 +2843,17 @@ static uint8_t nr_ue_get_sdu(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) { 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->cell_specific_k_offset = mac->ntn_ta.cell_specific_k_offset; - ntn_config_command_pdu->ntn_ta_commondrift = mac->ntn_ta.ntn_ta_commondrift; + + 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->N_common_ta_adj = mac->ntn_ta.N_common_ta_adj; + ntn_config_command_pdu->ntn_ta_commondrift = mac->ntn_ta.ntn_ta_commondrift; ntn_config_command_pdu->N_UE_TA_adj = mac->ntn_ta.N_UE_TA_adj; - ntn_config_command_pdu->ntn_total_time_advance_ms = mac->ntn_ta.N_common_ta_adj + mac->ntn_ta.N_UE_TA_adj; + ntn_config_command_pdu->N_UE_TA_drift = mac->ntn_ta.N_UE_TA_drift; + ntn_config_command_pdu->cell_specific_k_offset = mac->ntn_ta.cell_specific_k_offset; + ntn_config_command_pdu->ntn_total_time_advance_ms = get_total_TA_ms(&mac->ntn_ta); + ntn_config_command_pdu->ntn_total_time_advance_drift = get_total_TA_drift(&mac->ntn_ta); + dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_NTN_CONFIG_PARAMS; dl_config->number_pdus += 1; }