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:
@@ -308,6 +308,7 @@ These modes of operation are supported:
|
||||
* Reception of NR-PSS/NR-SSS
|
||||
* NR-PBCH supports multiple SSBs and flexible periodicity
|
||||
- RSRP measurement for the strongest SSB
|
||||
- RSRP measurement for neighboring cell at same SSB frequency with serving cell
|
||||
* Reception of NR-PDCCH (including reception of DCI, polar decoding, de-scrambling, de-modulation, RB de-mapping, etc)
|
||||
- common search space configured by MIB
|
||||
- user-specific search space configured by RRC
|
||||
@@ -456,6 +457,7 @@ These modes of operation are supported:
|
||||
- RRCReestablishmentRequest/RRC Reestablishment/Reestablishment complete
|
||||
- Support for master cell group configuration
|
||||
- Reception of UECapabilityEnquiry, encoding and transmission of UECapability
|
||||
- Support for measurement report of Event A2/A3
|
||||
* NTN according to 38.331 Rel.17
|
||||
- Reception of ntn-Config-r17 from SIB19 or reconfigurationWithSync
|
||||
- Handling of ntn-UlSyncValidityDuration-r17 in SIB19
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -2888,3 +2888,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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -855,12 +855,15 @@ int do_RRCSetupComplete(uint8_t *buffer,
|
||||
return((enc_rval.encoded+7)/8);
|
||||
}
|
||||
|
||||
// TODO: This function is only implemented for event A2
|
||||
// TODO: This function is only implemented for event A2/A3
|
||||
int do_nrMeasurementReport_SA(long trigger_to_measid,
|
||||
long trigger_quantity,
|
||||
long rs_type,
|
||||
uint16_t Nid_cell,
|
||||
int rsrp_index,
|
||||
bool neighbor_cell_valid,
|
||||
uint16_t neighbor_Nid_cell,
|
||||
int neighbor_rsrp_index,
|
||||
uint8_t *buffer,
|
||||
size_t buffer_size)
|
||||
{
|
||||
@@ -877,6 +880,7 @@ int do_nrMeasurementReport_SA(long trigger_to_measid,
|
||||
asn1cCalloc(measurementReport->criticalExtensions.choice.measurementReport, mrIE);
|
||||
mrIE->measResults.measId = trigger_to_measid;
|
||||
|
||||
// Serving cell
|
||||
NR_MeasResultServMO_t *measResultServMo = calloc_or_fail(1, sizeof(*measResultServMo));
|
||||
|
||||
NR_MeasResultNR_t *measResultServingCell = &measResultServMo->measResultServingCell;
|
||||
@@ -895,6 +899,30 @@ int do_nrMeasurementReport_SA(long trigger_to_measid,
|
||||
}
|
||||
|
||||
ASN_SEQUENCE_ADD(&mrIE->measResults.measResultServingMOList.list, measResultServMo);
|
||||
|
||||
// Neighbor cell
|
||||
if (neighbor_cell_valid) {
|
||||
struct NR_MeasResults__measResultNeighCells *measResultNeighCells = calloc_or_fail(1, sizeof(*measResultNeighCells));
|
||||
mrIE->measResults.measResultNeighCells = measResultNeighCells;
|
||||
measResultNeighCells->present = NR_MeasResults__measResultNeighCells_PR_measResultListNR;
|
||||
NR_MeasResultListNR_t *measResultListNR = calloc_or_fail(1, sizeof(*measResultListNR));
|
||||
measResultNeighCells->choice.measResultListNR = measResultListNR;
|
||||
struct NR_MeasResultNR *meas_result_neigh_cell = calloc_or_fail(1, sizeof(*meas_result_neigh_cell));
|
||||
asn1cCalloc(meas_result_neigh_cell->physCellId, neighbor_pci);
|
||||
*neighbor_pci = neighbor_Nid_cell;
|
||||
struct NR_MeasResultNR__measResult__cellResults *cellResults = &meas_result_neigh_cell->measResult.cellResults;
|
||||
struct NR_MeasQuantityResults *neigh_mq_res = calloc_or_fail(1, sizeof(*neigh_mq_res));
|
||||
if (trigger_quantity == NR_MeasTriggerQuantityOffset_PR_rsrp) {
|
||||
asn1cCalloc(neigh_mq_res->rsrp, rsrp);
|
||||
*rsrp = neighbor_rsrp_index;
|
||||
if (rs_type == NR_NR_RS_Type_ssb)
|
||||
cellResults->resultsSSB_Cell = neigh_mq_res;
|
||||
else
|
||||
cellResults->resultsCSI_RS_Cell = neigh_mq_res;
|
||||
}
|
||||
ASN_SEQUENCE_ADD(&measResultListNR->list, meas_result_neigh_cell);
|
||||
}
|
||||
|
||||
enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, NULL, (void *)&ul_dcch_msg, buffer, buffer_size);
|
||||
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
|
||||
|
||||
|
||||
@@ -131,6 +131,9 @@ int do_nrMeasurementReport_SA(long trigger_to_measid,
|
||||
long rs_type,
|
||||
uint16_t Nid_cell,
|
||||
int rsrp_index,
|
||||
bool neighbor_cell_valid,
|
||||
uint16_t neighbor_Nid_cell,
|
||||
int neighbor_rsrp_index,
|
||||
uint8_t *buffer,
|
||||
size_t buffer_size);
|
||||
|
||||
|
||||
+332
-80
@@ -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 */
|
||||
@@ -1355,7 +1381,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;
|
||||
@@ -1389,11 +1419,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);
|
||||
@@ -1414,7 +1489,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");
|
||||
|
||||
@@ -1487,7 +1562,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");
|
||||
@@ -2625,13 +2705,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;
|
||||
@@ -2676,6 +2753,179 @@ static long get_measurement_report_interval_ms(NR_ReportInterval_t interval)
|
||||
}
|
||||
}
|
||||
|
||||
static int get_rsrp_value(const meas_t *cell)
|
||||
{
|
||||
if (cell->ss_rsrp_dBm.init)
|
||||
return cell->ss_rsrp_dBm.val;
|
||||
if (cell->csi_rsrp_dBm.init)
|
||||
return cell->csi_rsrp_dBm.val;
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
static int get_meas_id(rrcPerNB_t *rrcNB, int report_config_id)
|
||||
{
|
||||
for (int j = 0; j < MAX_MEAS_ID; j++) {
|
||||
NR_MeasIdToAddMod_t *meas_id_toAddMod = rrcNB->MeasId[j];
|
||||
if (meas_id_toAddMod && meas_id_toAddMod->reportConfigId == report_config_id)
|
||||
return meas_id_toAddMod->measId;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void setup_meas_trigger(l3_measurements_t *l3_measurements,
|
||||
NR_EventTriggerConfig_t *event_config,
|
||||
int meas_id,
|
||||
long trigger_quantity,
|
||||
bool neighbor_cell_valid)
|
||||
{
|
||||
l3_measurements->trigger_to_measid = meas_id;
|
||||
l3_measurements->trigger_quantity = trigger_quantity;
|
||||
l3_measurements->rs_type = event_config->rsType;
|
||||
l3_measurements->reports_sent = 0;
|
||||
l3_measurements->max_reports =
|
||||
(event_config->reportAmount == NR_EventTriggerConfig__reportAmount_infinity) ? INT_MAX : (1 << event_config->reportAmount);
|
||||
l3_measurements->report_interval_ms = get_measurement_report_interval_ms(event_config->reportInterval);
|
||||
l3_measurements->neighbor_cell_valid = neighbor_cell_valid;
|
||||
}
|
||||
|
||||
static void start_meas_event(l3_measurements_t *l3_measurements,
|
||||
rrcPerNB_t *rrcNB,
|
||||
NR_timer_t *event_timer,
|
||||
NR_EventTriggerConfig_t *event_config,
|
||||
int report_config_id,
|
||||
long trigger_quantity,
|
||||
bool neighbor_cell_valid,
|
||||
long time_to_trigger)
|
||||
{
|
||||
nr_timer_setup(event_timer, get_event_time_to_trigger(time_to_trigger), 10);
|
||||
nr_timer_start(event_timer);
|
||||
|
||||
int meas_id = get_meas_id(rrcNB, report_config_id);
|
||||
AssertFatal(meas_id > 0, "meas_id did not found for report_config_id %i\n", report_config_id);
|
||||
|
||||
setup_meas_trigger(l3_measurements, event_config, meas_id, trigger_quantity, neighbor_cell_valid);
|
||||
}
|
||||
|
||||
static void stop_meas_event(l3_measurements_t *l3_measurements, NR_timer_t *event_timer)
|
||||
{
|
||||
nr_timer_stop(event_timer);
|
||||
nr_timer_stop(&l3_measurements->periodic_report_timer);
|
||||
l3_measurements->reports_sent = 0;
|
||||
}
|
||||
|
||||
// TS 38.331 - 5.5.4.3 Event A2 (Serving becomes worse than threshold)
|
||||
static void handle_event_a2(l3_measurements_t *l3_measurements,
|
||||
rrcPerNB_t *rrcNB,
|
||||
struct NR_EventTriggerConfig__eventId__eventA2 *event_A2,
|
||||
NR_EventTriggerConfig_t *event_trigger_config,
|
||||
long report_config_id)
|
||||
{
|
||||
if (event_A2->a2_Threshold.present != NR_MeasTriggerQuantity_PR_rsrp)
|
||||
return;
|
||||
|
||||
meas_t *serving_cell = &l3_measurements->serving_cell;
|
||||
int serving_cell_rsrp = get_rsrp_value(serving_cell);
|
||||
if (serving_cell_rsrp == INT_MAX) {
|
||||
LOG_E(NR_RRC, "There are no RSRP measurements taken for the active cell\n");
|
||||
}
|
||||
|
||||
// TS 38.133 - Table 10.1.6.1-1: SS-RSRP and CSI-RSRP measurement report mapping
|
||||
int rsrp_threshold = event_A2->a2_Threshold.choice.rsrp - 157;
|
||||
int rsrp_hysteresis = event_A2->hysteresis >> 1;
|
||||
|
||||
if (serving_cell_rsrp + rsrp_hysteresis < rsrp_threshold) {
|
||||
if (!nr_timer_is_active(&l3_measurements->TA2) && (l3_measurements->reports_sent == 0)) {
|
||||
start_meas_event(l3_measurements,
|
||||
rrcNB,
|
||||
&l3_measurements->TA2,
|
||||
event_trigger_config,
|
||||
report_config_id,
|
||||
event_A2->a2_Threshold.present,
|
||||
false,
|
||||
event_A2->timeToTrigger);
|
||||
|
||||
LOG_W(NR_RRC,
|
||||
"(active_cell_rsrp) %i + (rsrp_hysteresis) %i < (rsrp_threshold) %i\n",
|
||||
serving_cell_rsrp,
|
||||
rsrp_hysteresis,
|
||||
rsrp_threshold);
|
||||
}
|
||||
} else if (nr_timer_is_active(&l3_measurements->TA2) && (serving_cell_rsrp - rsrp_hysteresis > rsrp_threshold)) {
|
||||
stop_meas_event(l3_measurements, &l3_measurements->TA2);
|
||||
}
|
||||
}
|
||||
|
||||
// TS 38.331 - 5.5.4.4 Event A3 (Neighbour becomes offset better than SpCell)
|
||||
static void handle_event_a3(l3_measurements_t *l3_measurements,
|
||||
rrcPerNB_t *rrcNB,
|
||||
struct NR_EventTriggerConfig__eventId__eventA3 *event_A3,
|
||||
NR_EventTriggerConfig_t *event_trigger_config,
|
||||
long report_config_id)
|
||||
{
|
||||
if (event_A3->a3_Offset.present != NR_MeasTriggerQuantityOffset_PR_rsrp)
|
||||
return;
|
||||
|
||||
meas_t *serving_cell = &l3_measurements->serving_cell;
|
||||
|
||||
int serving_cell_rsrp = get_rsrp_value(serving_cell);
|
||||
if (serving_cell_rsrp == INT_MAX) {
|
||||
LOG_D(NR_RRC, "There are no RSRP measurements taken for the serving cell\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int rsrp_offset = event_A3->a3_Offset.choice.rsrp >> 1;
|
||||
int rsrp_hysteresis = event_A3->hysteresis;
|
||||
|
||||
// Check all neighboring cells for Event A3 condition
|
||||
bool entry_cond_met = false;
|
||||
bool above_leaving_threshold = false;
|
||||
int entry_neighbor_rsrp = INT_MIN;
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_NEIGHBORING_CELLS_MAX; i++) {
|
||||
meas_t *neighboring_cell = &l3_measurements->neighboring_cell[i];
|
||||
int neighboring_cell_rsrp = get_rsrp_value(neighboring_cell);
|
||||
|
||||
if (neighboring_cell_rsrp == INT_MAX) {
|
||||
LOG_D(NR_RRC, "There are no RSRP measurements taken for the neighboring cell %d\n", i);
|
||||
neighboring_cell_rsrp = INT_MIN;
|
||||
}
|
||||
|
||||
// Check entry condition
|
||||
if (neighboring_cell_rsrp > serving_cell_rsrp + rsrp_offset + rsrp_hysteresis) {
|
||||
entry_cond_met = true;
|
||||
entry_neighbor_rsrp = neighboring_cell_rsrp;
|
||||
}
|
||||
|
||||
// Check if any neighbor is still above leaving threshold
|
||||
if (neighboring_cell_rsrp >= serving_cell_rsrp + rsrp_offset - rsrp_hysteresis) {
|
||||
above_leaving_threshold = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger event if any neighbor meets entry condition
|
||||
if (entry_cond_met && !nr_timer_is_active(&l3_measurements->TA3) && (l3_measurements->reports_sent == 0)) {
|
||||
start_meas_event(l3_measurements,
|
||||
rrcNB,
|
||||
&l3_measurements->TA3,
|
||||
event_trigger_config,
|
||||
report_config_id,
|
||||
NR_MeasTriggerQuantityOffset_PR_rsrp,
|
||||
true,
|
||||
event_A3->timeToTrigger);
|
||||
|
||||
LOG_W(NR_RRC,
|
||||
"(neighboring_cell_rsrp) %i > (serving_cell_rsrp %i) + (rsrp_offset) %i + (rsrp_hysteresis) %i\n",
|
||||
entry_neighbor_rsrp,
|
||||
serving_cell_rsrp,
|
||||
rsrp_offset,
|
||||
rsrp_hysteresis);
|
||||
}
|
||||
// Stop event if all neighbors are below leaving threshold
|
||||
else if (nr_timer_is_active(&l3_measurements->TA3) && !above_leaving_threshold) {
|
||||
stop_meas_event(l3_measurements, &l3_measurements->TA3);
|
||||
}
|
||||
}
|
||||
|
||||
static void nr_ue_check_meas_report(NR_UE_RRC_INST_t *rrc, const uint8_t gnb_index)
|
||||
{
|
||||
rrcPerNB_t *rrcNB = rrc->perNB + gnb_index;
|
||||
@@ -2694,64 +2944,24 @@ static void nr_ue_check_meas_report(NR_UE_RRC_INST_t *rrc, const uint8_t gnb_ind
|
||||
continue;
|
||||
|
||||
NR_EventTriggerConfig_t *event_trigger_config = report_config_nr->reportType.choice.eventTriggered;
|
||||
if (event_trigger_config->eventId.present != NR_EventTriggerConfig__eventId_PR_eventA2)
|
||||
continue;
|
||||
|
||||
struct NR_EventTriggerConfig__eventId__eventA2 *event_A2 = event_trigger_config->eventId.choice.eventA2;
|
||||
if (event_A2->a2_Threshold.present != NR_MeasTriggerQuantity_PR_rsrp)
|
||||
continue;
|
||||
|
||||
meas_t *serving_cell = &l3_measurements->serving_cell;
|
||||
int serving_cell_rsrp = INT_MAX;
|
||||
if (serving_cell->ss_rsrp_dBm.init == true) {
|
||||
serving_cell_rsrp = serving_cell->ss_rsrp_dBm.val;
|
||||
} else if (serving_cell->csi_rsrp_dBm.init == true) {
|
||||
serving_cell_rsrp = serving_cell->csi_rsrp_dBm.val;
|
||||
} else {
|
||||
LOG_E(NR_RRC, "There are no RSRP measurements taken for the active cell\n");
|
||||
}
|
||||
|
||||
// TS 38.133 - Table 10.1.6.1-1: SS-RSRP and CSI-RSRP measurement report mapping
|
||||
int rsrp_threshold = event_A2->a2_Threshold.choice.rsrp - 157;
|
||||
int rsrp_hysteresis = event_A2->hysteresis >> 1;
|
||||
|
||||
// TS 38.331 - 5.5.4.3 Event A2 (Serving becomes worse than threshold)
|
||||
if (serving_cell_rsrp + rsrp_hysteresis < rsrp_threshold) {
|
||||
if (!nr_timer_is_active(&l3_measurements->TA2) && (l3_measurements->reports_sent == 0)) {
|
||||
nr_timer_setup(&l3_measurements->TA2, get_A2_event_time_to_trigger(event_A2->timeToTrigger), 10);
|
||||
nr_timer_start(&l3_measurements->TA2);
|
||||
int report_config_id = report_config->reportConfigId;
|
||||
int meas_id = -1;
|
||||
for (int j = 0; j < MAX_MEAS_ID; j++) {
|
||||
NR_MeasIdToAddMod_t *meas_id_toAddMod = rrcNB->MeasId[j];
|
||||
if (meas_id_toAddMod == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (meas_id_toAddMod->reportConfigId == report_config_id) {
|
||||
meas_id = meas_id_toAddMod->measId;
|
||||
}
|
||||
}
|
||||
AssertFatal(meas_id > 0, "meas_id did not found for report_config_id %i\n", report_config_id);
|
||||
|
||||
l3_measurements->trigger_to_measid = meas_id;
|
||||
l3_measurements->trigger_quantity = event_A2->a2_Threshold.present;
|
||||
l3_measurements->rs_type = event_trigger_config->rsType;
|
||||
l3_measurements->reports_sent = 0;
|
||||
l3_measurements->max_reports = (event_trigger_config->reportAmount == NR_EventTriggerConfig__reportAmount_infinity)
|
||||
? INT_MAX
|
||||
: (1 << event_trigger_config->reportAmount);
|
||||
l3_measurements->report_interval_ms = get_measurement_report_interval_ms(event_trigger_config->reportInterval);
|
||||
|
||||
LOG_W(NR_RRC,
|
||||
"(active_cell_rsrp) %i + (rsrp_hysteresis) %i < (rsrp_threshold) %i\n",
|
||||
serving_cell_rsrp,
|
||||
rsrp_hysteresis,
|
||||
rsrp_threshold);
|
||||
}
|
||||
} else if (nr_timer_is_active(&l3_measurements->TA2) && (serving_cell_rsrp - rsrp_hysteresis > rsrp_threshold)) {
|
||||
nr_timer_stop(&l3_measurements->TA2);
|
||||
nr_timer_stop(&l3_measurements->periodic_report_timer);
|
||||
l3_measurements->reports_sent = 0;
|
||||
switch (event_trigger_config->eventId.present) {
|
||||
case NR_EventTriggerConfig__eventId_PR_eventA2:
|
||||
handle_event_a2(l3_measurements,
|
||||
rrcNB,
|
||||
event_trigger_config->eventId.choice.eventA2,
|
||||
event_trigger_config,
|
||||
report_config->reportConfigId);
|
||||
break;
|
||||
case NR_EventTriggerConfig__eventId_PR_eventA3:
|
||||
handle_event_a3(l3_measurements,
|
||||
rrcNB,
|
||||
event_trigger_config->eventId.choice.eventA3,
|
||||
event_trigger_config,
|
||||
report_config->reportConfigId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2800,6 +3010,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);
|
||||
@@ -2904,21 +3162,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: {
|
||||
@@ -3320,11 +3566,17 @@ void rrc_ue_generate_measurementReport(rrcPerNB_t *rrc, instance_t ue_id)
|
||||
l3_measurements_t *l3m = &rrc->l3_measurements;
|
||||
int rsrp_dBm = l3m->rs_type == NR_NR_RS_Type_ssb ? l3m->serving_cell.ss_rsrp_dBm.val : l3m->serving_cell.csi_rsrp_dBm.val;
|
||||
int rsrp_index = get_rsrp_index(rsrp_dBm);
|
||||
int neighbor_rsrp_dBm =
|
||||
l3m->rs_type == NR_NR_RS_Type_ssb ? l3m->neighboring_cell[0].ss_rsrp_dBm.val : l3m->neighboring_cell[0].csi_rsrp_dBm.val;
|
||||
int neighbor_rsrp_index = get_rsrp_index(neighbor_rsrp_dBm);
|
||||
uint8_t size = do_nrMeasurementReport_SA(l3m->trigger_to_measid,
|
||||
l3m->trigger_quantity,
|
||||
l3m->rs_type,
|
||||
l3m->serving_cell.Nid_cell,
|
||||
rsrp_index,
|
||||
l3m->neighbor_cell_valid,
|
||||
l3m->neighboring_cell[0].Nid_cell,
|
||||
neighbor_rsrp_index,
|
||||
buffer,
|
||||
sizeof(buffer));
|
||||
|
||||
|
||||
@@ -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,13 +197,16 @@ 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;
|
||||
int reports_sent;
|
||||
int max_reports;
|
||||
long report_interval_ms;
|
||||
bool neighbor_cell_valid;
|
||||
NR_timer_t TA2;
|
||||
NR_timer_t TA3;
|
||||
NR_timer_t periodic_report_timer;
|
||||
} l3_measurements_t;
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ void nr_ue_rrc_timer_trigger(int module_id, int hfn, int frame, int gnb_id);
|
||||
void handle_t300_expiry(NR_UE_RRC_INST_t *rrc);
|
||||
void handle_t430_expiry(NR_UE_RRC_INST_t *rrc);
|
||||
|
||||
int get_A2_event_time_to_trigger(long time_to_trigger);
|
||||
int get_event_time_to_trigger(long time_to_trigger);
|
||||
void reset_rlf_timers_and_constants(NR_UE_Timers_Constants_t *tac);
|
||||
void set_default_timers_and_constants(NR_UE_Timers_Constants_t *tac);
|
||||
void nr_rrc_set_sib1_timers_and_constants(NR_UE_Timers_Constants_t *tac, NR_SIB1_t *sib1);
|
||||
|
||||
@@ -129,11 +129,24 @@ void handle_meas_timers(NR_UE_RRC_INST_t *rrc)
|
||||
l3_measurements_t *l3_measurements = &nb->l3_measurements;
|
||||
|
||||
bool ta2_expired = nr_timer_tick(&l3_measurements->TA2);
|
||||
bool ta3_expired = nr_timer_tick(&l3_measurements->TA3);
|
||||
if (ta2_expired && l3_measurements->trigger_quantity > 0) {
|
||||
rrc_ue_generate_measurementReport(nb, rrc->ue_id);
|
||||
l3_measurements->reports_sent = 1;
|
||||
|
||||
if (l3_measurements->reports_sent < l3_measurements->max_reports) {
|
||||
if (l3_measurements->reports_sent < l3_measurements->max_reports
|
||||
&& !nr_timer_is_active(&l3_measurements->periodic_report_timer)) {
|
||||
nr_timer_setup(&l3_measurements->periodic_report_timer, l3_measurements->report_interval_ms, 10);
|
||||
nr_timer_start(&l3_measurements->periodic_report_timer);
|
||||
}
|
||||
}
|
||||
|
||||
if (ta3_expired && l3_measurements->trigger_quantity > 0) {
|
||||
rrc_ue_generate_measurementReport(nb, rrc->ue_id);
|
||||
l3_measurements->reports_sent = 1;
|
||||
|
||||
if (l3_measurements->reports_sent < l3_measurements->max_reports
|
||||
&& !nr_timer_is_active(&l3_measurements->periodic_report_timer)) {
|
||||
nr_timer_setup(&l3_measurements->periodic_report_timer, l3_measurements->report_interval_ms, 10);
|
||||
nr_timer_start(&l3_measurements->periodic_report_timer);
|
||||
}
|
||||
@@ -663,7 +676,7 @@ void reset_rlf_timers_and_constants(NR_UE_Timers_Constants_t *tac)
|
||||
tac->N311_cnt = 0;
|
||||
}
|
||||
|
||||
int get_A2_event_time_to_trigger(long time_to_trigger)
|
||||
int get_event_time_to_trigger(long time_to_trigger)
|
||||
{
|
||||
switch (time_to_trigger) {
|
||||
case NR_TimeToTrigger_ms0:
|
||||
|
||||
Reference in New Issue
Block a user