From 757a3131a1024564118c1f60f74f9b0d4e3dbfb4 Mon Sep 17 00:00:00 2001 From: Raghavendra Dinavahi Date: Thu, 12 Feb 2026 20:20:55 +0100 Subject: [PATCH] UE: Fix if target cell has SSB at a different offset as in source cell Actual problem is that SYNC does not happen on the target cell as frame parameters is not updated and ssb start carrier is not correct in the UE frame parameters function nr_get_ssb_start_sc to determine ssb_start_subcarrier --- executables/nr-ue.c | 15 ++++++++++-- openair1/PHY/INIT/nr_parms.c | 14 ++++------- .../PHY/nr_phy_common/inc/nr_phy_common.h | 5 ++++ .../PHY/nr_phy_common/src/nr_phy_common.c | 23 +++++++++++++++++++ openair1/SCHED_NR/phy_procedures_nr_gNB.c | 16 ++++--------- openair2/LAYER2/NR_MAC_UE/config_ue.c | 21 +++++++++-------- 6 files changed, 61 insertions(+), 33 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 88a8ff84c8..bde020cbc7 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -486,12 +486,13 @@ static int handle_sync_req_from_mac(PHY_VARS_NR_UE *UE) UE->UE_scan_carrier = false; UE->target_Nid_cell = UE->synch_request.synch_req.target_Nid_cell; - const fapi_nr_ue_carrier_config_t *cfg = &UE->nrUE_config.carrier_config; + const fapi_nr_config_request_t *config = &UE->nrUE_config; + const fapi_nr_ue_carrier_config_t *cfg = &config->carrier_config; uint64_t dl_CarrierFreq = get_carrier_frequency(fp->N_RB_DL, fp->numerology_index, cfg->dl_frequency); uint64_t ul_CarrierFreq = get_carrier_frequency(fp->N_RB_UL, fp->numerology_index, cfg->uplink_frequency); if (dl_CarrierFreq != fp->dl_CarrierFreq || ul_CarrierFreq != fp->ul_CarrierFreq) { LOG_I(NR_PHY, - "[UE %d] RF frequency change: dl %lu->%lu Hz, ul %lu->%lu Hz (from dl_frequency=%u kHz, target_Nid_cell=%d)\n", + "[UE %d] SYNC REQ: RF frequency change: dl %lu->%lu Hz, ul %lu->%lu Hz (from dl_frequency=%u kHz, target_Nid_cell=%d)\n", UE->Mod_id, fp->dl_CarrierFreq, dl_CarrierFreq, @@ -505,6 +506,16 @@ static int handle_sync_req_from_mac(PHY_VARS_NR_UE *UE) init_symbol_rotation(fp); } + int ssb_start_subcarrier = nr_get_ssb_start_sc(fp->numerology_index, + config->ssb_table.ssb_offset_point_a, + config->ssb_table.ssb_subcarrier_offset, + fp->freq_range); + // SSB location can change during for ex: handover on the target cell + if (ssb_start_subcarrier != fp->ssb_start_subcarrier) { + fp->ssb_start_subcarrier = ssb_start_subcarrier; + LOG_I(NR_PHY, "SYNC REQ: SSB location changed:%d\n", fp->ssb_start_subcarrier); + } + // Apply Doppler based on NTN-Config for target cell if (UE->nrUE_config.ntn_config.is_targetcell) apply_ntn_timing_advance_and_doppler(UE, fp, -1); diff --git a/openair1/PHY/INIT/nr_parms.c b/openair1/PHY/INIT/nr_parms.c index 66f766571d..0e39e19d9e 100644 --- a/openair1/PHY/INIT/nr_parms.c +++ b/openair1/PHY/INIT/nr_parms.c @@ -24,6 +24,7 @@ #include "common/utils/LOG/log.h" #include "executables/softmodem-common.h" #include "PHY/MODULATION/nr_modulation.h" +#include "PHY/nr_phy_common/inc/nr_phy_common.h" /// Subcarrier spacings in Hz indexed by numerology index static const uint32_t nr_subcarrier_spacing[MAX_NUM_SUBCARRIER_SPACING] = {15e3, 30e3, 60e3, 120e3, 240e3}; @@ -421,16 +422,11 @@ int nr_init_frame_parms_ue(NR_DL_FRAME_PARMS *fp, fapi_nr_config_request_t* conf fp->samples_per_frame = 10 * fp->samples_per_subframe; fp->freq_range = get_freq_range_from_freq(fp->dl_CarrierFreq); - uint8_t sco = 0; - if (((fp->freq_range == FR1) && (config->ssb_table.ssb_subcarrier_offset < 24)) || - ((fp->freq_range == FR2) && (config->ssb_table.ssb_subcarrier_offset < 12))) { - if (fp->freq_range == FR1) - sco = config->ssb_table.ssb_subcarrier_offset>>config->ssb_config.scs_common; - else - sco = config->ssb_table.ssb_subcarrier_offset; - } + fp->ssb_start_subcarrier = nr_get_ssb_start_sc(config->ssb_config.scs_common, + config->ssb_table.ssb_offset_point_a, + config->ssb_table.ssb_subcarrier_offset, + fp->freq_range); - fp->ssb_start_subcarrier = (12 * config->ssb_table.ssb_offset_point_a + sco); set_Lmax(fp); fp->L_ssb = (((uint64_t) config->ssb_table.ssb_mask_list[0].ssb_mask)<<32) | config->ssb_table.ssb_mask_list[1].ssb_mask; diff --git a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h index b4c0e1239c..67bed6eef4 100644 --- a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h +++ b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h @@ -389,4 +389,9 @@ void nr_generate_csi_rs(const NR_DL_FRAME_PARMS *frame_parms, const uint8_t cdm_type, c16_t **dataF); +int nr_get_ssb_start_sc(int scs, + int ssb_offset_point_a, + int ssb_sco, + frequency_range_t freq_range); + #endif diff --git a/openair1/PHY/nr_phy_common/src/nr_phy_common.c b/openair1/PHY/nr_phy_common/src/nr_phy_common.c index a723780f94..c29a3a4936 100644 --- a/openair1/PHY/nr_phy_common/src/nr_phy_common.c +++ b/openair1/PHY/nr_phy_common/src/nr_phy_common.c @@ -481,3 +481,26 @@ void nr_fo_compensation(double fo_Hz, int samples_per_ms, int sample_offset, con mult_complex_vectors(rxdata_in, rot, rxdata_out, size, 14); #endif } + +/*! +* Setting the first subcarrier +* 3GPP TS 38.211 sections 7.4.3.1 and 4.4.4.2 +* for FR1 offsetToPointA and k_SSB are expressed in terms of 15 kHz SCS +* for FR2 offsetToPointA is expressed in terms of 60 kHz SCS and k_SSB expressed in terms of the SCS provided +* by the higher-layer parameter subCarrierSpacingCommon +*/ +int nr_get_ssb_start_sc(int scs, int ssb_offset_point_a, int ssb_sco, frequency_range_t freq_range) +{ + const int prb_offset = + (freq_range == FR1) ? ssb_offset_point_a >> scs : ssb_offset_point_a >> (scs - 2); + const int sc_offset = + (freq_range == FR1) ? ssb_sco >> scs : ssb_sco; + + int ssb_start_subcarrier = (12 * prb_offset + sc_offset); + + LOG_D(NR_PHY, "prb_offset:%d, ssb_subcarrier_offset:%d,scs :%d, Fr:%d, ssb_start_subcarrier:%d\n", + prb_offset, ssb_sco, scs, freq_range, ssb_start_subcarrier); + + return ssb_start_subcarrier; + +} diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index e304fe3dc9..baccacbf26 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -118,9 +118,10 @@ void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, const n // by the higher-layer parameter subCarrierSpacingCommon nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; const int scs = cfg->ssb_config.scs_common.value; - const int prb_offset = (fp->freq_range == FR1) ? pdu->ssbOffsetPointA >> scs : pdu->ssbOffsetPointA >> (scs - 2); - const int sc_offset = (fp->freq_range == FR1) ? pdu->SsbSubcarrierOffset >> scs : pdu->SsbSubcarrierOffset; - fp->ssb_start_subcarrier = (12 * prb_offset + sc_offset); + fp->ssb_start_subcarrier = nr_get_ssb_start_sc(scs, + pdu->ssbOffsetPointA, + pdu->SsbSubcarrierOffset, + fp->freq_range); if (fp->print_ue_help_cmdline_log && IS_SA_MODE(get_softmodem_params())) { fp->print_ue_help_cmdline_log = false; @@ -142,15 +143,6 @@ void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, const n fp->ssb_start_subcarrier, fp->threequarter_fs ? "-E" : ""); } - LOG_D(PHY, - "ssbOffsetPointA %d SSB SsbSubcarrierOffset %d prb_offset %d sc_offset %d scs %d ssb_start_subcarrier %d\n", - pdu->ssbOffsetPointA, - pdu->SsbSubcarrierOffset, - prb_offset, - sc_offset, - scs, - fp->ssb_start_subcarrier); - LOG_D(PHY,"SS TX: frame %d, slot %d, start_symbol %d\n", frame, slot, ssb_start_symbol); const nfapi_nr_tx_precoding_and_beamforming_t *pb = &pdu->precoding_and_beamforming; c16_t ***txdataF = gNB->common_vars.txdataF; diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 2a5c8e041d..5d078cf941 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -537,16 +537,15 @@ static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t // SSB Table config if (frequencyInfoDL && frequencyInfoDL->absoluteFrequencySSB) { - int scs_scaling = 1 << (cfg->ssb_config.scs_common); - if (frequencyInfoDL->absoluteFrequencyPointA < 600000) - scs_scaling = scs_scaling * 3; - if (frequencyInfoDL->absoluteFrequencyPointA > 2016666) - scs_scaling = scs_scaling >> 2; - uint32_t absolute_diff = (*frequencyInfoDL->absoluteFrequencySSB - frequencyInfoDL->absoluteFrequencyPointA); - cfg->ssb_table.ssb_offset_point_a = absolute_diff / (12 * scs_scaling) - 10; + cfg->ssb_table.ssb_offset_point_a = get_ssb_offset_to_pointA(*frequencyInfoDL->absoluteFrequencySSB, + frequencyInfoDL->absoluteFrequencyPointA, + cfg->ssb_config.scs_common, + mac->frequency_range); cfg->ssb_table.ssb_period = *scc->ssb_periodicityServingCell; // NSA -> take ssb offset from SCS - cfg->ssb_table.ssb_subcarrier_offset = absolute_diff % (12 * scs_scaling); + cfg->ssb_table.ssb_subcarrier_offset = get_ssb_subcarrier_offset(*frequencyInfoDL->absoluteFrequencySSB, + frequencyInfoDL->absoluteFrequencyPointA, + cfg->ssb_config.scs_common); cfg->ssb_table.ssb_case = set_ssb_case(*scc->ssbSubcarrierSpacing, mac->nr_band); } @@ -2113,12 +2112,14 @@ static void handle_reconfiguration_with_sync(NR_UE_MAC_INST_t *mac, ra->ra_state = nrRA_UE_IDLE; nr_ue_mac_default_configs(mac); + // PHY CONFIG request should be sent, ahead of SYNC request + // As SYNC request processes the new config + mac->if_module->phy_config_request(&mac->phy_config); + mac->phy_config.config_req.ntn_config.params_changed = false; mac->synch_request.Mod_id = mac->ue_id; mac->synch_request.CC_id = cc_idP; 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,