From 757a3131a1024564118c1f60f74f9b0d4e3dbfb4 Mon Sep 17 00:00:00 2001 From: Raghavendra Dinavahi Date: Thu, 12 Feb 2026 20:20:55 +0100 Subject: [PATCH 1/2] 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, From dafabe381c3e8584307e49df5aea022cf3d72575 Mon Sep 17 00:00:00 2001 From: Raghavendra Dinavahi Date: Thu, 12 Feb 2026 21:28:33 +0100 Subject: [PATCH 2/2] UE: update PDCCH config if MAC config changes without this fix, after sync is successful, RAR is not getting decoded. PDCCH area also changes as SSB offset can change. if MAC configuration changes. --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 4 ++ openair2/LAYER2/NR_MAC_UE/mac_proto.h | 1 + .../NR_MAC_UE/nr_ue_dci_configuration.c | 63 +++++++++++-------- 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 5d078cf941..5dcdf6a725 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -2106,6 +2106,10 @@ static void handle_reconfiguration_with_sync(NR_UE_MAC_INST_t *mac, configure_common_BWP_dl(mac, bwp_id, scc->downlinkConfigCommon->initialDownlinkBWP); if (scc->uplinkConfigCommon) configure_common_BWP_ul(mac, bwp_id, scc->uplinkConfigCommon->initialUplinkBWP); + + // Update PDCCH config as MAC configuration has changed + if (IS_SA_MODE(get_softmodem_params())) + update_pdcch_config(mac); } mac->state = UE_NOT_SYNC_RECONF; diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index 9ee1bfab28..e502d08f1f 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -377,4 +377,5 @@ void nr_ue_sidelink_scheduler(nr_sidelink_indication_t *sl_ind, NR_UE_MAC_INST_t NR_SearchSpace_t *get_common_search_space(const NR_UE_MAC_INST_t *mac, const NR_SearchSpaceId_t ss_id); ssb_ro_preambles_t get_ssb_ro_preambles_4step(struct NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB *config); +void update_pdcch_config(NR_UE_MAC_INST_t *mac); #endif diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index 8dbce0032c..43aab362f3 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -422,6 +422,38 @@ static bool monitor_dci_for_other_SI(NR_UE_MAC_INST_t *mac, return false; } +void update_pdcch_config(NR_UE_MAC_INST_t *mac) +{ + int scs = mac->current_DL_BWP ? mac->current_DL_BWP->scs : mac->numerology; + const int slots_per_frame = get_slots_per_frame_from_scs(scs); + int ssb_sc_offset_norm; + if (mac->ssb_subcarrier_offset < 24 && mac->frequency_range == FR1) + ssb_sc_offset_norm = mac->ssb_subcarrier_offset >> scs; + else + ssb_sc_offset_norm = mac->ssb_subcarrier_offset; + uint16_t ssb_offset_point_a = (mac->ssb_start_subcarrier - ssb_sc_offset_norm) / 12; + int ssb_start_symbol = get_ssb_start_symbol(mac->nr_band, scs, mac->mib_ssb); + get_type0_PDCCH_CSS_config_parameters(&mac->type0_PDCCH_CSS_config, + mac->mib_frame, + mac->mib, + slots_per_frame, + ssb_sc_offset_norm, + ssb_start_symbol, + scs, + mac->frequency_range, + mac->nr_band, + 273, // at this point UE in principle doesn't know the grid size (we assume the largest) + mac->mib_ssb, + 1, // If the UE is not configured with a periodicity, the UE assumes a periodicity of a half frame + ssb_offset_point_a); + if (mac->search_space_zero == NULL) + mac->search_space_zero = calloc(1, sizeof(*mac->search_space_zero)); + if (mac->coreset0 == NULL) + mac->coreset0 = calloc(1, sizeof(*mac->coreset0)); + fill_coresetZero(mac->coreset0, &mac->type0_PDCCH_CSS_config); + fill_searchSpaceZero(mac->search_space_zero, slots_per_frame, &mac->type0_PDCCH_CSS_config); +} + void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl_config, const frame_t frame, const int slot) { const NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP; @@ -430,37 +462,14 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl int scs = current_DL_BWP ? current_DL_BWP->scs : mac->numerology; const int slots_per_frame = get_slots_per_frame_from_scs(scs); if (mac->get_sib1) { - int ssb_sc_offset_norm; - if (mac->ssb_subcarrier_offset < 24 && mac->frequency_range == FR1) - ssb_sc_offset_norm = mac->ssb_subcarrier_offset >> scs; - else - ssb_sc_offset_norm = mac->ssb_subcarrier_offset; - uint16_t ssb_offset_point_a = (mac->ssb_start_subcarrier - ssb_sc_offset_norm) / 12; - int ssb_start_symbol = get_ssb_start_symbol(mac->nr_band, scs, mac->mib_ssb); - get_type0_PDCCH_CSS_config_parameters(&mac->type0_PDCCH_CSS_config, - mac->mib_frame, - mac->mib, - slots_per_frame, - ssb_sc_offset_norm, - ssb_start_symbol, - scs, - mac->frequency_range, - mac->nr_band, - 273, // at this point UE in principle doesn't know the grid size (we assume the largest) - mac->mib_ssb, - 1, // If the UE is not configured with a periodicity, the UE assumes a periodicity of a half frame - ssb_offset_point_a); - if (mac->search_space_zero == NULL) - mac->search_space_zero = calloc(1, sizeof(*mac->search_space_zero)); - if (mac->coreset0 == NULL) - mac->coreset0 = calloc(1, sizeof(*mac->coreset0)); - fill_coresetZero(mac->coreset0, &mac->type0_PDCCH_CSS_config); - fill_searchSpaceZero(mac->search_space_zero, slots_per_frame, &mac->type0_PDCCH_CSS_config); - if (is_ss_monitor_occasion(frame, slot, slots_per_frame, mac->search_space_zero)) { + update_pdcch_config(mac); + bool is_occasion = is_ss_monitor_occasion(frame, slot, slots_per_frame, mac->search_space_zero); + if (is_occasion) { LOG_D(NR_MAC_DCI, "Monitoring DCI for SIB1 in frame %d slot %d\n", frame, slot); config_dci_pdu(mac, dl_config, TYPE_SI_RNTI_, slot, mac->search_space_zero); } } + for (int i = 0; i < MAX_SI_GROUPS; i++) { if (!mac->get_otherSI[i]) continue;