diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index 042e0ce73d..a9642365a3 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -1559,4 +1559,15 @@ typedef struct f1ap_positioning_measurement_failure_s { // IE 9.3.1.2 (mandatory) long cause_value; } f1ap_positioning_measurement_failure_t; + +typedef struct f1ap_positioning_measurement_report_s { + // IE 9.3.1.23 (mandatory) + uint8_t transaction_id; + // (mandatory) + uint16_t lmf_measurement_id; + // (mandatory) + uint16_t ran_measurement_id; + // (mandatory) + f1ap_pos_measurement_result_list_t *pos_measurement_result_list; +} f1ap_positioning_measurement_report_t; #endif /* F1AP_MESSAGES_TYPES_H_ */ diff --git a/openair2/F1AP/lib/f1ap_positioning.c b/openair2/F1AP/lib/f1ap_positioning.c index 57a8add74d..3bbfe89304 100644 --- a/openair2/F1AP/lib/f1ap_positioning.c +++ b/openair2/F1AP/lib/f1ap_positioning.c @@ -5781,3 +5781,212 @@ void free_positioning_measurement_failure(f1ap_positioning_measurement_failure_t { // nothing to free } + +/** + * @brief Encode F1 positioning measurement report to ASN.1 + */ +F1AP_F1AP_PDU_t *encode_positioning_measurement_report(const f1ap_positioning_measurement_report_t *msg) +{ + F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + + /* Message Type */ + pdu->present = F1AP_F1AP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, tmp); + tmp->procedureCode = F1AP_ProcedureCode_id_PositioningMeasurementReport; + tmp->criticality = F1AP_Criticality_ignore; + tmp->value.present = F1AP_InitiatingMessage__value_PR_PositioningMeasurementReport; + F1AP_PositioningMeasurementReport_t *out = &tmp->value.choice.PositioningMeasurementReport; + + /* mandatory : TransactionID */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_PositioningMeasurementReportIEs_t, ie1); + ie1->id = F1AP_ProtocolIE_ID_id_TransactionID; + ie1->criticality = F1AP_Criticality_reject; + ie1->value.present = F1AP_PositioningMeasurementReportIEs__value_PR_TransactionID; + ie1->value.choice.TransactionID = msg->transaction_id; + + /* mandatory : LMF_MeasurementID */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_PositioningMeasurementReportIEs_t, ie2); + ie2->id = F1AP_ProtocolIE_ID_id_LMF_MeasurementID; + ie2->criticality = F1AP_Criticality_reject; + ie2->value.present = F1AP_PositioningMeasurementReportIEs__value_PR_LMF_MeasurementID; + ie2->value.choice.LMF_MeasurementID = msg->lmf_measurement_id; + + /* mandatory : RAN_MeasurementID */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_PositioningMeasurementReportIEs_t, ie3); + ie3->id = F1AP_ProtocolIE_ID_id_RAN_MeasurementID; + ie3->criticality = F1AP_Criticality_reject; + ie3->value.present = F1AP_PositioningMeasurementReportIEs__value_PR_RAN_MeasurementID; + ie3->value.choice.RAN_MeasurementID = msg->ran_measurement_id; + + /* Positioning Measurement Result List */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_PositioningMeasurementReportIEs_t, ie4); + ie4->id = F1AP_ProtocolIE_ID_id_PosMeasurementResultList; + ie4->criticality = F1AP_Criticality_reject; + ie4->value.present = F1AP_PositioningMeasurementReportIEs__value_PR_PosMeasurementResultList; + const f1ap_pos_measurement_result_list_t *pos_meas_res_list = msg->pos_measurement_result_list; + for (int i = 0; i < pos_meas_res_list->pos_measurement_result_list_length; i++) { + // mandatory : Positioning Measurement Result List Item + asn1cSequenceAdd(ie4->value.choice.PosMeasurementResultList.list, + F1AP_PosMeasurementResultList_Item_t, + f1_pos_meas_result_list_item); + f1ap_pos_measurement_result_list_item_t *pos_meas_res_list_item = &pos_meas_res_list->pos_measurement_result_list_item[i]; + F1AP_PosMeasurementResult_t *f1_posMeasurementResult = &f1_pos_meas_result_list_item->posMeasurementResult; + f1ap_pos_measurement_result_t *pos_measurement_result = &pos_meas_res_list_item->pos_measurement_result; + *f1_posMeasurementResult = encode_positioning_measurement_result(pos_measurement_result); + f1_pos_meas_result_list_item->tRPID = pos_meas_res_list_item->trp_id; + } + + return pdu; +} + +/** + * @brief Decode F1 Positioning Measurement report + */ +bool decode_positioning_measurement_report(const F1AP_F1AP_PDU_t *pdu, f1ap_positioning_measurement_report_t *out) +{ + DevAssert(out != NULL); + memset(out, 0, sizeof(*out)); + + F1AP_PositioningMeasurementReport_t *in = &pdu->choice.initiatingMessage->value.choice.PositioningMeasurementReport; + F1AP_PositioningMeasurementReportIEs_t *ie; + + F1AP_LIB_FIND_IE(F1AP_PositioningMeasurementReportIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_TransactionID, true); + F1AP_LIB_FIND_IE(F1AP_PositioningMeasurementReportIEs_t, + ie, + &in->protocolIEs.list, + F1AP_ProtocolIE_ID_id_LMF_MeasurementID, + true); + F1AP_LIB_FIND_IE(F1AP_PositioningMeasurementReportIEs_t, + ie, + &in->protocolIEs.list, + F1AP_ProtocolIE_ID_id_RAN_MeasurementID, + true); + F1AP_LIB_FIND_IE(F1AP_PositioningMeasurementReportIEs_t, + ie, + &in->protocolIEs.list, + F1AP_ProtocolIE_ID_id_PosMeasurementResultList, + true); + + for (int i = 0; i < in->protocolIEs.list.count; ++i) { + ie = in->protocolIEs.list.array[i]; + AssertError(ie != NULL, return false, "in->protocolIEs.list.array[i] is NULL"); + switch (ie->id) { + case F1AP_ProtocolIE_ID_id_TransactionID: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_PositioningMeasurementReportIEs__value_PR_TransactionID); + out->transaction_id = ie->value.choice.TransactionID; + break; + case F1AP_ProtocolIE_ID_id_LMF_MeasurementID: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_PositioningMeasurementReportIEs__value_PR_LMF_MeasurementID); + out->lmf_measurement_id = ie->value.choice.LMF_MeasurementID; + break; + case F1AP_ProtocolIE_ID_id_RAN_MeasurementID: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_PositioningMeasurementReportIEs__value_PR_RAN_MeasurementID); + out->ran_measurement_id = ie->value.choice.RAN_MeasurementID; + break; + case F1AP_ProtocolIE_ID_id_PosMeasurementResultList: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_PositioningMeasurementReportIEs__value_PR_PosMeasurementResultList); + F1AP_PosMeasurementResultList_t *f1_PosMeasurementResultList = &ie->value.choice.PosMeasurementResultList; + uint32_t pos_meas_result_list_len = f1_PosMeasurementResultList->list.count; + out->pos_measurement_result_list = calloc_or_fail(1, sizeof(*out->pos_measurement_result_list)); + f1ap_pos_measurement_result_list_t *pos_meas_res_list = out->pos_measurement_result_list; + pos_meas_res_list->pos_measurement_result_list_length = pos_meas_result_list_len; + pos_meas_res_list->pos_measurement_result_list_item = + calloc_or_fail(pos_meas_result_list_len, sizeof(*pos_meas_res_list->pos_measurement_result_list_item)); + for (int i = 0; i < pos_meas_result_list_len; i++) { + F1AP_PosMeasurementResultList_Item_t *f1_pos_meas_result_list_item = f1_PosMeasurementResultList->list.array[i]; + f1ap_pos_measurement_result_list_item_t *pos_meas_result_list_item = + &pos_meas_res_list->pos_measurement_result_list_item[i]; + _F1_CHECK_EXP(decode_positioning_measurement_result(&f1_pos_meas_result_list_item->posMeasurementResult, + &pos_meas_result_list_item->pos_measurement_result)); + pos_meas_result_list_item->trp_id = f1_pos_meas_result_list_item->tRPID; + } + break; + default: + PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); + break; + } + } + + return true; +} + +/** + * @brief F1 Positioning Measurement report deep copy + */ +f1ap_positioning_measurement_report_t cp_positioning_measurement_report(const f1ap_positioning_measurement_report_t *orig) +{ + // copy all mandatory fields that are not dynamic memory + f1ap_positioning_measurement_report_t cp = { + .transaction_id = orig->transaction_id, + .lmf_measurement_id = orig->lmf_measurement_id, + .ran_measurement_id = orig->ran_measurement_id, + }; + + cp.pos_measurement_result_list = calloc_or_fail(1, sizeof(*cp.pos_measurement_result_list)); + f1ap_pos_measurement_result_list_t *list_orig = orig->pos_measurement_result_list; + f1ap_pos_measurement_result_list_t *list_cp = cp.pos_measurement_result_list; + uint32_t pos_meas_result_list_len = list_orig->pos_measurement_result_list_length; + list_cp->pos_measurement_result_list_length = pos_meas_result_list_len; + list_cp->pos_measurement_result_list_item = + calloc_or_fail(pos_meas_result_list_len, sizeof(*list_cp->pos_measurement_result_list_item)); + for (int i = 0; i < pos_meas_result_list_len; i++) { + f1ap_pos_measurement_result_list_item_t *item_cp = &list_cp->pos_measurement_result_list_item[i]; + f1ap_pos_measurement_result_list_item_t *item_orig = &list_orig->pos_measurement_result_list_item[i]; + item_cp->pos_measurement_result = cp_positioning_measurement_result(&item_orig->pos_measurement_result); + item_cp->trp_id = item_orig->trp_id; + } + + return cp; +} + +/** + * @brief F1 Positioning Measurement report equality check + */ +bool eq_positioning_measurement_report(const f1ap_positioning_measurement_report_t *a, + const f1ap_positioning_measurement_report_t *b) +{ + _F1_EQ_CHECK_INT(a->transaction_id, b->transaction_id); + _F1_EQ_CHECK_INT(a->lmf_measurement_id, b->lmf_measurement_id); + _F1_EQ_CHECK_INT(a->ran_measurement_id, b->ran_measurement_id); + + f1ap_pos_measurement_result_list_t *list_a = a->pos_measurement_result_list; + f1ap_pos_measurement_result_list_t *list_b = b->pos_measurement_result_list; + uint32_t pos_meas_result_list_len = list_a->pos_measurement_result_list_length; + _F1_EQ_CHECK_INT(list_b->pos_measurement_result_list_length, pos_meas_result_list_len); + for (int i = 0; i < pos_meas_result_list_len; i++) { + f1ap_pos_measurement_result_list_item_t *item_a = &list_a->pos_measurement_result_list_item[i]; + f1ap_pos_measurement_result_list_item_t *item_b = &list_b->pos_measurement_result_list_item[i]; + _F1_CHECK_EXP(eq_positioning_measurement_result(&item_a->pos_measurement_result, &item_b->pos_measurement_result)); + _F1_EQ_CHECK_INT(item_a->trp_id, item_b->trp_id); + } + + return true; +} + +/** + * @brief Free Allocated F1 Positioning Measurement report + */ +void free_positioning_measurement_report(f1ap_positioning_measurement_report_t *msg) +{ + f1ap_pos_measurement_result_list_t *list = msg->pos_measurement_result_list; + uint32_t pos_meas_result_list_len = list->pos_measurement_result_list_length; + for (int i = 0; i < pos_meas_result_list_len; i++) { + f1ap_pos_measurement_result_t *posMeasurementResult = &list->pos_measurement_result_list_item[i].pos_measurement_result; + uint32_t pos_meas_result_length = posMeasurementResult->pos_measurement_result_item_length; + for (int j = 0; j < pos_meas_result_length; j++) { + f1ap_pos_measurement_result_item_t *item = &posMeasurementResult->pos_measurement_result_item[j]; + f1ap_measured_results_value_t *measuredResultsValue = &item->measured_results_value; + if (measuredResultsValue->present == F1AP_MEASURED_RESULTS_VALUE_PR_UL_ANGLEOFARRIVAL) { + if (measuredResultsValue->choice.ul_angle_of_arrival.zenith_aoa) { + free(measuredResultsValue->choice.ul_angle_of_arrival.zenith_aoa); + } + if (measuredResultsValue->choice.ul_angle_of_arrival.lcs_to_gcs_translation_aoa) { + free(measuredResultsValue->choice.ul_angle_of_arrival.lcs_to_gcs_translation_aoa); + } + } + } + free(posMeasurementResult->pos_measurement_result_item); + } + free(msg->pos_measurement_result_list->pos_measurement_result_list_item); + free(msg->pos_measurement_result_list); +} diff --git a/openair2/F1AP/lib/f1ap_positioning.h b/openair2/F1AP/lib/f1ap_positioning.h index 061bb29536..f138367d1c 100644 --- a/openair2/F1AP/lib/f1ap_positioning.h +++ b/openair2/F1AP/lib/f1ap_positioning.h @@ -128,4 +128,12 @@ f1ap_positioning_measurement_failure_t cp_positioning_measurement_failure(const bool eq_positioning_measurement_failure(const f1ap_positioning_measurement_failure_t *a, const f1ap_positioning_measurement_failure_t *b); void free_positioning_measurement_failure(f1ap_positioning_measurement_failure_t *msg); + +// positioning measurement report +struct F1AP_F1AP_PDU *encode_positioning_measurement_report(const f1ap_positioning_measurement_report_t *msg); +bool decode_positioning_measurement_report(const struct F1AP_F1AP_PDU *pdu, f1ap_positioning_measurement_report_t *out); +f1ap_positioning_measurement_report_t cp_positioning_measurement_report(const f1ap_positioning_measurement_report_t *orig); +bool eq_positioning_measurement_report(const f1ap_positioning_measurement_report_t *a, + const f1ap_positioning_measurement_report_t *b); +void free_positioning_measurement_report(f1ap_positioning_measurement_report_t *msg); #endif /* F1AP_POSITIONING_H_ */ diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index ed70b6ea9b..c7b009b081 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -2166,6 +2166,108 @@ static void test_f1ap_positioning_measurement_failure() printf("%s() successful\n", __func__); } +static void test_f1ap_positioning_measurement_report() +{ + f1ap_positioning_measurement_report_t orig = { + .transaction_id = 12, + .lmf_measurement_id = 1999, + .ran_measurement_id = 2225, + }; + + orig.pos_measurement_result_list = calloc_or_fail(1, sizeof(*orig.pos_measurement_result_list)); + uint32_t pos_meas_result_list_len = 2; + orig.pos_measurement_result_list->pos_measurement_result_list_length = pos_meas_result_list_len; + orig.pos_measurement_result_list->pos_measurement_result_list_item = + calloc_or_fail(pos_meas_result_list_len, sizeof(*orig.pos_measurement_result_list->pos_measurement_result_list_item)); + for (int i = 0; i < pos_meas_result_list_len; i++) { + f1ap_pos_measurement_result_list_item_t *pos_meas_result_list_item = + &orig.pos_measurement_result_list->pos_measurement_result_list_item[i]; + pos_meas_result_list_item->trp_id = 10 + i; + + f1ap_pos_measurement_result_t *posMeasurementResult = &pos_meas_result_list_item->pos_measurement_result; + uint32_t pos_meas_result_length = 4; + posMeasurementResult->pos_measurement_result_item_length = pos_meas_result_length; + posMeasurementResult->pos_measurement_result_item = + calloc_or_fail(pos_meas_result_length, sizeof(*posMeasurementResult->pos_measurement_result_item)); + + // Angle of arrival + f1ap_pos_measurement_result_item_t *pos_measurement_result_item = &posMeasurementResult->pos_measurement_result_item[0]; + f1ap_measured_results_value_t *measuredResultsValue = &pos_measurement_result_item->measured_results_value; + measuredResultsValue->present = F1AP_MEASURED_RESULTS_VALUE_PR_UL_ANGLEOFARRIVAL; + f1ap_ul_aoa_t *uL_AngleOfArrival = &measuredResultsValue->choice.ul_angle_of_arrival; + uL_AngleOfArrival->azimuth_aoa = 700 + i; + uL_AngleOfArrival->zenith_aoa = calloc_or_fail(1, sizeof(*uL_AngleOfArrival->zenith_aoa)); + *uL_AngleOfArrival->zenith_aoa = 450 + i; + uL_AngleOfArrival->lcs_to_gcs_translation_aoa = calloc_or_fail(1, sizeof(*uL_AngleOfArrival->lcs_to_gcs_translation_aoa)); + uL_AngleOfArrival->lcs_to_gcs_translation_aoa->alpha = 200; + uL_AngleOfArrival->lcs_to_gcs_translation_aoa->beta = 220; + uL_AngleOfArrival->lcs_to_gcs_translation_aoa->gamma = 280; + + f1ap_time_stamp_t *timeStamp = &pos_measurement_result_item->time_stamp; + timeStamp->system_frame_number = 100; + timeStamp->slot_index.present = F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_30; + timeStamp->slot_index.choice.scs_30 = 15; + + // UL SRS RSRP + pos_measurement_result_item = &posMeasurementResult->pos_measurement_result_item[1]; + measuredResultsValue = &pos_measurement_result_item->measured_results_value; + measuredResultsValue->present = F1AP_MEASURED_RESULTS_VALUE_PR_UL_SRS_RSRP; + measuredResultsValue->choice.ul_srs_rsrp = 100 + i; + + timeStamp = &pos_measurement_result_item->time_stamp; + timeStamp->system_frame_number = 101; + timeStamp->slot_index.present = F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_15; + timeStamp->slot_index.choice.scs_15 = 8; + + // UL RToA + pos_measurement_result_item = &posMeasurementResult->pos_measurement_result_item[2]; + measuredResultsValue = &pos_measurement_result_item->measured_results_value; + measuredResultsValue->present = F1AP_MEASURED_RESULTS_VALUE_PR_UL_RTOA; + f1ap_ul_rtoa_measurement_t *uL_RTOA = &measuredResultsValue->choice.ul_rtoa; + uL_RTOA->ul_rtoa_measurement_item.present = F1AP_ULRTOAMEAS_PR_K1; + uL_RTOA->ul_rtoa_measurement_item.choice.k1 = 98500 + i; + + timeStamp = &pos_measurement_result_item->time_stamp; + timeStamp->system_frame_number = 102; + timeStamp->slot_index.present = F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_60; + timeStamp->slot_index.choice.scs_60 = 31; + + // gNB RX-TX Time Diff + pos_measurement_result_item = &posMeasurementResult->pos_measurement_result_item[3]; + measuredResultsValue = &pos_measurement_result_item->measured_results_value; + measuredResultsValue->present = F1AP_MEASURED_RESULTS_VALUE_PR_GNB_RXTXTIMEDIFF; + f1ap_gnb_rx_tx_time_diff_t *gNB_RxTxTimeDiff = &measuredResultsValue->choice.gnb_rx_tx_time_diff; + gNB_RxTxTimeDiff->rx_tx_time_diff.present = F1AP_GNBRXTXTIMEDIFFMEAS_PR_K3; + gNB_RxTxTimeDiff->rx_tx_time_diff.choice.k3 = 98412 + i; + + timeStamp = &pos_measurement_result_item->time_stamp; + timeStamp->system_frame_number = 103; + timeStamp->slot_index.present = F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_120; + timeStamp->slot_index.choice.scs_120 = 70; + } + + F1AP_F1AP_PDU_t *f1enc = encode_positioning_measurement_report(&orig); + F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); + f1ap_msg_free(f1enc); + + f1ap_positioning_measurement_report_t decoded = {0}; + bool ret = decode_positioning_measurement_report(f1dec, &decoded); + AssertFatal(ret, "decode_positioning_measurement_report(): could not decode message\n"); + f1ap_msg_free(f1dec); + + ret = eq_positioning_measurement_report(&orig, &decoded); + AssertFatal(ret, "eq_positioning_measurement_report(): decoded message doesn't match\n"); + free_positioning_measurement_report(&decoded); + + f1ap_positioning_measurement_report_t cp = cp_positioning_measurement_report(&orig); + ret = eq_positioning_measurement_report(&orig, &cp); + AssertFatal(ret, "eq_positioning_measurement_report(): copied message doesn't match\n"); + free_positioning_measurement_report(&orig); + free_positioning_measurement_report(&cp); + + printf("%s() successful\n", __func__); +} + int main() { test_initial_ul_rrc_message_transfer(); @@ -2206,5 +2308,6 @@ int main() test_f1ap_positioning_measurement_request(); test_f1ap_positioning_measurement_response(); test_f1ap_positioning_measurement_failure(); + test_f1ap_positioning_measurement_report(); return 0; }