From 3fdbe01157bab1c9a405e18377b2cc15d2fb32c0 Mon Sep 17 00:00:00 2001 From: alexjiao Date: Wed, 5 Nov 2025 22:46:55 +0800 Subject: [PATCH] Add neighboring cell measurements Implement intra-frequency neighboring cell measurements including: - Measurement configuration structures - Blind search and known PCI validation - RSRP measurements for neighbor cells Co-authored-by: Roberto Louro Magueta --- .../nfapi/public_inc/fapi_nr_ue_interface.h | 17 +- openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h | 15 +- .../PHY/NR_UE_ESTIMATION/nr_ue_measurements.c | 271 ++++++++++++++++++ openair1/PHY/defs_nr_UE.h | 15 +- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 48 ++++ openair2/LAYER2/NR_MAC_UE/config_ue.c | 14 + openair2/LAYER2/NR_MAC_UE/mac_proto.h | 8 + openair2/RRC/NR_UE/rrc_UE.c | 159 ++++++++-- openair2/RRC/NR_UE/rrc_defs.h | 2 + 9 files changed, 523 insertions(+), 26 deletions(-) diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h index dd6b8135ab..4c99da4e35 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h @@ -29,6 +29,11 @@ #define NFAPI_UE_MAX_NUM_CB 8 #define NFAPI_MAX_NUM_UL_PDU 255 #define NFAPI_MAX_NUM_CSI_RATEMATCH 4 +// Maximum number of neighboring cells that can be tracked simultaneously +// Set to 1 due to PSS search limitation: pss_search_time_nr() returns only +// the single strongest PSS correlation peak, making it impossible to reliably +// detect multiple neighbor cells in the same measurement cycle +#define NUMBER_OF_NEIGHBORING_CELLS_MAX 1 /* typedef unsigned int uint32_t; @@ -718,6 +723,16 @@ typedef struct } fapi_nr_prach_config_t; +typedef struct { + uint16_t Nid_cell; + uint8_t active; + uint32_t ssb_freq; +} fapi_nr_neighboring_cell_t; + +typedef struct { + fapi_nr_neighboring_cell_t nr_neighboring_cell[NUMBER_OF_NEIGHBORING_CELLS_MAX]; +} fapi_nr_meas_config_t; + typedef struct { int16_t target_Nid_cell; bool ssb_bw_scan; @@ -733,7 +748,7 @@ typedef struct { fapi_nr_tdd_table_t tdd_table; fapi_nr_prach_config_t prach_config; fapi_nr_ntn_config_t ntn_config; - + fapi_nr_meas_config_t meas_config; } fapi_nr_config_request_t; #endif diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h index 7a47c67c52..4804f41b21 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h @@ -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]); diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c b/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c index b04f595481..a22a5c7c97 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c @@ -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)) @@ -246,6 +252,271 @@ void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue, ue->if_inst->dl_indication(&dl_indication); } +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, + }; + + 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 invalid RSRP to MAC to trigger reset + if (ue->if_inst && ue->if_inst->dl_indication) { + fapi_nr_l1_measurements_t l1_measurements = { + .gNB_index = proc->gNB_id, + .meas_type = NFAPI_NR_SS_MEAS, + .Nid_cell = neighbor_cell->Nid_cell, + .is_neighboring_cell = true, + .rsrp_dBm = INT_MAX, + .ssb_index = -1, + .sinr_dB = 0.0, + }; + + 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); + } + } + 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 + if (!ue->if_inst || !ue->if_inst->dl_indication) + continue; + + fapi_nr_l1_measurements_t l1_measurements = { + .gNB_index = proc->gNB_id, + .meas_type = NFAPI_NR_SS_MEAS, + .Nid_cell = nr_neighboring_cell->Nid_cell, + .is_neighboring_cell = true, + .rsrp_dBm = neighboring_cell_info->ssb_rsrp_dBm, + }; + + 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); + } +} + +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 // Measurement units: // - psd_awgn (AWGN power spectral density): dBm/Hz diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index 1cc3225d1a..e96927fe21 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -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 { diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 68c0541e3a..d09cc526b0 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -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; } diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 4077e793ab..da699656fa 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -2858,3 +2858,17 @@ void nr_rrc_mac_config_req_cg(module_id_t module_id, ret = pthread_mutex_unlock(&mac->if_mutex); AssertFatal(!ret, "mutex failed %d\n", ret); } + +void nr_rrc_mac_config_req_meas(module_id_t module_id, const nr_neighbor_cell_info_t *neighbor_cells, int num_neighbors) +{ + NR_UE_MAC_INST_t *mac = get_mac_inst(module_id); + + for (int i = 0; i < num_neighbors && i < NUMBER_OF_NEIGHBORING_CELLS_MAX; i++) { + fapi_nr_neighboring_cell_t *phy_cell = &mac->phy_config.config_req.meas_config.nr_neighboring_cell[i]; + phy_cell->Nid_cell = neighbor_cells[i].Nid_cell; + phy_cell->ssb_freq = neighbor_cells[i].ssb_freq; + phy_cell->active = neighbor_cells[i].active; + } + + mac->if_module->phy_config_request(&mac->phy_config); +} diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index 5ee8a62625..69ba897a21 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -97,6 +97,14 @@ void nr_rrc_mac_config_other_sib(module_id_t module_id, NR_SIB19_r17_t *sib19_r1 void nr_rrc_mac_resume_rb(module_id_t module_id, bool is_srb, int rb_id); void nr_rrc_mac_config_req_reset(module_id_t module_id, NR_UE_MAC_reset_cause_t cause); +typedef struct nr_neighbor_cell_info { + int16_t Nid_cell; // Physical Cell ID + uint32_t ssb_freq; // SSB frequency (ARFCN) + uint8_t active; // 1 = active, 0 = inactive +} nr_neighbor_cell_info_t; + +void nr_rrc_mac_config_req_meas(module_id_t module_id, const nr_neighbor_cell_info_t *neighbor_cells, int num_neighbors); + /**\brief initialization NR UE MAC instance(s)*/ NR_UE_MAC_INST_t * nr_l2_init_ue(int nb_inst); diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 424118fa4c..dbd6fb2818 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -921,6 +921,14 @@ static void nr_rrc_manage_rlc_bearers(NR_UE_RRC_INST_t *rrc, const NR_CellGroupC } } +static void nr_ue_meas_reset(meas_t *meas_cell, bool csi_meas) +{ + if (csi_meas) + meas_cell->csi_rsrp_dBm.init = false; + else + meas_cell->ss_rsrp_dBm.init = false; +} + static void nr_rrc_process_reconfigurationWithSync(NR_UE_RRC_INST_t *rrc, NR_ReconfigurationWithSync_t *reconfigurationWithSync, int gNB_index) @@ -934,6 +942,24 @@ static void nr_rrc_process_reconfigurationWithSync(NR_UE_RRC_INST_t *rrc, return; } + // Clear neighbor cell lists from measurement objects during handover + rrcPerNB_t *rrcNB = &rrc->perNB[gNB_index]; + for (int i = 0; i < MAX_MEAS_OBJ; i++) { + if (rrcNB->MeasObj[i] && rrcNB->MeasObj[i]->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) { + NR_MeasObjectNR_t *measObjNR = rrcNB->MeasObj[i]->measObject.choice.measObjectNR; + if (measObjNR->cellsToAddModList) { + ASN_STRUCT_FREE(asn_DEF_NR_CellsToAddModList, measObjNR->cellsToAddModList); + measObjNR->cellsToAddModList = NULL; + } + } + } + + l3_measurements_t *l3m = &rrcNB->l3_measurements; + for (int i = 0; i < NUMBER_OF_NEIGHBORING_CELLS_MAX; i++) { + nr_ue_meas_reset(&l3m->neighboring_cell[i], false); + nr_ue_meas_reset(&l3m->neighboring_cell[i], true); + } + if (reconfigurationWithSync->spCellConfigCommon) { /* if the frequencyInfoDL is included, consider the target SpCell to be one on the SSB frequency indicated by the frequencyInfoDL */ @@ -1353,7 +1379,11 @@ static void handle_measid_remove(rrcPerNB_t *rrc, struct NR_MeasIdToRemoveList * } } -static void handle_measid_addmod(rrcPerNB_t *rrc, struct NR_MeasIdToAddModList *addmod_list, NR_UE_Timers_Constants_t *timers) +static void handle_measid_addmod(rrcPerNB_t *rrc, + struct NR_MeasIdToAddModList *addmod_list, + NR_UE_Timers_Constants_t *timers, + nr_neighbor_cell_info_t *neighbor_cells, + int *num_neighbors) { for (int i = 0; i < addmod_list->list.count; i++) { NR_MeasId_t id = addmod_list->list.array[i]->measId; @@ -1387,11 +1417,56 @@ static void handle_measid_addmod(rrcPerNB_t *rrc, struct NR_MeasIdToAddModList * nr_timer_start(&timers->T321); } } + // Check for A3 event and extract neighbor cell info for MAC/PHY + else if (reportNR->reportType.present == NR_ReportConfigNR__reportType_PR_eventTriggered) { + NR_EventTriggerConfig_t *eventTriggerConfig = reportNR->reportType.choice.eventTriggered; + if (eventTriggerConfig->eventId.present == NR_EventTriggerConfig__eventId_PR_eventA3) { + if (rrc->MeasObj[measObjectId] + && rrc->MeasObj[measObjectId]->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) { + NR_MeasObjectNR_t *obj_nr = rrc->MeasObj[measObjectId]->measObject.choice.measObjectNR; + + uint32_t ssb_freq = 0; + if (obj_nr->ssbFrequency) { + ssb_freq = *obj_nr->ssbFrequency; + } + + if (obj_nr->cellsToAddModList) { + NR_CellsToAddModList_t *cellsToAddModList = obj_nr->cellsToAddModList; + for (int j = 0; j < cellsToAddModList->list.count; j++) { + if (*num_neighbors < NUMBER_OF_NEIGHBORING_CELLS_MAX) { + NR_CellsToAddMod_t *cell = cellsToAddModList->list.array[j]; + neighbor_cells[*num_neighbors].Nid_cell = cell->physCellId; + neighbor_cells[*num_neighbors].ssb_freq = ssb_freq; + neighbor_cells[*num_neighbors].active = 1; + (*num_neighbors)++; + } else { + LOG_W(NR_RRC, + "More than %d neighboring cells configured, ignoring excess cells!\n", + NUMBER_OF_NEIGHBORING_CELLS_MAX); + break; + } + } + } else { + // We do not know PCI of neighboring cells, so we do blind search + if (*num_neighbors < NUMBER_OF_NEIGHBORING_CELLS_MAX) { + neighbor_cells[*num_neighbors].Nid_cell = -1; + neighbor_cells[*num_neighbors].ssb_freq = ssb_freq; + neighbor_cells[*num_neighbors].active = 1; + (*num_neighbors)++; + } + } + } + } + } } } } -static void nr_rrc_ue_process_measConfig(rrcPerNB_t *rrc, NR_MeasConfig_t *const measConfig, NR_UE_Timers_Constants_t *timers) +static void nr_rrc_ue_process_measConfig(rrcPerNB_t *rrc, + NR_MeasConfig_t *const measConfig, + NR_UE_Timers_Constants_t *timers, + nr_neighbor_cell_info_t *neighbor_cells, + int *num_neighbors) { if (measConfig->measObjectToRemoveList) handle_measobj_remove(rrc, measConfig->measObjectToRemoveList, timers); @@ -1412,7 +1487,7 @@ static void nr_rrc_ue_process_measConfig(rrcPerNB_t *rrc, NR_MeasConfig_t *const handle_measid_remove(rrc, measConfig->measIdToRemoveList, timers); if (measConfig->measIdToAddModList) - handle_measid_addmod(rrc, measConfig->measIdToAddModList, timers); + handle_measid_addmod(rrc, measConfig->measIdToAddModList, timers, neighbor_cells, num_neighbors); LOG_W(NR_RRC, "Measurement gaps not yet supported!\n"); @@ -1485,7 +1560,12 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_ } if (ie->measConfig) { LOG_I(NR_RRC, "RRCReconfiguration includes Measurement Configuration\n"); - nr_rrc_ue_process_measConfig(rrcNB, ie->measConfig, &rrc->timers_and_constants); + nr_neighbor_cell_info_t neighbor_cells[NUMBER_OF_NEIGHBORING_CELLS_MAX]; + int num_neighbors = 0; + nr_rrc_ue_process_measConfig(rrcNB, ie->measConfig, &rrc->timers_and_constants, neighbor_cells, &num_neighbors); + if (num_neighbors > 0) { + nr_rrc_mac_config_req_meas(rrc->ue_id, neighbor_cells, num_neighbors); + } } if (ie->lateNonCriticalExtension) { LOG_E(NR_RRC, "RRCReconfiguration includes lateNonCriticalExtension. Not handled.\n"); @@ -2613,13 +2693,10 @@ static void apply_ema(val_init_t *vi_rsrp_dBm, float filter_coeff_rsrp, int rsrp *meas_init = true; } -void nr_ue_meas_filtering(rrcPerNB_t *rrc, bool is_neighboring_cell, uint16_t Nid_cell, bool csi_meas, int rsrp_dBm) +void nr_ue_meas_filtering(rrcPerNB_t *rrc, meas_t *meas_cell, uint16_t Nid_cell, bool csi_meas, int rsrp_dBm) { - if (is_neighboring_cell) - return; - l3_measurements_t *l3_measurements = &rrc->l3_measurements; - meas_t *meas_cell = &l3_measurements->serving_cell; + if (meas_cell->Nid_cell != Nid_cell) { meas_cell->ss_rsrp_dBm.init = false; meas_cell->csi_rsrp_dBm.init = false; @@ -2788,6 +2865,54 @@ static void nr_rrc_handle_ra_indication(NR_UE_RRC_INST_t *rrc, bool ra_succeeded } } +static void nr_rrc_handle_meas_indication(NR_UE_RRC_INST_t *rrc, NRRrcMacMeasDataInd *meas_ind) +{ + rrcPerNB_t *rrcNB = rrc->perNB + meas_ind->gnb_index; + l3_measurements_t *l3_measurements = &rrcNB->l3_measurements; + meas_t *meas_cell = NULL; + + if (meas_ind->is_neighboring_cell) { + uint16_t target_nid_cell = meas_ind->Nid_cell; + for (int i = 0; i < NUMBER_OF_NEIGHBORING_CELLS_MAX; i++) { + if (l3_measurements->neighboring_cell[i].Nid_cell == target_nid_cell) { + meas_cell = &l3_measurements->neighboring_cell[i]; + break; + } + } + if (!meas_cell) { + for (int i = 0; i < NUMBER_OF_NEIGHBORING_CELLS_MAX; i++) { + if (!l3_measurements->neighboring_cell[i].ss_rsrp_dBm.init && !l3_measurements->neighboring_cell[i].csi_rsrp_dBm.init) { + meas_cell = &l3_measurements->neighboring_cell[i]; + break; + } + } + } + } else { + meas_cell = &l3_measurements->serving_cell; + } + + if (!meas_cell) { + LOG_E(NR_RRC, "meas_cell not found!\n"); + return; + } + + if (meas_ind->is_neighboring_cell && meas_ind->rsrp_dBm == INT_MAX) { + LOG_W(NR_RRC, "[Nid_cell %i] Neighboring cell not detected. L3 measurements will be reset.\n", meas_ind->Nid_cell); + meas_cell->Nid_cell = meas_ind->Nid_cell; + nr_ue_meas_reset(meas_cell, meas_ind->is_csi_meas); + } else { + LOG_D(NR_RRC, + "[%s][Nid_cell %i] Received %s measurements: RSRP = %i (dBm)\n", + meas_ind->is_neighboring_cell ? "Neighboring cell" : "Active cell", + meas_ind->Nid_cell, + meas_ind->is_csi_meas ? "CSI meas" : "SSB meas", + meas_ind->rsrp_dBm); + + nr_ue_meas_filtering(rrcNB, meas_cell, meas_ind->Nid_cell, meas_ind->is_csi_meas, meas_ind->rsrp_dBm); + nr_ue_check_meas_report(rrc, meas_ind->gnb_index); + } +} + void *rrc_nrue_task(void *args_p) { itti_mark_task_ready(TASK_RRC_NRUE); @@ -2892,21 +3017,9 @@ void *rrc_nrue(void *notUsed) nr_rrc_ue_decode_NR_SBCCH_SL_BCH_Message(rrc, sbcch->gnb_index,sbcch->frame, sbcch->slot, sbcch->sdu, sbcch->sdu_size, sbcch->rx_slss_id); + break; case NR_RRC_MAC_MEAS_DATA_IND: - - LOG_D(NR_RRC, "[%s][Nid_cell %i] Received %s measurements: RSRP = %i (dBm)\n", - NR_RRC_MAC_MEAS_DATA_IND(msg_p).is_neighboring_cell? "Neighboring cell" : "Active cell", - NR_RRC_MAC_MEAS_DATA_IND(msg_p).Nid_cell, - NR_RRC_MAC_MEAS_DATA_IND(msg_p).is_csi_meas ? "CSI meas" : "SSB meas", - NR_RRC_MAC_MEAS_DATA_IND(msg_p).rsrp_dBm); - - rrcPerNB_t *rrcNB = rrc->perNB + NR_RRC_MAC_MEAS_DATA_IND(msg_p).gnb_index; - nr_ue_meas_filtering(rrcNB, - NR_RRC_MAC_MEAS_DATA_IND(msg_p).is_neighboring_cell, - NR_RRC_MAC_MEAS_DATA_IND(msg_p).Nid_cell, - NR_RRC_MAC_MEAS_DATA_IND(msg_p).is_csi_meas, - NR_RRC_MAC_MEAS_DATA_IND(msg_p).rsrp_dBm); - nr_ue_check_meas_report(rrc, NR_RRC_MAC_MEAS_DATA_IND(msg_p).gnb_index); + nr_rrc_handle_meas_indication(rrc, &NR_RRC_MAC_MEAS_DATA_IND(msg_p)); break; case NR_RRC_MAC_CCCH_DATA_IND: { diff --git a/openair2/RRC/NR_UE/rrc_defs.h b/openair2/RRC/NR_UE/rrc_defs.h index 336afcc63a..a8ce126068 100644 --- a/openair2/RRC/NR_UE/rrc_defs.h +++ b/openair2/RRC/NR_UE/rrc_defs.h @@ -70,6 +70,7 @@ #define MAX_MEAS_CONFIG 64 #define MAX_MEAS_ID 64 #define MAX_QUANTITY_CONFIG 2 +#define NUMBER_OF_NEIGHBORING_CELLS_MAX 1 typedef enum { nr_SecondaryCellGroupConfig_r15=0, @@ -196,6 +197,7 @@ typedef struct l3_measurements_s { float ssb_filter_coeff_rsrp; float csi_RS_filter_coeff_rsrp; meas_t serving_cell; + meas_t neighboring_cell[NUMBER_OF_NEIGHBORING_CELLS_MAX]; long trigger_to_measid; long trigger_quantity; long rs_type;