Merge remote-tracking branch 'origin/meas-report-a3' into integration_2026_w04 (!3669)

NR UE: add neighboring cell measurements and measurement report of event A3

This is a rework of MR !2385 (closed), the goal is to add neighboring cell
measurements and measurement report of event A3 for NR UE.

This MR was tested with:
- 3rd-party gNB
- 2 OAI-gNB-DUs

The neighboring cell measurements and measurement report of event A3 for OAI-UE
are working as expected.
This commit is contained in:
Jaroslava Fiedlerova
2026-01-19 14:00:57 +01:00
18 changed files with 1006 additions and 193 deletions
+12
View File
@@ -86,6 +86,18 @@ int pss_synchro_nr(const c16_t **rxdata,
int *f_off,
int *pssPeak,
int *pssAvg);
int pss_search_time_nr(const c16_t **rxdata,
const NR_DL_FRAME_PARMS *frame_parms,
const c16_t pssTime[NUMBER_PSS_SEQUENCE][frame_parms->ofdm_symbol_size],
bool fo_flag,
int is,
int target_Nid_cell,
int *nid2,
int *f_off,
int *pssPeak,
int *pssAvg,
int search_start,
int search_length);
void generate_pss_nr_time(const NR_DL_FRAME_PARMS *fp, const int N_ID_2, int ssbFirstSCS, c16_t pssTime[fp->ofdm_symbol_size]);
int16_t *get_primary_synchro_nr2(const int nid2);
+14 -1
View File
@@ -114,10 +114,23 @@ uint32_t nr_ue_calculate_ssb_rsrp(const NR_DL_FRAME_PARMS *fp,
int ssb_start_subcarrier);
void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue,
uint8_t gNB_index,
int ssb_index,
const UE_nr_rxtx_proc_t *proc,
c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]);
// Structure to pass data to neighboring cell measurement task
typedef struct {
UE_nr_rxtx_proc_t proc;
PHY_VARS_NR_UE *ue;
c16_t *rxdata[NB_RX_ANTENNAS_MAX];
uint32_t rxdata_size;
c16_t rxdata_ant[];
} nr_meas_task_args_t;
void nr_ue_meas_neighboring_cell(void *arg);
void do_neighboring_cell_measurements(UE_nr_rxtx_proc_t *proc, PHY_VARS_NR_UE *ue, c16_t **rxdata, uint32_t rxdata_size);
void nr_ue_rrc_measurements(PHY_VARS_NR_UE *ue,
const UE_nr_rxtx_proc_t *proc,
c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP]);
@@ -37,6 +37,12 @@
#include "common/utils/LOG/log.h"
#include "PHY/sse_intrin.h"
#include "SCHED_NR_UE/defs.h"
#include "PHY/NR_REFSIG/sss_nr.h"
#include "PHY/NR_REFSIG/pss_nr.h"
#include "PHY/NR_REFSIG/ss_pbch_nr.h"
#include "PHY/MODULATION/modulation_UE.h"
#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
#include "PHY/NR_UE_ESTIMATION/nr_estimation.h"
#define K1 ((long long int) 512)
#define K2 ((long long int) (1024-K1))
@@ -184,6 +190,35 @@ uint32_t nr_ue_calculate_ssb_rsrp(const NR_DL_FRAME_PARMS *fp,
return rsrp;
}
// Send SSB RSRP measurement to MAC
static void send_ssb_rsrp_meas(PHY_VARS_NR_UE *ue,
const UE_nr_rxtx_proc_t *proc,
uint16_t Nid_cell,
int rsrp_dBm,
bool is_neighboring_cell,
int ssb_index,
float sinr_dB)
{
if (!ue->if_inst || !ue->if_inst->dl_indication)
return;
fapi_nr_l1_measurements_t l1_measurements = {
.gNB_index = proc->gNB_id,
.meas_type = NFAPI_NR_SS_MEAS,
.Nid_cell = Nid_cell,
.is_neighboring_cell = is_neighboring_cell,
.rsrp_dBm = rsrp_dBm,
.ssb_index = ssb_index,
.sinr_dB = sinr_dB,
};
nr_downlink_indication_t dl_indication = {0};
fapi_nr_rx_indication_t rx_ind = {0};
nr_fill_dl_indication(&dl_indication, NULL, &rx_ind, proc, ue, NULL);
nr_fill_rx_indication(&rx_ind, FAPI_NR_MEAS_IND, ue, NULL, NULL, 1, proc, &l1_measurements, NULL);
ue->if_inst->dl_indication(&dl_indication);
}
// This function implements:
// - SS reference signal received power (SS-RSRP) as per clause 5.1.1 of 3GPP TS 38.215 version 16.3.0 Release 16
// - no Layer 3 filtering implemented (no filterCoefficient provided from RRC)
@@ -227,23 +262,245 @@ void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue,
ue->measurements.ssb_sinr_dB[ssb_index]);
// Send SS measurements to MAC
if (!ue->if_inst || !ue->if_inst->dl_indication)
return;
send_ssb_rsrp_meas(ue,
proc,
ue->frame_parms.Nid_cell,
ue->measurements.ssb_rsrp_dBm[ssb_index],
false,
ssb_index,
ue->measurements.ssb_sinr_dB[ssb_index]);
}
fapi_nr_l1_measurements_t l1_measurements = {
.gNB_index = proc->gNB_id,
.meas_type = NFAPI_NR_SS_MEAS,
.Nid_cell = ue->frame_parms.Nid_cell,
.rsrp_dBm = ue->measurements.ssb_rsrp_dBm[ssb_index],
.sinr_dB = ue->measurements.ssb_sinr_dB[ssb_index],
.ssb_index = ssb_index,
.is_neighboring_cell = false,
static bool search_neighboring_cell(NR_DL_FRAME_PARMS *frame_parms,
fapi_nr_neighboring_cell_t *nr_neighboring_cell,
neighboring_cell_info_t *neighboring_cell_info,
c16_t **rxdata,
uint32_t rxdata_size,
uint32_t rxdataF_sz,
c16_t rxdataF[][rxdataF_sz],
c16_t pssTime[][frame_parms->ofdm_symbol_size])
{
int detected_nid_cell = -1;
int ssb_offset = 0;
int freq_offset_pss = 0;
int pss_peak = 0;
int pss_avg = 0;
int32_t sss_metric = 0;
uint8_t sss_phase = 0;
int freq_offset_sss = 0;
nr_ssb_search_params_t search_params = {
.frame_parms = frame_parms,
.rxdata = rxdata,
.rxdata_size = rxdata_size,
.ssb_start_subcarrier = frame_parms->ssb_start_subcarrier,
.target_nid_cell = -1, // Blind search
.exclude_nid_cell = frame_parms->Nid_cell, // Exclude serving cell
.apply_freq_offset = false,
.search_frame_id = 0, // Search in first frame of buffer
.fo_flag = false,
.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 = &pss_peak,
.pss_avg = &pss_avg,
};
nr_downlink_indication_t dl_indication = {0};
fapi_nr_rx_indication_t rx_ind = {0};
nr_fill_dl_indication(&dl_indication, NULL, &rx_ind, proc, ue, NULL);
nr_fill_rx_indication(&rx_ind, FAPI_NR_MEAS_IND, ue, NULL, NULL, 1, proc, &l1_measurements, NULL);
ue->if_inst->dl_indication(&dl_indication);
bool found = nr_search_ssb_common(&search_params);
if (found) {
nr_neighboring_cell->Nid_cell = detected_nid_cell;
LOG_I(NR_PHY,
"Found neighbor cell PCI=%d (sss_metric=%d, ssb_offset=%d, pss_peak=%d dB, pss_avg=%d dB)\n",
detected_nid_cell,
sss_metric,
ssb_offset,
pss_peak,
pss_avg);
// Update search window
neighboring_cell_info->pss_search_start = ssb_offset + frame_parms->nb_prefix_samples - 16;
neighboring_cell_info->pss_search_length = 32;
}
return found;
}
static bool validate_known_pci(NR_DL_FRAME_PARMS *frame_parms,
fapi_nr_neighboring_cell_t *nr_neighboring_cell,
neighboring_cell_info_t *neighboring_cell_info,
c16_t **rxdata,
uint32_t rxdataF_sz,
c16_t rxdataF[][rxdataF_sz],
c16_t pssTime[][frame_parms->ofdm_symbol_size])
{
int known_pci = nr_neighboring_cell->Nid_cell;
int pss_index = GET_NID2(known_pci);
int f_off = 0;
int pss_peak = 0;
int pss_avg = 0;
int start = neighboring_cell_info->pss_search_start;
int length = neighboring_cell_info->pss_search_length;
int peak_position = pss_search_time_nr((const c16_t **)rxdata,
frame_parms,
pssTime,
false, // no frequency offset estimation for tracking
0, // first frame
known_pci,
&pss_index,
&f_off,
&pss_peak,
&pss_avg,
start,
length);
if (peak_position < frame_parms->nb_prefix_samples) {
if (neighboring_cell_info->valid_meas)
neighboring_cell_info->consec_fail++;
LOG_D(NR_PHY,
"PSS validation failed for PCI=%d (search window: start=%d, length=%d, peak=%d dB, avg=%d dB), consec_fail=%d\n",
known_pci,
start,
length,
pss_peak,
pss_avg,
neighboring_cell_info->consec_fail);
return false;
}
int ssb_offset = peak_position - frame_parms->nb_prefix_samples;
uint8_t sss_symbol = SSS_SYMBOL_NB - PSS_SYMBOL_NB;
nr_slot_fep(NULL, frame_parms, 0, 0, rxdataF, link_type_dl, ssb_offset, (c16_t **)rxdata);
nr_slot_fep(NULL, frame_parms, 0, sss_symbol, rxdataF, link_type_dl, ssb_offset, (c16_t **)rxdata);
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(frame_parms,
pss_index,
known_pci,
0,
frame_parms->ssb_start_subcarrier,
&detected_nid_cell,
&sss_metric,
&sss_phase,
&freq_offset_sss,
rxdataF);
if (!sss_detected) {
if (neighboring_cell_info->valid_meas)
neighboring_cell_info->consec_fail++;
LOG_D(NR_PHY,
"Known PCI validation failed for PCI=%d (metric=%d), consec_fail=%d\n",
known_pci,
sss_metric,
neighboring_cell_info->consec_fail);
return false;
}
LOG_D(NR_PHY, "Known PCI validation completed for PCI=%d, metric=%d\n", known_pci, sss_metric);
neighboring_cell_info->consec_fail = 0;
neighboring_cell_info->valid_meas = true;
neighboring_cell_info->pss_search_start = peak_position - 16;
neighboring_cell_info->pss_search_length = 32;
return true;
}
void do_neighboring_cell_measurements(UE_nr_rxtx_proc_t *proc, PHY_VARS_NR_UE *ue, c16_t **rxdata, uint32_t rxdata_size)
{
NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
const uint32_t rxdataF_sz = ue->frame_parms.samples_per_slot_wCP;
// Generate PSS time-domain sequences once for all neighbor cells
__attribute__((aligned(32))) c16_t pssTime[NUMBER_PSS_SEQUENCE][frame_parms->ofdm_symbol_size];
for (int nid2_idx = 0; nid2_idx < NUMBER_PSS_SEQUENCE; nid2_idx++) {
generate_pss_nr_time(frame_parms, nid2_idx, frame_parms->ssb_start_subcarrier, pssTime[nid2_idx]);
}
__attribute__((aligned(32))) c16_t rxdataF[ue->frame_parms.nb_antennas_rx][rxdataF_sz];
for (int cell_idx = 0; cell_idx < NUMBER_OF_NEIGHBORING_CELLS_MAX; cell_idx++) {
fapi_nr_neighboring_cell_t *neighbor_cell = &ue->nrUE_config.meas_config.nr_neighboring_cell[cell_idx];
if (neighbor_cell->active == 0) {
continue;
}
memset(rxdataF, 0, sizeof(rxdataF));
neighboring_cell_info_t *neighboring_cell_info = &ue->measurements.neighboring_cell_info[cell_idx];
// performing the correlation on a frame length plus two symbols
// to take into account the possibility of PSS between the two frames
if (neighboring_cell_info->pss_search_length == 0) {
neighboring_cell_info->pss_search_length = frame_parms->samples_per_frame + (2 * frame_parms->ofdm_symbol_size);
}
bool is_blind_search = (neighbor_cell->Nid_cell == (uint16_t)-1) || (neighbor_cell->Nid_cell == frame_parms->Nid_cell);
if (neighbor_cell->Nid_cell == frame_parms->Nid_cell) {
LOG_D(NR_PHY, "Neighbor cell PCI %d matches serving cell, using blind search\n", neighbor_cell->Nid_cell);
neighboring_cell_info->pss_search_start = 0;
neighboring_cell_info->pss_search_length = frame_parms->samples_per_frame + (2 * frame_parms->ofdm_symbol_size);
}
LOG_D(NR_PHY,
"Neighbor cell measurement: Nid_cell=%u, is_blind_search=%s, active=%u\n",
neighbor_cell->Nid_cell,
is_blind_search ? "true" : "false",
neighbor_cell->active);
if (is_blind_search) {
if (!search_neighboring_cell(frame_parms,
neighbor_cell,
neighboring_cell_info,
rxdata,
rxdata_size,
rxdataF_sz,
rxdataF,
pssTime)) {
continue;
}
} else {
if (!validate_known_pci(frame_parms, neighbor_cell, neighboring_cell_info, rxdata, rxdataF_sz, rxdataF, pssTime)) {
if (neighboring_cell_info->consec_fail >= NEIGHBOR_CELL_MAX_CONSECUTIVE_FAILURES) {
LOG_D(NR_PHY, "Max consecutive failures reached for PCI=%d, resetting to full search\n", neighbor_cell->Nid_cell);
neighboring_cell_info->pss_search_start = 0;
neighboring_cell_info->pss_search_length = frame_parms->samples_per_frame + (2 * frame_parms->ofdm_symbol_size);
neighboring_cell_info->valid_meas = false;
neighboring_cell_info->consec_fail = 0;
send_ssb_rsrp_meas(ue, proc, neighbor_cell->Nid_cell, INT_MAX, true, -1, 0.0);
}
continue;
}
}
// RSRP measurements
neighboring_cell_info->ssb_rsrp = nr_ue_calculate_ssb_rsrp(frame_parms, proc, rxdataF, 0, frame_parms->ssb_start_subcarrier);
neighboring_cell_info->ssb_rsrp_dBm = 10 * log10(neighboring_cell_info->ssb_rsrp) + 30 - SQ15_SQUARED_NORM_FACTOR_DB
- ((int)ue->openair0_cfg[0].rx_gain[0] - (int)ue->openair0_cfg[0].rx_gain_offset[0])
- dB_fixed(ue->frame_parms.ofdm_symbol_size);
// Send SS measurements to MAC
send_ssb_rsrp_meas(ue, proc, neighbor_cell->Nid_cell, neighboring_cell_info->ssb_rsrp_dBm, true, -1, 0.0);
}
}
void nr_ue_meas_neighboring_cell(void *arg)
{
nr_meas_task_args_t *args = (nr_meas_task_args_t *)arg;
do_neighboring_cell_measurements(&args->proc, args->ue, args->rxdata, args->rxdata_size);
args->ue->measurements.meas_request_pending = false;
free(args);
}
// This function computes the received noise power
+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;
@@ -246,6 +246,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
+41 -31
View File
@@ -50,16 +50,18 @@
//#define DBG_PSS_NR
static time_stats_t generic_time[TIME_LAST];
static int pss_search_time_nr(const c16_t **rxdata,
const NR_DL_FRAME_PARMS *frame_parms,
const c16_t pssTime[NUMBER_PSS_SEQUENCE][frame_parms->ofdm_symbol_size],
bool fo_flag,
int is,
int target_Nid_cell,
int *nid2,
int *f_off,
int *pssPeak,
int *pssAvg);
int pss_search_time_nr(const c16_t **rxdata,
const NR_DL_FRAME_PARMS *frame_parms,
const c16_t pssTime[NUMBER_PSS_SEQUENCE][frame_parms->ofdm_symbol_size],
bool fo_flag,
int is,
int target_Nid_cell,
int *nid2,
int *f_off,
int *pssPeak,
int *pssAvg,
int search_start,
int search_length);
static int16_t primary_synchro_nr2[NUMBER_PSS_SEQUENCE][LENGTH_PSS_NR] = {0};
static c16_t primary_synchro[NUMBER_PSS_SEQUENCE][LENGTH_PSS_NR] = {0};
@@ -407,7 +409,7 @@ int pss_synchro_nr(const c16_t **rxdata,
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PSS_SEARCH_TIME_NR, VCD_FUNCTION_IN);
int synchro_position =
pss_search_time_nr(rxdata, frame_parms, pssTime, fo_flag, is, target_Nid_cell, nid2, f_off, pssPeak, pssAvg);
pss_search_time_nr(rxdata, frame_parms, pssTime, fo_flag, is, target_Nid_cell, nid2, f_off, pssPeak, pssAvg, -1, -1);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PSS_SEARCH_TIME_NR, VCD_FUNCTION_OUT);
@@ -496,30 +498,38 @@ int pss_synchro_nr(const c16_t **rxdata,
*
*********************************************************************/
static int pss_search_time_nr(const c16_t **rxdata,
const NR_DL_FRAME_PARMS *frame_parms,
const c16_t pssTime[NUMBER_PSS_SEQUENCE][frame_parms->ofdm_symbol_size],
bool fo_flag,
int is,
int target_Nid_cell,
int *nid2,
int *f_off,
int *pssPeak,
int *pssAvg)
int pss_search_time_nr(const c16_t **rxdata,
const NR_DL_FRAME_PARMS *frame_parms,
const c16_t pssTime[NUMBER_PSS_SEQUENCE][frame_parms->ofdm_symbol_size],
bool fo_flag,
int is,
int target_Nid_cell,
int *nid2,
int *f_off,
int *pssPeak,
int *pssAvg,
int search_start,
int search_length)
{
unsigned int n, ar, peak_position, pss_source;
int64_t peak_value;
int64_t avg[NUMBER_PSS_SEQUENCE] = {0};
double ffo_est = 0;
// performing the correlation on a frame length plus one symbol for the first of the two frame
// to take into account the possibility of PSS in between the two frames
unsigned int length;
if (is==0)
length = frame_parms->samples_per_frame + (2*frame_parms->ofdm_symbol_size);
else
length = frame_parms->samples_per_frame;
// Determine search window
unsigned int start, length;
if (search_start >= 0 && search_length > 0) {
// Use provided search window for known neighboring cells
start = search_start;
length = search_length;
} else {
// For initial cell search
start = 0;
if (is == 0)
length = frame_parms->samples_per_frame + (2 * frame_parms->ofdm_symbol_size);
else
length = frame_parms->samples_per_frame;
}
AssertFatal(length>0,"illegal length %d\n",length);
peak_value = 0;
@@ -547,7 +557,7 @@ static int pss_search_time_nr(const c16_t **rxdata,
}
for (int pss_index = pss_index_start; pss_index < pss_index_end; pss_index++) {
for (n = 0; n < length; n += 4) { //
for (n = start; n < start + length; n += 4) { //
int64_t pss_corr_ue=0;
/* calculate dot product of primary_synchro_time_nr and rxdata[ar][n]
@@ -627,7 +637,7 @@ static int pss_search_time_nr(const c16_t **rxdata,
dB_fixed64(avg[pss_source]),
ffo_est);
if (peak_value < 5*avg[pss_source])
if ((search_start < 0 || search_length == 0) && peak_value < 5 * avg[pss_source])
return(-1);
+38 -1
View File
@@ -125,6 +125,17 @@ typedef enum {
#define debug_msg if (((mac_xface->frame%100) == 0) || (mac_xface->frame < 50)) msg
#define NEIGHBOR_CELL_MAX_CONSECUTIVE_FAILURES 10
typedef struct {
int pss_search_start;
int pss_search_length;
uint32_t ssb_rsrp;
int ssb_rsrp_dBm;
int consec_fail; // Counter for consecutive detection failures
bool valid_meas;
} neighboring_cell_info_t;
typedef struct {
uint8_t decoded_output[3]; // PBCH paylod not larger than 3B
uint8_t xtra_byte;
@@ -214,7 +225,9 @@ typedef struct {
unsigned char nb_antennas_rx;
/// DLSCH error counter
// short dlsch_errors;
/// Info about neighboring cells to perform the measurements
neighboring_cell_info_t neighboring_cell_info[NUMBER_OF_NEIGHBORING_CELLS_MAX];
bool meas_request_pending;
} PHY_NR_MEASUREMENTS;
typedef struct {
@@ -573,6 +586,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;
@@ -973,6 +973,32 @@ static void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue,
}
static bool check_meas_to_perform(PHY_VARS_NR_UE *ue, int nr_slot_rx)
{
if (nr_slot_rx != 0) {
return false;
}
if (ue->measurements.meas_request_pending == true) {
return false;
}
// Check if any intra-frequency neighbor cells are configured
uint32_t serving_ssb_freq = get_ssb_arfcn(&ue->frame_parms);
bool has_intra_freq_neighbors = false;
for (int cell_idx = 0; cell_idx < NUMBER_OF_NEIGHBORING_CELLS_MAX; cell_idx++) {
fapi_nr_neighboring_cell_t *nr_neighboring_cell = &ue->nrUE_config.meas_config.nr_neighboring_cell[cell_idx];
if (nr_neighboring_cell->active == 1) {
if (nr_neighboring_cell->ssb_freq == 0 || nr_neighboring_cell->ssb_freq == serving_ssb_freq) {
has_intra_freq_neighbors = true;
}
}
}
return has_intra_freq_neighbors;
}
static bool is_ssb_index_transmitted(const PHY_VARS_NR_UE *ue, const int index)
{
if (ue->received_config_request) {
@@ -1188,6 +1214,28 @@ int pbch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_da
} // for i
} // for rsc_id
} // for gNB_id
PHY_NR_MEASUREMENTS *measurements = &ue->measurements;
if (check_meas_to_perform(ue, nr_slot_rx)) {
measurements->meas_request_pending = true;
// Copy rxdata
uint32_t rxdata_size = (2 * (fp->samples_per_frame) + fp->ofdm_symbol_size);
size_t total_size = sizeof(nr_meas_task_args_t) + fp->nb_antennas_rx * rxdata_size * sizeof(c16_t);
nr_meas_task_args_t *args = malloc(total_size);
args->proc = *proc;
args->ue = ue;
args->rxdata_size = rxdata_size;
for (int i = 0; i < fp->nb_antennas_rx; i++) {
args->rxdata[i] = &args->rxdata_ant[i * rxdata_size];
memcpy(args->rxdata[i], ue->common_vars.rxdata[i], rxdata_size * sizeof(c16_t));
}
task_t t = {.func = nr_ue_meas_neighboring_cell, .args = args};
pushTpool(&get_nrUE_params()->Tpool, t);
}
TracyCZoneEnd(ctx);
return sampleShift;
}