diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 345a12d99a..0e5a308259 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -908,7 +908,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) @@ -916,22 +917,43 @@ 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; + int ms_since_epoch = abs_subframe_tx - abs_subframe_epoch; + // cope with sfn wrap-around + // this currently limits us to epoch times that are not more than 5.12 seconds in the future (allowed are up to 10.24 seconds) + // and ntn-UlSyncValidityDuration < 5.12 seconds (allowed are up to 900 seconds) + // TODO: fix this limitation by introducing the hyper frame number HFN + if (ms_since_epoch < -5120) + ms_since_epoch += 10240; + else if (ms_since_epoch > 5120) + ms_since_epoch -= 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 + const double total_ta_drift_variant = UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_drift_variant; // µs/s² + UE->timing_advance = fp->samples_per_subframe * (total_ta_ms + (total_ta_drift / 1000.0) * (ms_since_epoch / 1000.0) + (total_ta_drift_variant / 1000.0) * (ms_since_epoch / 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, total_ta_drift_variant = %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, + total_ta_drift_variant, + ms_since_epoch, UE->timing_advance); } } @@ -1088,9 +1110,6 @@ void *UE_thread(void *arg) 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); - // TODO: remove this autonomous TA and use up-to-date values of ta-Common, ta-CommonDrift and ta-CommonDriftVariant from received SIB19 instead - if (get_nrUE_params()->autonomous_ta) - UE->timing_advance -= 2 * shiftForNextFrame; // read in first symbol AssertFatal(fp->ofdm_symbol_size + fp->nb_prefix_samples0 == UE->rfdevice.trx_read_func(&UE->rfdevice, @@ -1215,8 +1234,8 @@ void *UE_thread(void *arg) UE->N_TA_offset = new_N_TA_offset; } - if (curMsg.proc.nr_slot_tx == 0) - nr_ue_rrc_timer_trigger(UE->Mod_id, curMsg.proc.frame_tx, curMsg.proc.gNB_id); + if (curMsg.proc.nr_slot_rx == 0) + nr_ue_rrc_timer_trigger(UE->Mod_id, curMsg.proc.frame_rx, curMsg.proc.gNB_id); // RX slot processing. We launch and forget. notifiedFIFO_elt_t *newRx = newNotifiedFIFO_elt(sizeof(nr_rxtx_thread_data_t), curMsg.proc.nr_slot_tx, NULL, UE_dl_processing); @@ -1232,7 +1251,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/executables/position_interface.h b/executables/position_interface.h index 0324bceb15..8ef0b3144b 100644 --- a/executables/position_interface.h +++ b/executables/position_interface.h @@ -41,16 +41,16 @@ /*----------------------------------------------------------------------------------------------------------------------------------------------------*/ // clang-format off #define POSITION_CONFIG_PARAMS_DEF { \ - {CONFIG_STRING_POSITION_X, HELP_STRING_POSITION, 0, .dblptr=&(position->positionX), .defuintval=0, TYPE_DOUBLE, 0}, \ - {CONFIG_STRING_POSITION_Y, HELP_STRING_POSITION, 0, .dblptr=&(position->positionY), .defuintval=0, TYPE_DOUBLE, 0}, \ - {CONFIG_STRING_POSITION_Z, HELP_STRING_POSITION, 0, .dblptr=&(position->positionZ), .defuintval=0, TYPE_DOUBLE, 0} \ + {CONFIG_STRING_POSITION_X, HELP_STRING_POSITION, 0, .dblptr=&(position->X), .defuintval=0, TYPE_DOUBLE, 0}, \ + {CONFIG_STRING_POSITION_Y, HELP_STRING_POSITION, 0, .dblptr=&(position->Y), .defuintval=0, TYPE_DOUBLE, 0}, \ + {CONFIG_STRING_POSITION_Z, HELP_STRING_POSITION, 0, .dblptr=&(position->Z), .defuintval=0, TYPE_DOUBLE, 0} \ } // clang-format on typedef struct position { - double positionX; - double positionY; - double positionZ; + double X; + double Y; + double Z; } position_t; void get_position_coordinates(int Mod_id, position_t *position); 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..64c8dab07e 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,18 @@ typedef struct { } fapi_nr_ta_command_pdu; typedef struct { - // 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; + int epoch_sfn; + int epoch_subframe; + // cell scheduling offset expressed in terms of 15kHz SCS long cell_specific_k_offset; + // ntn_total_time_advance_ms represents the complete round-trip-time between gNB and UE via SAT double ntn_total_time_advance_ms; + // drift rate of ntn_total_time_advance_ms in µs/s + double ntn_total_time_advance_drift; + // change rate of ntn_total_time_advance_ms drift in µs/s² + double ntn_total_time_advance_drift_variant; } 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..52d5033457 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -352,10 +352,12 @@ 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.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.cell_specific_k_offset = ntn_params_message->cell_specific_k_offset; - 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.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->ntn_config_params.ntn_total_time_advance_drift_variant = ntn_params_message->ntn_total_time_advance_drift_variant; ue->ntn_config_message->update = true; } diff --git a/openair2/COMMON/mac_messages_def.h b/openair2/COMMON/mac_messages_def.h index e3ef328017..f342444c65 100644 --- a/openair2/COMMON/mac_messages_def.h +++ b/openair2/COMMON/mac_messages_def.h @@ -60,3 +60,5 @@ MESSAGE_DEF(NR_RRC_MAC_CCCH_DATA_IND, MESSAGE_PRIORITY_MED_PLUS, NRRrcMacCcch MESSAGE_DEF(NR_RRC_MAC_BCCH_DATA_IND, MESSAGE_PRIORITY_MED_PLUS, NRRrcMacBcchDataInd, nr_rrc_mac_bcch_data_ind) MESSAGE_DEF(NR_RRC_MAC_SBCCH_DATA_IND, MESSAGE_PRIORITY_MED_PLUS, NRRrcMacSBcchDataInd, nr_rrc_mac_sbcch_data_ind) +// Message to GNB_APP to update SIB19 satellite position information +MESSAGE_DEF(GNB_SAT_POSITION_UPDATE, MESSAGE_PRIORITY_MED, gnb_sat_position_update_t, gnb_sat_position_update) diff --git a/openair2/COMMON/mac_messages_types.h b/openair2/COMMON/mac_messages_types.h index ebbda2fc26..f5712b115a 100644 --- a/openair2/COMMON/mac_messages_types.h +++ b/openair2/COMMON/mac_messages_types.h @@ -31,6 +31,7 @@ #include #include "OCTET_STRING.h" +#include "NR_MAC_gNB/mac_config.h" //-------------------------------------------------------------------------------------------// // Defines to access message fields. @@ -61,6 +62,8 @@ #define RRC_MAC_DRX_CONFIG_REQ(mSGpTR) (mSGpTR)->ittiMsg.rrc_mac_drx_config_req +#define GNB_SAT_POSITION_UPDATE(mSGpTR) (mSGpTR)->ittiMsg.gnb_sat_position_update + // Some constants from "LAYER2/MAC/defs.h" #define BCCH_SDU_SIZE (512) #define BCCH_SDU_MBMS_SIZE (512) diff --git a/openair2/GNB_APP/gnb_app.c b/openair2/GNB_APP/gnb_app.c index e90a83d3bc..3b97dfe515 100644 --- a/openair2/GNB_APP/gnb_app.c +++ b/openair2/GNB_APP/gnb_app.c @@ -212,6 +212,11 @@ void *gNB_app_task(void *args_p) LOG_I(GNB_APP, " Received %s: timer_id %ld\n", msg_name, TIMER_HAS_EXPIRED(msg_p).timer_id); break; + case GNB_SAT_POSITION_UPDATE: + LOG_I(GNB_APP, " Received GNB_SAT_POSITION_UPDATE message\n"); + nr_update_sib19(&GNB_SAT_POSITION_UPDATE(msg_p)); + break; + default: LOG_E(GNB_APP, "Received unexpected message %s\n", msg_name); break; diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 36ac8b6b1f..044dc56351 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -221,11 +221,18 @@ void prepare_scc(NR_ServingCellConfigCommon_t *scc) scc->ext2->ntn_Config_r17->ephemerisInfo_r17 = calloc_or_fail(1, sizeof(*scc->ext2->ntn_Config_r17->ephemerisInfo_r17)); scc->ext2->ntn_Config_r17->ta_Info_r17 = calloc_or_fail(1, sizeof(*scc->ext2->ntn_Config_r17->ta_Info_r17)); + scc->ext2->ntn_Config_r17->ta_Info_r17->ta_Common_r17 = -1; scc->ext2->ntn_Config_r17->ta_Info_r17->ta_CommonDrift_r17 = calloc_or_fail(1, sizeof(*scc->ext2->ntn_Config_r17->ta_Info_r17->ta_CommonDrift_r17)); scc->ext2->ntn_Config_r17->ephemerisInfo_r17->present = NR_EphemerisInfo_r17_PR_positionVelocity_r17; scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17 = calloc_or_fail(1, sizeof(*scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17)); + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17->positionX_r17 = LONG_MAX; + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17->positionY_r17 = LONG_MAX; + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17->positionZ_r17 = LONG_MAX; + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17->velocityVX_r17 = LONG_MAX; + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17->velocityVY_r17 = LONG_MAX; + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17->velocityVZ_r17 = LONG_MAX; } void prepare_msgA_scc(NR_ServingCellConfigCommon_t *scc) { NR_BWP_UplinkCommon_t *initialUplinkBWP = scc->uplinkConfigCommon->initialUplinkBWP; diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 8624766bd1..297be42261 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -267,59 +267,104 @@ 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; + const position_t pos_ue = *position_params; // 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; + const position_t pos_sat = {sat_pos->positionX_r17 * 1.3, sat_pos->positionY_r17 * 1.3, 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 + const position_t dir_sat_ue = {pos_ue.X - pos_sat.X, pos_ue.Y - pos_sat.Y, 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 + const position_t vel_sat = {sat_pos->velocityVX_r17 * 0.06, sat_pos->velocityVY_r17 * 0.06, 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) { + if (!ntn_Config_r17) + return; + 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; + ntn_ta->N_common_ta_drift = *ntn_Config_r17->ta_Info_r17->ta_CommonDrift_r17 * 0.2e-3; + else // Need R - Release if not present + ntn_ta->N_common_ta_drift = 0; + // ta_CommonDriftVariant_r17 (is in units of 0.2e-4 µs/s²) + if (ntn_Config_r17->ta_Info_r17->ta_CommonDriftVariant_r17) + ntn_ta->N_common_ta_drift_variant = *ntn_Config_r17->ta_Info_r17->ta_CommonDriftVariant_r17 * 0.2e-4; + else // Need R - Release if not present + ntn_ta->N_common_ta_drift_variant = 0; + } else { // Need R - Release if not present + ntn_ta->N_common_ta_adj = 0; + ntn_ta->N_common_ta_drift = 0; + ntn_ta->N_common_ta_drift_variant = 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, variant %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_common_ta_drift, + ntn_ta->N_common_ta_drift_variant, + 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) @@ -1875,10 +1920,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 0174e88abc..9abe9751cb 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -528,12 +528,19 @@ 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; + // 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; // 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; + // 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; @@ -652,15 +659,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->N_common_ta_drift + 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 5539ce08ef..25ddd20b1d 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -2848,11 +2848,14 @@ 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->epoch_sfn = mac->ntn_ta.epoch_sfn; + ntn_config_command_pdu->epoch_subframe = mac->ntn_ta.epoch_subframe; 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->N_common_ta_adj = mac->ntn_ta.N_common_ta_adj; - 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->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); + ntn_config_command_pdu->ntn_total_time_advance_drift_variant = mac->ntn_ta.N_common_ta_drift_variant; + dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_NTN_CONFIG_PARAMS; dl_config->number_pdus += 1; } diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index abe6e5218a..bfc9f63538 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -927,6 +927,96 @@ bool nr_mac_configure_other_sib(gNB_MAC_INST *nrmac, int num_cu_sib, const f1ap_ return true; } +bool nr_update_sib19(const gnb_sat_position_update_t *sat_position) +{ + gNB_MAC_INST *nrmac = RC.nrmac[0]; + NR_COMMON_channels_t *cc = &nrmac->common_channels[0]; + NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; + + if (!scc || !scc->ext2 || !scc->ext2->ntn_Config_r17) + return false; + + const vector_t *pos = &sat_position->position; + const vector_t *vel = &sat_position->velocity; + + LOG_D(NR_MAC, "SFN = %d, SubFrame = %d\n", sat_position->sfn, sat_position->subframe); + LOG_D(NR_MAC, "TA_Common: value %d, %f msec\n", sat_position->delay, sat_position->delay * 4.072e-6); + LOG_D(NR_MAC, "TA_CommonDrift: value %d, = %f µsec/sec\n", sat_position->drift, sat_position->drift * 0.2e-3); + LOG_D(NR_MAC, "TA_CommonDriftVariant: value %d, %f µsec/sec^2\n", sat_position->accel, sat_position->accel * 0.2e-4); + LOG_D(NR_MAC, "SAT Position: values %d/%d/%d, %.3f/%3f/%3f metres in X/Y/Z\n", pos->X, pos->Y, pos->Z, pos->X * 1.3, pos->Y * 1.3, pos->Z * 1.3); + LOG_D(NR_MAC, "SAT Velocity: values %d/%d/%d, %.3f/%3f/%3f m/s in X/Y/Z\n", vel->X, vel->Y, vel->Z, vel->X * 0.06, vel->Y * 0.06, vel->Z * 0.06); + + NR_SCHED_LOCK(&nrmac->sched_lock); + + if (!scc->ext2->ntn_Config_r17->epochTime_r17) + scc->ext2->ntn_Config_r17->epochTime_r17 = calloc (1, sizeof(*scc->ext2->ntn_Config_r17->epochTime_r17)); + + NR_EpochTime_r17_t *epoch_time_r17 = scc->ext2->ntn_Config_r17->epochTime_r17; + epoch_time_r17->sfn_r17 = sat_position->sfn; + epoch_time_r17->subFrameNR_r17 = sat_position->subframe; + + if (!scc->ext2->ntn_Config_r17->ta_Info_r17) + scc->ext2->ntn_Config_r17->ta_Info_r17 = calloc(1, sizeof(*scc->ext2->ntn_Config_r17->ta_Info_r17)); + + NR_TA_Info_r17_t *sib19_ta_info = scc->ext2->ntn_Config_r17->ta_Info_r17; + + // SIB19 provides Round trip delay on feeder link (between gNB and SAT). + sib19_ta_info->ta_Common_r17 = sat_position->delay; + + if (sat_position->drift) { + if (!sib19_ta_info->ta_CommonDrift_r17) + sib19_ta_info->ta_CommonDrift_r17 = calloc(1, sizeof(*sib19_ta_info->ta_CommonDrift_r17)); + *sib19_ta_info->ta_CommonDrift_r17 = sat_position->drift; + } else + free_and_zero(sib19_ta_info->ta_CommonDrift_r17); + + if (sat_position->accel) { + if (!sib19_ta_info->ta_CommonDriftVariant_r17) + sib19_ta_info->ta_CommonDriftVariant_r17 = calloc(1, sizeof(*sib19_ta_info->ta_CommonDriftVariant_r17)); + *sib19_ta_info->ta_CommonDriftVariant_r17 = sat_position->accel; + } else + free_and_zero(sib19_ta_info->ta_CommonDriftVariant_r17); + + // Currently PositionVelocity is supported and not yet the OrbitalParams + if (!scc->ext2->ntn_Config_r17->ephemerisInfo_r17) { + scc->ext2->ntn_Config_r17->ephemerisInfo_r17 = calloc(1, sizeof(*scc->ext2->ntn_Config_r17->ephemerisInfo_r17)); + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->present = NR_EphemerisInfo_r17_PR_NOTHING; + } + if (scc->ext2->ntn_Config_r17->ephemerisInfo_r17->present == NR_EphemerisInfo_r17_PR_orbital_r17) { + ASN_STRUCT_FREE(asn_DEF_NR_Orbital_r17, scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.orbital_r17); + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.orbital_r17 = NULL; + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->present = NR_EphemerisInfo_r17_PR_NOTHING; + } + if (!scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17) + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17 = + calloc(1, sizeof(*scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17)); + + scc->ext2->ntn_Config_r17->ephemerisInfo_r17->present = NR_EphemerisInfo_r17_PR_positionVelocity_r17; + NR_PositionVelocity_r17_t *sib19_PosVel = scc->ext2->ntn_Config_r17->ephemerisInfo_r17->choice.positionVelocity_r17; + + sib19_PosVel->positionX_r17 = pos->X; + sib19_PosVel->positionY_r17 = pos->Y; + sib19_PosVel->positionZ_r17 = pos->Z; + sib19_PosVel->velocityVX_r17 = vel->X; + sib19_PosVel->velocityVY_r17 = vel->Y; + sib19_PosVel->velocityVZ_r17 = vel->Z; + + NR_SystemInformation_IEs_t *sysInfov17 = calloc(1, sizeof(*sysInfov17)); // for othersibs + struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type_du = calloc(1, sizeof(*type_du)); + type_du->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib19_v1700; + NR_SIB19_r17_t *sib19 = get_SIB19_NR(cc->ServingCellConfigCommon); + type_du->choice.sib19_v1700 = sib19; + add_sib_to_systeminformation(sysInfov17, type_du); + + cc->other_sib_bcch_length[1] = encode_sysinfo_ie(sysInfov17, cc->other_sib_bcch_pdu[1], sizeof(cc->other_sib_bcch_pdu[1])); + AssertFatal(cc->other_sib_bcch_length[1] > 0, "could not encode SIB19\n"); + ASN_STRUCT_FREE(asn_DEF_NR_SystemInformation_IEs, sysInfov17); + + NR_SCHED_UNLOCK(&nrmac->sched_lock); + + return true; +} + void prepare_du_configuration_update(gNB_MAC_INST *mac, f1ap_served_cell_info_t *info, NR_BCCH_BCH_Message_t *mib, diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_config.h b/openair2/LAYER2/NR_MAC_gNB/mac_config.h new file mode 100644 index 0000000000..1fdbe62115 --- /dev/null +++ b/openair2/LAYER2/NR_MAC_gNB/mac_config.h @@ -0,0 +1,46 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef __LAYER2_NR_MAC_CONFIG_H__ +#define __LAYER2_NR_MAC_CONFIG_H__ + +#include + +typedef struct vector_s { + int X; + int Y; + int Z; +} vector_t; + +// Format similar to values sent in SIB19 +typedef struct gnb_sat_position_update_s { + int sfn; + int subframe; + uint32_t delay; + int drift; + uint32_t accel; + vector_t position; + vector_t velocity; +} gnb_sat_position_update_t; + +bool nr_update_sib19(const gnb_sat_position_update_t *sat_position); + +#endif /*__LAYER2_NR_MAC_CONFIG_H__*/ diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index fc1254c802..ea2cc617bd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -88,6 +88,7 @@ /* MAC */ #include "LAYER2/NR_MAC_COMMON/nr_mac_common.h" +#include "LAYER2/NR_MAC_gNB/mac_config.h" #include "NR_TAG.h" /* Defs */ diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index f858a10599..da1b3d8ba0 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -195,7 +195,7 @@ static void get_epochtime_from_sib19scheduling(NR_UE_RRC_SI_INFO *SI_info, int * int slot = slot_end_sib19_window % N; *subframe = ceil(slot / slots_per_subframe); - LOG_D(NR_RRC, + LOG_I(NR_RRC, "Get EPOCHTIME: x:%d, N:%d, slot_endw:%d, frame:%d, subframe:%d , slot:%d\n", x, N, @@ -228,7 +228,7 @@ static int eval_epoch_time(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntnc // after the frame where the message indicating the epochTime is received // i.e. Epochframe can be present or future SFN diff_frames = (epoch_frame - frame + 1024) % 1024; // According to 38.331 Epochtime is defined for serving cell like this - LOG_D(NR_RRC, "Epoch frame %d, ahead by %d frames\n", epoch_frame, diff_frames); + LOG_I(NR_RRC, "Epoch frame %d, ahead by %d frames\n", epoch_frame, diff_frames); return diff_frames; } @@ -248,7 +248,7 @@ static int get_ntn_timervalues(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t * int expire_before_ms = ((val430 >= 120) ? 10000 : 2000); int diff = *val430_ms - expire_before_ms; int sib19_timer_ms = (diff > 0) ? diff : ((*val430_ms - sib19_periodicity_ms) > 0) ? (*val430_ms - sib19_periodicity_ms) : 0; - LOG_D(NR_RRC, "val430:%d s, T430:%d ms, sib19_timer:%d ms\n", val430, *val430_ms, sib19_timer_ms); + LOG_I(NR_RRC, "val430:%d s, T430:%d ms, sib19_timer:%d ms\n", val430, *val430_ms, sib19_timer_ms); return sib19_timer_ms; } diff --git a/radio/rfsimulator/apply_channelmod.c b/radio/rfsimulator/apply_channelmod.c index efeb03b25c..a976c63264 100644 --- a/radio/rfsimulator/apply_channelmod.c +++ b/radio/rfsimulator/apply_channelmod.c @@ -25,6 +25,7 @@ #include #include #include +#include "openair2/LAYER2/NR_MAC_gNB/mac_config.h" #include "rfsimulator.h" /* @@ -95,7 +96,13 @@ void rxAddInput(const c16_t *input_sig, const double vel_ue_sat = (vel_sat_x * dir_ue_sat_x + vel_sat_y * dir_ue_sat_y + vel_sat_z * dir_ue_sat_z) / dist_ue_sat; double dist_sat_gnb = 0; + double vel_sat_gnb = 0; + double acc_sat_gnb = 0; if (channelDesc->modelid == SAT_LEO_TRANS) { + const double acc_sat_x = 0; + const double acc_sat_y = -w_sat * w_sat * radius_sat * sin(w_sat * t); + const double acc_sat_z = -w_sat * w_sat * radius_sat * cos(w_sat * t); + const double pos_gnb_x = 0; const double pos_gnb_y = 0; const double pos_gnb_z = radius_earth; @@ -105,6 +112,8 @@ void rxAddInput(const c16_t *input_sig, const double dir_sat_gnb_z = pos_gnb_z - pos_sat_z; dist_sat_gnb = sqrt(dir_sat_gnb_x * dir_sat_gnb_x + dir_sat_gnb_y * dir_sat_gnb_y + dir_sat_gnb_z * dir_sat_gnb_z); + vel_sat_gnb = (vel_sat_x * dir_sat_gnb_x + vel_sat_y * dir_sat_gnb_y + vel_sat_z * dir_sat_gnb_z) / dist_sat_gnb; + acc_sat_gnb = (acc_sat_x * dir_sat_gnb_x + acc_sat_y * dir_sat_gnb_y + acc_sat_z * dir_sat_gnb_z) / dist_sat_gnb; } const double prop_delay = (dist_ue_sat + dist_sat_gnb) / c; @@ -117,8 +126,33 @@ void rxAddInput(const c16_t *input_sig, if(TS - last_TS >= channelDesc->sampling_rate) { last_TS = TS; - LOG_D(HW, "Satellite orbit: time %f s, Position = (%f, %f, %f), Velocity = (%f, %f, %f)\n", t, pos_sat_x, pos_sat_y, pos_sat_z, vel_sat_x, vel_sat_y, vel_sat_z); - LOG_D(HW, "Uplink delay %f ms, Doppler shift UE->SAT %f kHz\n", prop_delay * 1000, f_Doppler_shift_ue_sat / 1000); + LOG_I(HW, "Satellite orbit: time %f s, Position = (%f, %f, %f), Velocity = (%f, %f, %f)\n", t, pos_sat_x, pos_sat_y, pos_sat_z, vel_sat_x, vel_sat_y, vel_sat_z); + LOG_I(HW, "Uplink delay %f ms, Doppler shift UE->SAT %f kHz\n", prop_delay * 1000, f_Doppler_shift_ue_sat / 1000); + LOG_I(HW, "Satellite velocity towards gNB: %f m/s, acceleration towards gNB: %f m/s²\n", vel_sat_gnb, acc_sat_gnb); + } + + const int samples_per_subframe = channelDesc->sampling_rate / 1000; + const int abs_subframe = TS / samples_per_subframe; + if (abs_subframe % 10 == 0) { // update SIB19 information for the next frame + gnb_sat_position_update_t sat_position = { + .sfn = (abs_subframe / 10 + 1) % 1024, + .subframe = 0, + .delay = 2 * dist_sat_gnb / (c * 4.072e-9), + .drift = 2 * -vel_sat_gnb / (c * 0.2e-9), + .accel = 2 * acc_sat_gnb / (c * 0.2e-10), + .position.X = pos_sat_x / 1.3, + .position.Y = pos_sat_y / 1.3, + .position.Z = pos_sat_z / 1.3, + .velocity.X = vel_sat_x / 0.06, + .velocity.Y = vel_sat_y / 0.06, + .velocity.Z = vel_sat_z / 0.06, + }; + // Here we update the SIB19 information directly in the gNB MAC layer. + // Without rf-simulaor, in a real system or with an external channel emulator, the SIB19 updates would + // be provided via an external interface (e.g. O-RAN E2 interface) to the MAC layer (in the O-DU). + // We do it directly here, because we can, and an E2 Interface implementation (e.g using FlexRIC) + // would pull too many dependencies into rf-simulator, just for updating SIB19. + nr_update_sib19(&sat_position); } } else { const double dir_sat_ue_x = pos_ue_x - pos_sat_x; @@ -151,8 +185,8 @@ void rxAddInput(const c16_t *input_sig, if(TS - last_TS >= channelDesc->sampling_rate) { last_TS = TS; - LOG_D(HW, "Satellite orbit: time %f s, Position = (%f, %f, %f), Velocity = (%f, %f, %f)\n", t, pos_sat_x, pos_sat_y, pos_sat_z, vel_sat_x, vel_sat_y, vel_sat_z); - LOG_D(HW, "Downlink delay %f ms, Doppler shift SAT->UE %f kHz\n", prop_delay * 1000, f_Doppler_shift_sat_ue / 1000); + LOG_I(HW, "Satellite orbit: time %f s, Position = (%f, %f, %f), Velocity = (%f, %f, %f)\n", t, pos_sat_x, pos_sat_y, pos_sat_z, vel_sat_x, vel_sat_y, vel_sat_z); + LOG_I(HW, "Downlink delay %f ms, Doppler shift SAT->UE %f kHz\n", prop_delay * 1000, f_Doppler_shift_sat_ue / 1000); } } }