From 3aad8d774805a03a974f06437584d5f94b68678e Mon Sep 17 00:00:00 2001 From: francescomani Date: Sun, 28 Dec 2025 11:14:55 +0100 Subject: [PATCH] move NTN L1 functions to a new file from nr-ue --- CMakeLists.txt | 1 + executables/nr-ue.c | 171 ---------------- openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c | 191 ++++++++++++++++++ .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 12 +- 4 files changed, 203 insertions(+), 172 deletions(-) create mode 100644 openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 70ea409d36..036088abf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -979,6 +979,7 @@ set(PHY_SRC_UE ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/cic_filter_nr.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_initial_sync.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_initial_sync_sl.c + ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_ue_rf_helpers.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_pbch.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 2772fc5aae..f9740978cd 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -497,8 +497,6 @@ 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; @@ -735,175 +733,6 @@ 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) -{ - 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; - - 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) - *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_ntn_config_t *ntn_config_params = &UE->nrUE_config.ntn_config; - - // Handle terrestrial networks - if (ntn_config_params->cell_specific_k_offset == 0) { - if (abs_subframe_tx >= 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; - 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; - - 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); - - // 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}; - - // 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 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; - - const double N_common_ta_adj = ntn_config_params->N_common_ta_adj; - 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; - - 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; - - 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, - int frame_rx, - int slot_rx, - int *duration_rx_to_tx, - int *timing_advance, - int *ntn_koffset, - bool *ntn_targetcell) -{ - // return if there is no pending update - if (!UE->nrUE_config.ntn_config.params_changed) - return; - - *ntn_targetcell = UE->nrUE_config.ntn_config.is_targetcell; - - // skip update if it is for the target cell - if (UE->nrUE_config.ntn_config.is_targetcell) - return; - - UE->nrUE_config.ntn_config.params_changed = false; - - 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; - - *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) { //this thread should be over the processing thread to keep in real time diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c new file mode 100644 index 0000000000..51031d5449 --- /dev/null +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c @@ -0,0 +1,191 @@ +/* + * 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 + */ + +#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h" + +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; + + 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) + *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 +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_ntn_config_t *ntn_config_params = &UE->nrUE_config.ntn_config; + + // Handle terrestrial networks + if (ntn_config_params->cell_specific_k_offset == 0) { + if (abs_subframe_tx >= 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; + 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; + + 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); + + // 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}; + + // 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 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; + + const double N_common_ta_adj = ntn_config_params->N_common_ta_adj; + 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; + + 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; + + 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 +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, + bool *ntn_targetcell) +{ + // return if there is no pending update + if (!UE->nrUE_config.ntn_config.params_changed) + return; + + *ntn_targetcell = UE->nrUE_config.ntn_config.is_targetcell; + + // skip update if it is for the target cell + if (UE->nrUE_config.ntn_config.is_targetcell) + return; + + UE->nrUE_config.ntn_config.params_changed = false; + + 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; + + *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); +} diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index ed348986e8..2a24902295 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -330,7 +330,17 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, pdsch_scope_req_t *scope_req); int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t slot, c16_t **txData); - +void apply_ntn_config(PHY_VARS_NR_UE *UE, + NR_DL_FRAME_PARMS *fp, + int hfn_rx, + int frame_rx, + int slot_rx, + int *duration_rx_to_tx, + int *timing_advance, + int *ntn_koffset, + bool *ntn_targetcell); +void fix_ntn_epoch_hfn(PHY_VARS_NR_UE *UE, int hfn, int frame); +void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_PARMS *fp, int abs_subframe_tx); void dump_nrdlsch(PHY_VARS_NR_UE *ue,uint8_t gNB_id,uint8_t nr_slot_rx,unsigned int *coded_bits_per_codeword,int round, unsigned char harq_pid); void nr_a_sum_b(c16_t *input_x, c16_t *input_y, unsigned short nb_rb);