From 3e65d75f833e7abebae37a093162405678fb7bd7 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Mon, 18 Nov 2024 17:29:20 +0000 Subject: [PATCH] UE sends the measurement report for event A2 --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 57 ++++++++++++ openair2/RRC/NR/MESSAGES/asn1_msg.h | 11 +++ openair2/RRC/NR_UE/rrc_UE.c | 93 +++++++++++++++++++ openair2/RRC/NR_UE/rrc_defs.h | 1 + openair2/RRC/NR_UE/rrc_proto.h | 3 + openair2/RRC/NR_UE/rrc_timers_and_constants.c | 55 +++++++++++ 6 files changed, 220 insertions(+) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 3ff6aecea9..21ccb1e40f 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -785,6 +785,63 @@ int do_RRCSetupComplete(uint8_t *buffer, return((enc_rval.encoded+7)/8); } +// TODO: This function is only implemented for event A2 +int do_nrMeasurementReport_SA(NR_MeasurementReport_t *measurementReport, + long trigger_to_measid, + long trigger_quantity, + long rs_type, + uint16_t Nid_cell, + int rsrp_dBm, + uint8_t *buffer, + size_t buffer_size) +{ + asn_enc_rval_t enc_rval; + NR_UL_DCCH_Message_t ul_dcch_msg; + memset((void *)&ul_dcch_msg, 0, sizeof(NR_UL_DCCH_Message_t)); + + ul_dcch_msg.message.present = NR_UL_DCCH_MessageType_PR_c1; + ul_dcch_msg.message.choice.c1 = CALLOC(1, sizeof(struct NR_UL_DCCH_MessageType__c1)); + ul_dcch_msg.message.choice.c1->present = NR_UL_DCCH_MessageType__c1_PR_measurementReport; + + memset(measurementReport, 0, sizeof(struct NR_MeasurementReport)); + ul_dcch_msg.message.choice.c1->choice.measurementReport = measurementReport; + measurementReport->criticalExtensions.present = NR_MeasurementReport__criticalExtensions_PR_measurementReport; + + NR_MeasurementReport_IEs_t *measurementReport_ie = CALLOC(1, sizeof(struct NR_MeasurementReport_IEs)); + measurementReport->criticalExtensions.choice.measurementReport = measurementReport_ie; + measurementReport_ie->measResults.measId = trigger_to_measid; + + NR_MeasResultServMO_t *measResultServMo = CALLOC(1, sizeof(struct NR_MeasResultServMO)); + + NR_MeasResultNR_t *measResultServingCell = &measResultServMo->measResultServingCell; + measResultServingCell->physCellId = CALLOC(1, sizeof(NR_PhysCellId_t)); + *measResultServingCell->physCellId = Nid_cell; + + struct NR_MeasQuantityResults *active_mq_res = CALLOC(1, sizeof(struct NR_MeasQuantityResults)); + + if (trigger_quantity == NR_MeasTriggerQuantityOffset_PR_rsrp) { + active_mq_res->rsrp = CALLOC(1, sizeof(NR_RSRP_Range_t)); + + *active_mq_res->rsrp = rsrp_dBm + 157; + if (rs_type == NR_NR_RS_Type_ssb) + measResultServingCell->measResult.cellResults.resultsSSB_Cell = active_mq_res; + else + measResultServingCell->measResult.cellResults.resultsCSI_RS_Cell = active_mq_res; + } + + ASN_SEQUENCE_ADD(&measurementReport_ie->measResults.measResultServingMOList.list, measResultServMo); + 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); + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { + xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg); + } + + LOG_I(NR_RRC, "MeasurementReport Encoded %zd bits (%zd bytes)\n", enc_rval.encoded, (enc_rval.encoded + 7) / 8); + + return ((enc_rval.encoded + 7) / 8); +} + int do_NR_DLInformationTransfer(uint8_t *buffer, size_t buffer_len, uint8_t transaction_id, diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index dde62d142f..4f13ca2fad 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -50,11 +50,13 @@ #include "NR_ReestablishmentCause.h" #include "NR_SRB-ToAddModList.h" #include "NR_SecurityConfig.h" +#include "NR_MeasurementReport.h" #include "NR_MeasurementTimingConfiguration.h" #include "ds/seq_arr.h" #include "ds/byte_array.h" #include "rrc_messages_types.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h" +#include "common/utils/nr/nr_common.h" struct asn_TYPE_descriptor_s; typedef struct { @@ -127,6 +129,15 @@ int do_NR_MeasurementTimingConfiguration(const NR_MeasurementTimingConfiguration int do_RRCSetupRequest(uint8_t *buffer, size_t buffer_size, uint8_t *rv, uint64_t fiveG_S_TMSI_part1); +int do_nrMeasurementReport_SA(NR_MeasurementReport_t *measurementReport, + long trigger_to_measid, + long trigger_quantity, + long rs_type, + uint16_t Nid_cell, + int rsrp_dBm, + uint8_t *buffer, + size_t buffer_size); + int do_NR_RRCReconfigurationComplete_for_nsa(uint8_t *buffer, size_t buffer_size, NR_RRC_TransactionIdentifier_t Transaction_id); int do_NR_RRCReconfigurationComplete(uint8_t *buffer, size_t buffer_size, const uint8_t Transaction_id); diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 8683269054..d0b551f67a 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -2277,6 +2277,79 @@ void nr_ue_meas_filtering(rrcPerNB_t *rrc, bool is_neighboring_cell, uint16_t Ni apply_ema(&meas_cell->ss_rsrp_dBm, l3_measurements->ssb_filter_coeff_rsrp, rsrp_dBm); } +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; + l3_measurements_t *l3_measurements = &rrcNB->l3_measurements; + + for (int i = 0; i < MAX_MEAS_CONFIG; i++) { + NR_ReportConfigToAddMod_t *report_config = rrcNB->ReportConfig[i]; + if (report_config == NULL) + continue; + + if (report_config->reportConfig.present != NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR) + continue; + + NR_ReportConfigNR_t *report_config_nr = report_config->reportConfig.choice.reportConfigNR; + if (report_config_nr->reportType.present != NR_ReportConfigNR__reportType_PR_eventTriggered) + 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)) { + 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; + + 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); + } + } +} + void nr_rrc_handle_ra_indication(NR_UE_RRC_INST_t *rrc, bool ra_succeeded) { NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants; @@ -2407,6 +2480,7 @@ void *rrc_nrue(void *notUsed) 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 - 157); + nr_ue_check_meas_report(rrc, NR_RRC_MAC_MEAS_DATA_IND(msg_p).gnb_index); break; case NR_RRC_MAC_CCCH_DATA_IND: { @@ -3045,3 +3119,22 @@ void nr_rrc_set_mac_queue(instance_t instance, notifiedFIFO_t *mac_input_nf) NR_UE_RRC_INST_t *rrc = get_NR_UE_rrc_inst(instance); rrc->mac_input_nf = mac_input_nf; } + +void rrc_ue_generate_measurementReport(rrcPerNB_t *rrc, instance_t ue_id) +{ + uint8_t buffer[NR_RRC_BUF_SIZE]; + NR_MeasurementReport_t measurementReport = {0}; + 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; + uint8_t size = do_nrMeasurementReport_SA(&measurementReport, + l3m->trigger_to_measid, + l3m->trigger_quantity, + l3m->rs_type, + l3m->serving_cell.Nid_cell, + rsrp_dBm, + buffer, + sizeof(buffer)); + + int srb_id = 1; // possibly TODO in SRB3 in some cases + nr_pdcp_data_req_srb(ue_id, srb_id, 0, size, buffer, deliver_pdu_srb_rlc, NULL); +} diff --git a/openair2/RRC/NR_UE/rrc_defs.h b/openair2/RRC/NR_UE/rrc_defs.h index 31e7305b96..dfcd01a09a 100644 --- a/openair2/RRC/NR_UE/rrc_defs.h +++ b/openair2/RRC/NR_UE/rrc_defs.h @@ -199,6 +199,7 @@ typedef struct l3_measurements_s { long trigger_to_measid; long trigger_quantity; long rs_type; + NR_timer_t TA2; } l3_measurements_t; typedef struct rrcPerNB { diff --git a/openair2/RRC/NR_UE/rrc_proto.h b/openair2/RRC/NR_UE/rrc_proto.h index ba853f546d..d332ca5175 100644 --- a/openair2/RRC/NR_UE/rrc_proto.h +++ b/openair2/RRC/NR_UE/rrc_proto.h @@ -55,6 +55,8 @@ void nr_rrc_going_to_IDLE(NR_UE_RRC_INST_t *rrc, void handle_RRCRelease(NR_UE_RRC_INST_t *rrc); +void rrc_ue_generate_measurementReport(rrcPerNB_t *rrc, instance_t ue_id); + void set_rlf_sib1_timers_and_constants(NR_UE_Timers_Constants_t *tac, NR_UE_TimersAndConstants_t *ue_TimersAndConstants); /**\brief RRC UE task. @@ -83,6 +85,7 @@ void nr_ue_rrc_timer_trigger(int module_id, 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); 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 57f6be2eb6..b0bc4661bc 100644 --- a/openair2/RRC/NR_UE/rrc_timers_and_constants.c +++ b/openair2/RRC/NR_UE/rrc_timers_and_constants.c @@ -122,6 +122,19 @@ void nr_rrc_SI_timers(NR_UE_RRC_SI_INFO *SInfo) } } +void handle_meas_timers(NR_UE_RRC_INST_t *rrc) +{ + for (int i = 0; i < NB_CNX_UE; i++) { + rrcPerNB_t *nb = &rrc->perNB[i]; + l3_measurements_t *l3_measurements = &nb->l3_measurements; + + bool ta2_expired = nr_timer_tick(&l3_measurements->TA2); + if (ta2_expired && l3_measurements->trigger_quantity > 0) { + rrc_ue_generate_measurementReport(nb, rrc->ue_id); + } + } +} + void nr_rrc_handle_timers(NR_UE_RRC_INST_t *rrc) { NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants; @@ -185,6 +198,8 @@ void nr_rrc_handle_timers(NR_UE_RRC_INST_t *rrc) // Spec 38.331 Section 5.2.2.6 handle_t430_expiry(rrc); } + + handle_meas_timers(rrc); } int nr_rrc_get_T304(long t304) @@ -630,3 +645,43 @@ void reset_rlf_timers_and_constants(NR_UE_Timers_Constants_t *tac) tac->N310_cnt = 0; tac->N311_cnt = 0; } + +int get_A2_event_time_to_trigger(long time_to_trigger) +{ + switch (time_to_trigger) { + case NR_TimeToTrigger_ms0: + return 0; + case NR_TimeToTrigger_ms40: + return 40; + case NR_TimeToTrigger_ms64: + return 64; + case NR_TimeToTrigger_ms80: + return 80; + case NR_TimeToTrigger_ms100: + return 100; + case NR_TimeToTrigger_ms128: + return 128; + case NR_TimeToTrigger_ms160: + return 160; + case NR_TimeToTrigger_ms256: + return 256; + case NR_TimeToTrigger_ms320: + return 320; + case NR_TimeToTrigger_ms480: + return 480; + case NR_TimeToTrigger_ms512: + return 512; + case NR_TimeToTrigger_ms640: + return 640; + case NR_TimeToTrigger_ms1024: + return 1024; + case NR_TimeToTrigger_ms1280: + return 1280; + case NR_TimeToTrigger_ms2560: + return 2560; + case NR_TimeToTrigger_ms5120: + return 5120; + default: + AssertFatal(false, "Invalid TimeToTrigger %ld\n", time_to_trigger); + } +}