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.
This commit is contained in:
alexjiao
2026-01-09 11:25:16 +08:00
parent 699a5453a0
commit 13933f9db8
3 changed files with 181 additions and 60 deletions
+150 -60
View File
@@ -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;
@@ -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
+24
View File
@@ -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;