From 13933f9db886b0d6caa367d9d150016d1eb4664e Mon Sep 17 00:00:00 2001 From: alexjiao Date: Wed, 5 Nov 2025 22:36:25 +0800 Subject: [PATCH] Refactor SSB search to common function Extract common SSB search logic into nr_search_ssb_common() to enable code reuse between initial sync and neighbor cell measurements. --- .../PHY/NR_UE_TRANSPORT/nr_initial_sync.c | 210 +++++++++++++----- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 7 + openair1/PHY/defs_nr_UE.h | 24 ++ 3 files changed, 181 insertions(+), 60 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c b/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c index da70fd50e8..e747b86528 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c @@ -171,6 +171,114 @@ static void compensate_freq_offset(c16_t **x, const NR_DL_FRAME_PARMS *fp, const } } +/* + * Common SSB search function used by both initial sync and neighbor cell search + */ +bool nr_search_ssb_common(nr_ssb_search_params_t *params) +{ + const NR_DL_FRAME_PARMS *fp = params->frame_parms; + + const uint32_t pssTime_sz = fp->ofdm_symbol_size; + c16_t(*pssTime)[pssTime_sz] = (c16_t(*)[pssTime_sz])params->pssTime; + + // Perform PSS search + int nid2 = -1; + int freq_offset_pss = 0; + int pss_peak = 0; + int pss_avg = 0; + const int sync_pos = pss_synchro_nr((const c16_t **)params->rxdata, + fp, + pssTime, + params->search_frame_id, + params->fo_flag, + params->target_nid_cell, + &nid2, + &freq_offset_pss, + &pss_peak, + &pss_avg); + + if (params->pss_peak) + *params->pss_peak = pss_peak; + if (params->pss_avg) + *params->pss_avg = pss_avg; + if (params->freq_offset_pss) + *params->freq_offset_pss = freq_offset_pss; + + if (sync_pos < fp->nb_prefix_samples || nid2 < 0) { + return false; + } + + const int ssb_offset = sync_pos - fp->nb_prefix_samples; + if (params->ssb_offset) + *params->ssb_offset = ssb_offset; + +#ifdef DEBUG_INITIAL_SYNCH + LOG_I(PHY, "Initial sync : Estimated PSS position %d, Nid2 %d, ssb offset %d\n", sync_pos, nid2, ssb_offset); +#endif + + // Check that SSB fits within buffer + if (ssb_offset + NR_N_SYMBOLS_SSB * (fp->ofdm_symbol_size + fp->nb_prefix_samples) >= params->rxdata_size) { + LOG_D(PHY, + "SSB extends beyond buffer boundary (sync_pos %d, ssb_offset %d, buffer_size %d)\n", + sync_pos, + ssb_offset, + params->rxdata_size); + return false; + } + + // Apply frequency offset compensation if requested + if (params->apply_freq_offset && freq_offset_pss != 0) { + compensate_freq_offset(params->rxdata, fp, freq_offset_pss, params->search_frame_id); + } + + // Extract SSB symbols to frequency domain + // Symbol ordering: 0=PSS, 1=PBCH, 2=SSS, 3=PBCH + const uint32_t rxdataF_sz = fp->samples_per_slot_wCP; + c16_t(*rxdataF)[rxdataF_sz] = (c16_t(*)[rxdataF_sz])params->rxdataF; + const int sample_offset = params->search_frame_id * fp->samples_per_frame + ssb_offset; + for (int i = 0; i < NR_N_SYMBOLS_SSB; i++) { + nr_slot_fep(NULL, fp, 0, i, rxdataF, link_type_dl, sample_offset, (c16_t **)params->rxdata); + } + + // Perform SSS detection + int detected_nid_cell = -1; + int32_t sss_metric = 0; + uint8_t sss_phase = 0; + int freq_offset_sss = 0; + + bool sss_detected = rx_sss_nr(fp, + nid2, + params->target_nid_cell, + freq_offset_pss, + params->ssb_start_subcarrier, + &detected_nid_cell, + &sss_metric, + &sss_phase, + &freq_offset_sss, + rxdataF); + + if (params->sss_metric) + *params->sss_metric = sss_metric; + if (params->sss_phase) + *params->sss_phase = sss_phase; + if (params->freq_offset_sss) + *params->freq_offset_sss = freq_offset_sss; + + if (!sss_detected || detected_nid_cell < 0) { + return false; + } + + // Check if we should exclude the serving cell + if (params->exclude_nid_cell >= 0 && detected_nid_cell == params->exclude_nid_cell) { + return false; + } + + if (params->detected_nid_cell) + *params->detected_nid_cell = detected_nid_cell; + + return true; +} + void nr_scan_ssb(void *arg) { /* Initial synchronisation @@ -196,79 +304,61 @@ void nr_scan_ssb(void *arg) for (int nid2 = 0; nid2 < pss_sequence; nid2++) generate_pss_nr_time(fp, nid2, ssbInfo->gscnInfo.ssbFirstSC, pssTime[nid2]); + const uint32_t rxdataF_sz = fp->samples_per_slot_wCP; + __attribute__((aligned(32))) c16_t rxdataF[fp->nb_antennas_rx][rxdataF_sz]; + // initial sync performed on two successive frames, if pbch passes on first frame, no need to process second frame // only one frame is used for simulation tools for (int frame_id = 0; frame_id < ssbInfo->nFrames && !ssbInfo->syncRes.cell_detected; frame_id++) { if (ssbInfo->freqOffset) compensate_freq_offset(rxdata, fp, ssbInfo->freqOffset, frame_id); - /* process pss search on received buffer */ - ssbInfo->syncRes.frame_id = frame_id; - int nid2; + int detected_nid_cell = -1; + int ssb_offset = 0; int freq_offset_pss = 0; - const int sync_pos = pss_synchro_nr((const c16_t **)rxdata, - fp, - pssTime, - frame_id, - ssbInfo->foFlag, - ssbInfo->targetNidCell, - &nid2, - &freq_offset_pss, - &ssbInfo->pssCorrPeakPower, - &ssbInfo->pssCorrAvgPower); - if (sync_pos < fp->nb_prefix_samples) - continue; - - ssbInfo->ssbOffset = sync_pos - fp->nb_prefix_samples; - -#ifdef DEBUG_INITIAL_SYNCH - LOG_I(PHY, "Initial sync : Estimated PSS position %d, Nid2 %d, ssb offset %d\n", sync_pos, nid2, ssbInfo->ssbOffset); -#endif - /* check that SSS/PBCH block is continuous inside the received buffer */ - if (ssbInfo->ssbOffset + NR_N_SYMBOLS_SSB * (fp->ofdm_symbol_size + fp->nb_prefix_samples) >= fp->samples_per_frame) { - LOG_D(PHY, "Can't try to decode SSS from PSS position, will retry (PSS circular buffer wrapping): sync_pos %d\n", sync_pos); - continue; - } - - // digital compensation of FFO for SSB symbols - if (ssbInfo->foFlag) { - compensate_freq_offset(rxdata, fp, freq_offset_pss, frame_id); - } - - /* slot_fep function works for lte and takes into account begining of frame with prefix for subframe 0 */ - /* for NR this is not the case but slot_fep is still used for computing FFT of samples */ - /* in order to achieve correct processing for NR prefix samples is forced to 0 and then restored after function call */ - /* symbol number are from beginning of SS/PBCH blocks as below: */ - /* Signal PSS PBCH SSS PBCH */ - /* symbol number 0 1 2 3 */ - /* time samples in buffer rxdata are used as input of FFT -> FFT results are stored in the frequency buffer rxdataF */ - /* rxdataF stores SS/PBCH from beginning of buffers in the same symbol order as in time domain */ - - const uint32_t rxdataF_sz = fp->samples_per_slot_wCP; - __attribute__((aligned(32))) c16_t rxdataF[fp->nb_antennas_rx][rxdataF_sz]; - for (int i = 0; i < NR_N_SYMBOLS_SSB; i++) - nr_slot_fep(NULL, fp, 0, i, rxdataF, link_type_dl, frame_id * fp->samples_per_frame + ssbInfo->ssbOffset, (c16_t **)rxdata); - int freq_offset_sss = 0; - int32_t metric_tdd_ncp = 0; - uint8_t phase_tdd_ncp; - ssbInfo->syncRes.cell_detected = rx_sss_nr(fp, - nid2, - ssbInfo->targetNidCell, - freq_offset_pss, - ssbInfo->gscnInfo.ssbFirstSC, - &ssbInfo->nidCell, - &metric_tdd_ncp, - &phase_tdd_ncp, - &freq_offset_sss, - rxdataF); + int32_t sss_metric = 0; + uint8_t sss_phase = 0; + + nr_ssb_search_params_t search_params = { + .frame_parms = fp, + .rxdata = rxdata, + .rxdata_size = fp->samples_per_frame, + .ssb_start_subcarrier = ssbInfo->gscnInfo.ssbFirstSC, + .target_nid_cell = ssbInfo->targetNidCell, + .exclude_nid_cell = -1, // No exclusion for initial sync + .apply_freq_offset = ssbInfo->foFlag, + .search_frame_id = frame_id, + .fo_flag = ssbInfo->foFlag, + .rxdataF = rxdataF, + .pssTime = pssTime, + .detected_nid_cell = &detected_nid_cell, + .ssb_offset = &ssb_offset, + .sss_metric = &sss_metric, + .freq_offset_pss = &freq_offset_pss, + .freq_offset_sss = &freq_offset_sss, + .sss_phase = &sss_phase, + .pss_peak = &ssbInfo->pssCorrPeakPower, + .pss_avg = &ssbInfo->pssCorrAvgPower, + }; + + ssbInfo->syncRes.frame_id = frame_id; + ssbInfo->syncRes.cell_detected = nr_search_ssb_common(&search_params); + + if (!ssbInfo->syncRes.cell_detected) { + continue; + } + + ssbInfo->ssbOffset = ssb_offset; + ssbInfo->nidCell = detected_nid_cell; + #ifdef DEBUG_INITIAL_SYNCH LOG_I(PHY, "TDD Normal prefix: sss detection result; %d, CellId %d metric %d, phase %d, measured offset %d\n", ssbInfo->syncRes.cell_detected, ssbInfo->nidCell, - metric_tdd_ncp, - phase_tdd_ncp, + sss_metric, + sss_phase, ssbInfo->syncRes.rx_offset); #endif ssbInfo->freqOffset += freq_offset_pss + freq_offset_sss; 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 2a24902295..b7f7330995 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -241,6 +241,13 @@ nr_initial_sync_t nr_initial_sync(UE_nr_rxtx_proc_t *proc, nr_gscn_info_t gscnInfo[MAX_GSCN_BAND], int numGscn); +/*! + \brief Common SSB search function shared by initial sync and neighbor cell search + @param params Pointer to SSB search parameters structure + @return true if SSB was successfully detected, false otherwise +*/ +bool nr_search_ssb_common(nr_ssb_search_params_t *params); + /*! \brief This function gets the carrier frequencies either from FP or command-line-set global variables, depending on the availability of the latter diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index a166dc13cd..1cc3225d1a 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -573,6 +573,30 @@ typedef struct { task_ans_t *ans; } nr_ue_ssb_scan_t; +// Common SSB search parameters - used by both initial sync and neighbor cell search +typedef struct { + const NR_DL_FRAME_PARMS *frame_parms; + c16_t **rxdata; + uint32_t rxdata_size; + int ssb_start_subcarrier; + int target_nid_cell; // -1 for blind search, specific PCI for targeted search + int exclude_nid_cell; // -1 for no exclusion, or serving cell PCI to exclude + bool apply_freq_offset; // whether to compensate frequency offset + int search_frame_id; // Frame index to search (0, 1, 2...) within rxdata buffer + bool fo_flag; // frequency offset estimation flag for pss_synchro_nr() + void *rxdataF; // Pre-allocated rxdataF buffer + void *pssTime; // Pre-generated PSS time sequences + // Output parameters + int *detected_nid_cell; // detected PCI + int *ssb_offset; // SSB offset in samples + int32_t *sss_metric; // SSS detection metric + int *freq_offset_pss; // PSS frequency offset estimate + int *freq_offset_sss; // SSS frequency offset estimate + uint8_t *sss_phase; // SSS phase + int *pss_peak; // PSS correlation peak power + int *pss_avg; // PSS correlation average power +} nr_ssb_search_params_t; + typedef struct nr_phy_data_tx_s { NR_UE_ULSCH_t ulsch; NR_UE_PUCCH pucch_vars;