diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 585b1e48e2..054d76d953 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -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); diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index eb8da91c2a..43d51a9e07 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -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); diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index dbd6fb2818..ca28386275 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -2741,6 +2741,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; @@ -2759,64 +2932,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; } } } @@ -3421,11 +3554,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)); diff --git a/openair2/RRC/NR_UE/rrc_defs.h b/openair2/RRC/NR_UE/rrc_defs.h index a8ce126068..efe2e6e147 100644 --- a/openair2/RRC/NR_UE/rrc_defs.h +++ b/openair2/RRC/NR_UE/rrc_defs.h @@ -204,7 +204,9 @@ typedef struct l3_measurements_s { 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; diff --git a/openair2/RRC/NR_UE/rrc_proto.h b/openair2/RRC/NR_UE/rrc_proto.h index d8f2c0f23f..ab14f704b9 100644 --- a/openair2/RRC/NR_UE/rrc_proto.h +++ b/openair2/RRC/NR_UE/rrc_proto.h @@ -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); diff --git a/openair2/RRC/NR_UE/rrc_timers_and_constants.c b/openair2/RRC/NR_UE/rrc_timers_and_constants.c index 133ffb8d8e..d7ad5c249e 100644 --- a/openair2/RRC/NR_UE/rrc_timers_and_constants.c +++ b/openair2/RRC/NR_UE/rrc_timers_and_constants.c @@ -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: