From 372ab680561024ae4874396c99825db960602de4 Mon Sep 17 00:00:00 2001 From: Rakesh Mundlamuri Date: Tue, 23 Dec 2025 13:35:00 +0530 Subject: [PATCH] Add F1AP positioning measurement response message enc/dec + test Reference: 3GPP 38.473 v16.21.0 Mandatory fields: Transcation ID (supported) LMF Measurement ID (supported) RAN Measurement ID (supported) Positioning Measurement Result List (range: 0..1) > Positioning Measurement Result List Item (range: 1..64) >> Positioning Measurement Result (supported) >> TRP ID (supported) Optional fields: Criticality Diagnostics (not supported) --- openair2/COMMON/f1ap_messages_types.h | 135 +++++ openair2/F1AP/lib/f1ap_lib_includes.h | 6 + openair2/F1AP/lib/f1ap_positioning.c | 840 ++++++++++++++++++++++++++ openair2/F1AP/lib/f1ap_positioning.h | 6 + openair2/F1AP/tests/f1ap_lib_test.c | 103 ++++ 5 files changed, 1090 insertions(+) diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index b7b227549f..fc8587c35c 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -1295,6 +1295,130 @@ typedef enum f1ap_pos_report_characteristics_e { F1AP_POSREPORTCHARACTERISTICS_PERIODIC = 1 } f1ap_pos_report_characteristics_pr; +typedef struct f1ap_lcs_to_gcs_translationaoa_c { + uint16_t alpha; + uint16_t beta; + uint16_t gamma; +} f1ap_lcs_to_gcs_translationaoa_t; + +typedef struct f1ap_ul_aoa_s { + uint16_t azimuth_aoa; + uint16_t *zenith_aoa; + f1ap_lcs_to_gcs_translationaoa_t *lcs_to_gcs_translation_aoa; +} f1ap_ul_aoa_t; + +typedef union f1ap_relative_path_delay_c { + uint32_t k0; + uint32_t k1; + uint32_t k2; + uint32_t k3; + uint32_t k4; + uint32_t k5; +} f1ap_relative_path_delay_u, f1ap_ul_rtoa_measurement_item_u, f1ap_gnb_rx_tx_time_diff_meas_u; + +typedef enum f1ap_gnb_rx_tx_time_diff_meas_e { + F1AP_GNBRXTXTIMEDIFFMEAS_PR_NOTHING, + F1AP_GNBRXTXTIMEDIFFMEAS_PR_K0, + F1AP_GNBRXTXTIMEDIFFMEAS_PR_K1, + F1AP_GNBRXTXTIMEDIFFMEAS_PR_K2, + F1AP_GNBRXTXTIMEDIFFMEAS_PR_K3, + F1AP_GNBRXTXTIMEDIFFMEAS_PR_K4, + F1AP_GNBRXTXTIMEDIFFMEAS_PR_K5 +} f1ap_gnb_rx_tx_time_diff_meas_pr; + +typedef struct f1ap_gnb_rx_tx_time_diff_meas_s { + f1ap_gnb_rx_tx_time_diff_meas_pr present; + f1ap_gnb_rx_tx_time_diff_meas_u choice; +} f1ap_gnb_rx_tx_time_diff_meas_t; + +typedef enum f1ap_ul_rtoa_measurement_item_e { + F1AP_ULRTOAMEAS_PR_NOTHING, + F1AP_ULRTOAMEAS_PR_K0, + F1AP_ULRTOAMEAS_PR_K1, + F1AP_ULRTOAMEAS_PR_K2, + F1AP_ULRTOAMEAS_PR_K3, + F1AP_ULRTOAMEAS_PR_K4, + F1AP_ULRTOAMEAS_PR_K5 +} f1ap_ul_rtoa_measurement_item_pr; + +typedef struct f1ap_ul_rtoa_measurement_item_s { + f1ap_ul_rtoa_measurement_item_pr present; + f1ap_ul_rtoa_measurement_item_u choice; +} f1ap_ul_rtoa_measurement_item_t; + +typedef union f1ap_ul_rtoa_measurement_s { + f1ap_ul_rtoa_measurement_item_t ul_rtoa_measurement_item; +} f1ap_ul_rtoa_measurement_t; + +typedef struct f1ap_gnb_rx_tx_time_diff_s { + f1ap_gnb_rx_tx_time_diff_meas_t rx_tx_time_diff; +} f1ap_gnb_rx_tx_time_diff_t; + +typedef union f1ap_measured_results_value_c { + f1ap_ul_aoa_t ul_angle_of_arrival; + uint8_t ul_srs_rsrp; + f1ap_ul_rtoa_measurement_t ul_rtoa; + f1ap_gnb_rx_tx_time_diff_t gnb_rx_tx_time_diff; +} f1ap_measured_results_value_u; + +typedef enum f1ap_measured_results_value_e { + F1AP_MEASURED_RESULTS_VALUE_PR_NOTHING, + F1AP_MEASURED_RESULTS_VALUE_PR_UL_ANGLEOFARRIVAL, + F1AP_MEASURED_RESULTS_VALUE_PR_UL_SRS_RSRP, + F1AP_MEASURED_RESULTS_VALUE_PR_UL_RTOA, + F1AP_MEASURED_RESULTS_VALUE_PR_GNB_RXTXTIMEDIFF +} f1ap_measured_results_value_pr; + +typedef struct f1ap_measured_results_value_s { + f1ap_measured_results_value_pr present; + f1ap_measured_results_value_u choice; +} f1ap_measured_results_value_t; + +typedef union f1ap_time_stamp_slot_index_c { + uint8_t scs_15; + uint8_t scs_30; + uint8_t scs_60; + uint8_t scs_120; +} f1ap_time_stamp_slot_index_u; + +typedef enum f1ap_time_stamp_slot_index_e { + F1AP_TIME_STAMP_SLOT_INDEX_PR_NOTHING, + F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_15, + F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_30, + F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_60, + F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_120 +} f1ap_time_stamp_slot_index_pr; + +typedef struct f1ap_time_stamp_slot_index_s { + f1ap_time_stamp_slot_index_pr present; + f1ap_time_stamp_slot_index_u choice; +} f1ap_time_stamp_slot_index_t; + +typedef struct f1ap_time_stamp_s { + uint16_t system_frame_number; + f1ap_time_stamp_slot_index_t slot_index; +} f1ap_time_stamp_t; + +typedef struct f1ap_pos_measurement_result_item_s { + f1ap_measured_results_value_t measured_results_value; + f1ap_time_stamp_t time_stamp; +} f1ap_pos_measurement_result_item_t; + +typedef struct f1ap_pos_measurement_result_s { + f1ap_pos_measurement_result_item_t *pos_measurement_result_item; + uint32_t pos_measurement_result_item_length; +} f1ap_pos_measurement_result_t; + +typedef struct f1ap_pos_measurement_result_list_item_s { + f1ap_pos_measurement_result_t pos_measurement_result; + uint32_t trp_id; +} f1ap_pos_measurement_result_list_item_t; + +typedef struct f1ap_pos_measurement_result_list_s { + f1ap_pos_measurement_result_list_item_t *pos_measurement_result_list_item; + uint32_t pos_measurement_result_list_length; +} f1ap_pos_measurement_result_list_t; + typedef struct f1ap_positioning_information_req_s { // IE 9.3.1.4 (mandatory) uint32_t gNB_CU_ue_id; @@ -1412,4 +1536,15 @@ typedef struct f1ap_measurement_req_s { f1ap_srs_configuration_t *srs_configuration; } f1ap_positioning_measurement_req_t; +typedef struct f1ap_positioning_measurement_resp_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_resp_t; + #endif /* F1AP_MESSAGES_TYPES_H_ */ diff --git a/openair2/F1AP/lib/f1ap_lib_includes.h b/openair2/F1AP/lib/f1ap_lib_includes.h index 3211cb3d45..a50bd5efe5 100644 --- a/openair2/F1AP/lib/f1ap_lib_includes.h +++ b/openair2/F1AP/lib/f1ap_lib_includes.h @@ -84,5 +84,11 @@ #include "F1AP_RelativeCartesianLocation.h" #include "F1AP_TRP-MeasurementRequestItem.h" #include "F1AP_PosMeasurementQuantities-Item.h" +#include "F1AP_PosMeasurementResultList-Item.h" +#include "F1AP_PosMeasurementResultItem.h" +#include "F1AP_UL-AoA.h" +#include "F1AP_UL-RTOA-Measurement.h" +#include "F1AP_GNB-RxTxTimeDiff.h" +#include "F1AP_LCS-to-GCS-TranslationAoA.h" #endif /* F1AP_LIB_INCLUDES_H_ */ diff --git a/openair2/F1AP/lib/f1ap_positioning.c b/openair2/F1AP/lib/f1ap_positioning.c index 859d9a2ae4..c8a7cfee72 100644 --- a/openair2/F1AP/lib/f1ap_positioning.c +++ b/openair2/F1AP/lib/f1ap_positioning.c @@ -2840,6 +2840,628 @@ static bool eq_trp_info_type_response_item(const f1ap_trp_information_type_respo return true; } +static F1AP_PosMeasurementResult_t encode_positioning_measurement_result(const f1ap_pos_measurement_result_t *posMeasurementResult) +{ + F1AP_PosMeasurementResult_t f1_posMeasurementResult = {0}; + for (int i = 0; i < posMeasurementResult->pos_measurement_result_item_length; i++) { + asn1cSequenceAdd(f1_posMeasurementResult.list, F1AP_PosMeasurementResultItem_t, f1_pos_measurement_result_item); + f1ap_pos_measurement_result_item_t *pos_measurement_result_item = &posMeasurementResult->pos_measurement_result_item[i]; + + // measuredResultsValue + F1AP_MeasuredResultsValue_t *f1_measuredResultsValue = &f1_pos_measurement_result_item->measuredResultsValue; + f1ap_measured_results_value_t *measuredResultsValue = &pos_measurement_result_item->measured_results_value; + + switch (measuredResultsValue->present) { + case F1AP_MEASURED_RESULTS_VALUE_PR_NOTHING: + f1_measuredResultsValue->present = F1AP_MeasuredResultsValue_PR_NOTHING; + break; + // Angle of Arrival + case F1AP_MEASURED_RESULTS_VALUE_PR_UL_ANGLEOFARRIVAL: + f1_measuredResultsValue->present = F1AP_MeasuredResultsValue_PR_uL_AngleOfArrival; + asn1cCalloc(f1_measuredResultsValue->choice.uL_AngleOfArrival, f1_uL_AngleOfArrival); + f1ap_ul_aoa_t *uL_AngleOfArrival = &measuredResultsValue->choice.ul_angle_of_arrival; + f1_uL_AngleOfArrival->azimuthAoA = uL_AngleOfArrival->azimuth_aoa; + if (uL_AngleOfArrival->zenith_aoa) { + asn1cCalloc(f1_uL_AngleOfArrival->zenithAoA, f1_zenithAoA); + *f1_zenithAoA = *uL_AngleOfArrival->zenith_aoa; + } + if (uL_AngleOfArrival->lcs_to_gcs_translation_aoa) { + asn1cCalloc(f1_uL_AngleOfArrival->lCS_to_GCS_TranslationAoA, f1_lCS_to_GCS_TranslationAoA); + f1ap_lcs_to_gcs_translationaoa_t *lCS_to_GCS_TranslationAoA = uL_AngleOfArrival->lcs_to_gcs_translation_aoa; + + f1_lCS_to_GCS_TranslationAoA->alpha = lCS_to_GCS_TranslationAoA->alpha; + f1_lCS_to_GCS_TranslationAoA->beta = lCS_to_GCS_TranslationAoA->beta; + f1_lCS_to_GCS_TranslationAoA->gamma = lCS_to_GCS_TranslationAoA->gamma; + } + break; + // UL SRS RSRP + case F1AP_MEASURED_RESULTS_VALUE_PR_UL_SRS_RSRP: + f1_measuredResultsValue->present = F1AP_MeasuredResultsValue_PR_uL_SRS_RSRP; + f1_measuredResultsValue->choice.uL_SRS_RSRP = measuredResultsValue->choice.ul_srs_rsrp; + break; + // UL RTOA + case F1AP_MEASURED_RESULTS_VALUE_PR_UL_RTOA: + f1_measuredResultsValue->present = F1AP_MeasuredResultsValue_PR_uL_RTOA; + asn1cCalloc(f1_measuredResultsValue->choice.uL_RTOA, f1_uL_RTOA); + f1ap_ul_rtoa_measurement_t *uL_RTOA = &measuredResultsValue->choice.ul_rtoa; + f1ap_ul_rtoa_measurement_item_t *ul_rtoa_meas_item = &uL_RTOA->ul_rtoa_measurement_item; + F1AP_UL_RTOA_MeasurementItem_t *f1_ul_rtoa_meas_item = &f1_uL_RTOA->uL_RTOA_MeasurementItem; + + switch (ul_rtoa_meas_item->present) { + case F1AP_ULRTOAMEAS_PR_NOTHING: + f1_ul_rtoa_meas_item->present = F1AP_UL_RTOA_MeasurementItem_PR_NOTHING; + break; + case F1AP_ULRTOAMEAS_PR_K0: + f1_ul_rtoa_meas_item->present = F1AP_UL_RTOA_MeasurementItem_PR_k0; + f1_ul_rtoa_meas_item->choice.k0 = ul_rtoa_meas_item->choice.k0; + break; + case F1AP_ULRTOAMEAS_PR_K1: + f1_ul_rtoa_meas_item->present = F1AP_UL_RTOA_MeasurementItem_PR_k1; + f1_ul_rtoa_meas_item->choice.k1 = ul_rtoa_meas_item->choice.k1; + break; + case F1AP_ULRTOAMEAS_PR_K2: + f1_ul_rtoa_meas_item->present = F1AP_UL_RTOA_MeasurementItem_PR_k2; + f1_ul_rtoa_meas_item->choice.k2 = ul_rtoa_meas_item->choice.k2; + break; + case F1AP_ULRTOAMEAS_PR_K3: + f1_ul_rtoa_meas_item->present = F1AP_UL_RTOA_MeasurementItem_PR_k3; + f1_ul_rtoa_meas_item->choice.k3 = ul_rtoa_meas_item->choice.k3; + break; + case F1AP_ULRTOAMEAS_PR_K4: + f1_ul_rtoa_meas_item->present = F1AP_UL_RTOA_MeasurementItem_PR_k4; + f1_ul_rtoa_meas_item->choice.k4 = ul_rtoa_meas_item->choice.k4; + break; + case F1AP_ULRTOAMEAS_PR_K5: + f1_ul_rtoa_meas_item->present = F1AP_UL_RTOA_MeasurementItem_PR_k5; + f1_ul_rtoa_meas_item->choice.k5 = ul_rtoa_meas_item->choice.k5; + break; + default: + AssertFatal(false, "Illegal uL_RTOA_MeasurementItem %d\n", ul_rtoa_meas_item->present); + break; + } + break; + // gNB RX-TX Time Diff + case F1AP_MEASURED_RESULTS_VALUE_PR_GNB_RXTXTIMEDIFF: + f1_measuredResultsValue->present = F1AP_MeasuredResultsValue_PR_gNB_RxTxTimeDiff; + asn1cCalloc(f1_measuredResultsValue->choice.gNB_RxTxTimeDiff, f1_gNB_RxTxTimeDiff); + + f1ap_gnb_rx_tx_time_diff_t *gNB_RxTxTimeDiff = &measuredResultsValue->choice.gnb_rx_tx_time_diff; + f1ap_gnb_rx_tx_time_diff_meas_t *rx_tx_time_diff = &gNB_RxTxTimeDiff->rx_tx_time_diff; + F1AP_GNBRxTxTimeDiffMeas_t *f1_rxTxTimeDiff = &f1_gNB_RxTxTimeDiff->rxTxTimeDiff; + + switch (rx_tx_time_diff->present) { + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_NOTHING: + f1_rxTxTimeDiff->present = F1AP_GNBRxTxTimeDiffMeas_PR_NOTHING; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K0: + f1_rxTxTimeDiff->present = F1AP_GNBRxTxTimeDiffMeas_PR_k0; + f1_rxTxTimeDiff->choice.k0 = rx_tx_time_diff->choice.k0; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K1: + f1_rxTxTimeDiff->present = F1AP_GNBRxTxTimeDiffMeas_PR_k1; + f1_rxTxTimeDiff->choice.k1 = rx_tx_time_diff->choice.k1; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K2: + f1_rxTxTimeDiff->present = F1AP_GNBRxTxTimeDiffMeas_PR_k2; + f1_rxTxTimeDiff->choice.k2 = rx_tx_time_diff->choice.k2; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K3: + f1_rxTxTimeDiff->present = F1AP_GNBRxTxTimeDiffMeas_PR_k3; + f1_rxTxTimeDiff->choice.k3 = rx_tx_time_diff->choice.k3; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K4: + f1_rxTxTimeDiff->present = F1AP_GNBRxTxTimeDiffMeas_PR_k4; + f1_rxTxTimeDiff->choice.k4 = rx_tx_time_diff->choice.k4; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K5: + f1_rxTxTimeDiff->present = F1AP_GNBRxTxTimeDiffMeas_PR_k5; + f1_rxTxTimeDiff->choice.k5 = rx_tx_time_diff->choice.k5; + break; + default: + AssertFatal(false, "Illegal rxTxTimeDiff value %d\n", rx_tx_time_diff->present); + break; + } + break; + default: + AssertFatal(false, "Illegal measuredResultsValue %d\n", measuredResultsValue->present); + break; + } + + // timeStamp + F1AP_TimeStamp_t *f1_timeStamp = &f1_pos_measurement_result_item->timeStamp; + f1ap_time_stamp_t *timeStamp = &pos_measurement_result_item->time_stamp; + f1_timeStamp->systemFrameNumber = timeStamp->system_frame_number; + F1AP_TimeStampSlotIndex_t *f1_slotIndex = &f1_timeStamp->slotIndex; + f1ap_time_stamp_slot_index_t *slot_index = &timeStamp->slot_index; + switch (slot_index->present) { + case F1AP_TIME_STAMP_SLOT_INDEX_PR_NOTHING: + f1_slotIndex->present = F1AP_TimeStampSlotIndex_PR_NOTHING; + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_15: + f1_slotIndex->present = F1AP_TimeStampSlotIndex_PR_sCS_15; + f1_slotIndex->choice.sCS_15 = slot_index->choice.scs_15; + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_30: + f1_slotIndex->present = F1AP_TimeStampSlotIndex_PR_sCS_30; + f1_slotIndex->choice.sCS_30 = slot_index->choice.scs_30; + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_60: + f1_slotIndex->present = F1AP_TimeStampSlotIndex_PR_sCS_60; + f1_slotIndex->choice.sCS_60 = slot_index->choice.scs_60; + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_120: + f1_slotIndex->present = F1AP_TimeStampSlotIndex_PR_sCS_120; + f1_slotIndex->choice.sCS_120 = slot_index->choice.scs_120; + break; + default: + AssertFatal(false, "Illegal slotIndex value %d\n", slot_index->present); + break; + } + } + return f1_posMeasurementResult; +} + +static bool decode_positioning_measurement_result(F1AP_PosMeasurementResult_t *f1_posMeasurementResult, + f1ap_pos_measurement_result_t *posMeasurementResult) +{ + uint32_t pos_meas_result_length = f1_posMeasurementResult->list.count; + 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)); + for (int i = 0; i < pos_meas_result_length; i++) { + F1AP_PosMeasurementResultItem_t *f1_pos_measurement_result_item = f1_posMeasurementResult->list.array[i]; + f1ap_pos_measurement_result_item_t *pos_measurement_result_item = &posMeasurementResult->pos_measurement_result_item[i]; + + // measuredResultsValue + F1AP_MeasuredResultsValue_t *f1_measuredResultsValue = &f1_pos_measurement_result_item->measuredResultsValue; + f1ap_measured_results_value_t *measuredResultsValue = &pos_measurement_result_item->measured_results_value; + + switch (f1_measuredResultsValue->present) { + case F1AP_MeasuredResultsValue_PR_NOTHING: + measuredResultsValue->present = F1AP_MEASURED_RESULTS_VALUE_PR_NOTHING; + break; + // Angle of Arrival + case F1AP_MeasuredResultsValue_PR_uL_AngleOfArrival: + measuredResultsValue->present = F1AP_MEASURED_RESULTS_VALUE_PR_UL_ANGLEOFARRIVAL; + + F1AP_UL_AoA_t *f1_uL_AngleOfArrival = f1_measuredResultsValue->choice.uL_AngleOfArrival; + f1ap_ul_aoa_t *uL_AngleOfArrival = &measuredResultsValue->choice.ul_angle_of_arrival; + + uL_AngleOfArrival->azimuth_aoa = f1_uL_AngleOfArrival->azimuthAoA; + if (f1_uL_AngleOfArrival->zenithAoA) { + uL_AngleOfArrival->zenith_aoa = calloc_or_fail(1, sizeof(*uL_AngleOfArrival->zenith_aoa)); + *uL_AngleOfArrival->zenith_aoa = *f1_uL_AngleOfArrival->zenithAoA; + } + if (f1_uL_AngleOfArrival->lCS_to_GCS_TranslationAoA) { + uL_AngleOfArrival->lcs_to_gcs_translation_aoa = calloc_or_fail(1, sizeof(*uL_AngleOfArrival->lcs_to_gcs_translation_aoa)); + f1ap_lcs_to_gcs_translationaoa_t *lCS_to_GCS_TranslationAoA = uL_AngleOfArrival->lcs_to_gcs_translation_aoa; + F1AP_LCS_to_GCS_TranslationAoA_t *f1_lCS_to_GCS_TranslationAoA = f1_uL_AngleOfArrival->lCS_to_GCS_TranslationAoA; + + lCS_to_GCS_TranslationAoA->alpha = f1_lCS_to_GCS_TranslationAoA->alpha; + lCS_to_GCS_TranslationAoA->beta = f1_lCS_to_GCS_TranslationAoA->beta; + lCS_to_GCS_TranslationAoA->gamma = f1_lCS_to_GCS_TranslationAoA->gamma; + } + break; + // UL SRS RSRP + case F1AP_MeasuredResultsValue_PR_uL_SRS_RSRP: + measuredResultsValue->present = F1AP_MEASURED_RESULTS_VALUE_PR_UL_SRS_RSRP; + measuredResultsValue->choice.ul_srs_rsrp = f1_measuredResultsValue->choice.uL_SRS_RSRP; + break; + // UL RTOA + case F1AP_MeasuredResultsValue_PR_uL_RTOA: + measuredResultsValue->present = F1AP_MEASURED_RESULTS_VALUE_PR_UL_RTOA; + F1AP_UL_RTOA_Measurement_t *f1_uL_RTOA = f1_measuredResultsValue->choice.uL_RTOA; + f1ap_ul_rtoa_measurement_t *uL_RTOA = &measuredResultsValue->choice.ul_rtoa; + F1AP_UL_RTOA_MeasurementItem_t *f1_ul_rtoa_meas_item = &f1_uL_RTOA->uL_RTOA_MeasurementItem; + f1ap_ul_rtoa_measurement_item_t *ul_rtoa_meas_item = &uL_RTOA->ul_rtoa_measurement_item; + + switch (f1_ul_rtoa_meas_item->present) { + case F1AP_UL_RTOA_MeasurementItem_PR_NOTHING: + ul_rtoa_meas_item->present = F1AP_ULRTOAMEAS_PR_NOTHING; + break; + case F1AP_UL_RTOA_MeasurementItem_PR_k0: + ul_rtoa_meas_item->present = F1AP_ULRTOAMEAS_PR_K0; + ul_rtoa_meas_item->choice.k0 = f1_ul_rtoa_meas_item->choice.k0; + break; + case F1AP_UL_RTOA_MeasurementItem_PR_k1: + ul_rtoa_meas_item->present = F1AP_ULRTOAMEAS_PR_K1; + ul_rtoa_meas_item->choice.k1 = f1_ul_rtoa_meas_item->choice.k1; + break; + case F1AP_UL_RTOA_MeasurementItem_PR_k2: + ul_rtoa_meas_item->present = F1AP_ULRTOAMEAS_PR_K2; + ul_rtoa_meas_item->choice.k2 = f1_ul_rtoa_meas_item->choice.k2; + break; + case F1AP_UL_RTOA_MeasurementItem_PR_k3: + ul_rtoa_meas_item->present = F1AP_ULRTOAMEAS_PR_K3; + ul_rtoa_meas_item->choice.k3 = f1_ul_rtoa_meas_item->choice.k3; + break; + case F1AP_UL_RTOA_MeasurementItem_PR_k4: + ul_rtoa_meas_item->present = F1AP_ULRTOAMEAS_PR_K4; + ul_rtoa_meas_item->choice.k4 = f1_ul_rtoa_meas_item->choice.k4; + break; + case F1AP_UL_RTOA_MeasurementItem_PR_k5: + ul_rtoa_meas_item->present = F1AP_ULRTOAMEAS_PR_K5; + ul_rtoa_meas_item->choice.k5 = f1_ul_rtoa_meas_item->choice.k5; + break; + default: + AssertError(false, return false, "Illegal uL_RTOA_MeasurementItem %d\n", f1_ul_rtoa_meas_item->present); + break; + } + break; + // gNB RX-TX Time Diff + case F1AP_MeasuredResultsValue_PR_gNB_RxTxTimeDiff: + measuredResultsValue->present = F1AP_MEASURED_RESULTS_VALUE_PR_GNB_RXTXTIMEDIFF; + F1AP_GNB_RxTxTimeDiff_t *f1_gNB_RxTxTimeDiff = f1_measuredResultsValue->choice.gNB_RxTxTimeDiff; + f1ap_gnb_rx_tx_time_diff_t *gNB_RxTxTimeDiff = &measuredResultsValue->choice.gnb_rx_tx_time_diff; + F1AP_GNBRxTxTimeDiffMeas_t *f1_rxTxTimeDiff = &f1_gNB_RxTxTimeDiff->rxTxTimeDiff; + f1ap_gnb_rx_tx_time_diff_meas_t *rx_tx_time_diff = &gNB_RxTxTimeDiff->rx_tx_time_diff; + + switch (f1_rxTxTimeDiff->present) { + case F1AP_GNBRxTxTimeDiffMeas_PR_NOTHING: + rx_tx_time_diff->present = F1AP_GNBRXTXTIMEDIFFMEAS_PR_NOTHING; + break; + case F1AP_GNBRxTxTimeDiffMeas_PR_k0: + rx_tx_time_diff->present = F1AP_GNBRXTXTIMEDIFFMEAS_PR_K0; + rx_tx_time_diff->choice.k0 = f1_rxTxTimeDiff->choice.k0; + break; + case F1AP_GNBRxTxTimeDiffMeas_PR_k1: + rx_tx_time_diff->present = F1AP_GNBRXTXTIMEDIFFMEAS_PR_K1; + rx_tx_time_diff->choice.k1 = f1_rxTxTimeDiff->choice.k1; + break; + case F1AP_GNBRxTxTimeDiffMeas_PR_k2: + rx_tx_time_diff->present = F1AP_GNBRXTXTIMEDIFFMEAS_PR_K2; + rx_tx_time_diff->choice.k2 = f1_rxTxTimeDiff->choice.k2; + break; + case F1AP_GNBRxTxTimeDiffMeas_PR_k3: + rx_tx_time_diff->present = F1AP_GNBRXTXTIMEDIFFMEAS_PR_K3; + rx_tx_time_diff->choice.k3 = f1_rxTxTimeDiff->choice.k3; + break; + case F1AP_GNBRxTxTimeDiffMeas_PR_k4: + rx_tx_time_diff->present = F1AP_GNBRXTXTIMEDIFFMEAS_PR_K4; + rx_tx_time_diff->choice.k4 = f1_rxTxTimeDiff->choice.k4; + break; + case F1AP_GNBRxTxTimeDiffMeas_PR_k5: + rx_tx_time_diff->present = F1AP_GNBRXTXTIMEDIFFMEAS_PR_K5; + rx_tx_time_diff->choice.k5 = f1_rxTxTimeDiff->choice.k5; + break; + default: + AssertError(false, return false, "Illegal rxTxTimeDiff value %d\n", f1_rxTxTimeDiff->present); + break; + } + break; + default: + AssertError(false, return false, "Illegal measuredResultsValue %d\n", f1_measuredResultsValue->present); + break; + } + + // timeStamp + F1AP_TimeStamp_t *f1_timeStamp = &f1_pos_measurement_result_item->timeStamp; + f1ap_time_stamp_t *timeStamp = &pos_measurement_result_item->time_stamp; + timeStamp->system_frame_number = f1_timeStamp->systemFrameNumber; + F1AP_TimeStampSlotIndex_t *f1_slotIndex = &f1_timeStamp->slotIndex; + f1ap_time_stamp_slot_index_t *slot_index = &timeStamp->slot_index; + switch (f1_slotIndex->present) { + case F1AP_TimeStampSlotIndex_PR_NOTHING: + slot_index->present = F1AP_TIME_STAMP_SLOT_INDEX_PR_NOTHING; + break; + case F1AP_TimeStampSlotIndex_PR_sCS_15: + slot_index->present = F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_15; + slot_index->choice.scs_15 = f1_slotIndex->choice.sCS_15; + break; + case F1AP_TimeStampSlotIndex_PR_sCS_30: + slot_index->present = F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_30; + slot_index->choice.scs_30 = f1_slotIndex->choice.sCS_30; + break; + case F1AP_TimeStampSlotIndex_PR_sCS_60: + slot_index->present = F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_60; + slot_index->choice.scs_60 = f1_slotIndex->choice.sCS_60; + break; + case F1AP_TimeStampSlotIndex_PR_sCS_120: + slot_index->present = F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_120; + slot_index->choice.scs_120 = f1_slotIndex->choice.sCS_120; + break; + default: + AssertError(false, return false, "Illegal slotIndex value %d\n", f1_slotIndex->present); + break; + } + } + return true; +} + +static f1ap_pos_measurement_result_t cp_positioning_measurement_result(const f1ap_pos_measurement_result_t *in) +{ + f1ap_pos_measurement_result_t out = {0}; + uint32_t pos_meas_result_length = in->pos_measurement_result_item_length; + out.pos_measurement_result_item_length = pos_meas_result_length; + out.pos_measurement_result_item = calloc_or_fail(pos_meas_result_length, sizeof(*out.pos_measurement_result_item)); + for (int i = 0; i < pos_meas_result_length; i++) { + f1ap_pos_measurement_result_item_t *f1_pos_measurement_result_item = &in->pos_measurement_result_item[i]; + f1ap_pos_measurement_result_item_t *pos_measurement_result_item = &out.pos_measurement_result_item[i]; + + // measuredResultsValue + f1ap_measured_results_value_t *f1_measuredResultsValue = &f1_pos_measurement_result_item->measured_results_value; + f1ap_measured_results_value_t *measuredResultsValue = &pos_measurement_result_item->measured_results_value; + + measuredResultsValue->present = f1_measuredResultsValue->present; + switch (f1_measuredResultsValue->present) { + case F1AP_MEASURED_RESULTS_VALUE_PR_NOTHING: + // nothing to copy + break; + // Angle of Arrival + case F1AP_MEASURED_RESULTS_VALUE_PR_UL_ANGLEOFARRIVAL: { + f1ap_ul_aoa_t *f1_uL_AngleOfArrival = &f1_measuredResultsValue->choice.ul_angle_of_arrival; + f1ap_ul_aoa_t *uL_AngleOfArrival = &measuredResultsValue->choice.ul_angle_of_arrival; + + uL_AngleOfArrival->azimuth_aoa = f1_uL_AngleOfArrival->azimuth_aoa; + if (f1_uL_AngleOfArrival->zenith_aoa) { + uL_AngleOfArrival->zenith_aoa = calloc_or_fail(1, sizeof(*uL_AngleOfArrival->zenith_aoa)); + *uL_AngleOfArrival->zenith_aoa = *f1_uL_AngleOfArrival->zenith_aoa; + } + if (f1_uL_AngleOfArrival->lcs_to_gcs_translation_aoa) { + uL_AngleOfArrival->lcs_to_gcs_translation_aoa = calloc_or_fail(1, sizeof(*uL_AngleOfArrival->lcs_to_gcs_translation_aoa)); + + f1ap_lcs_to_gcs_translationaoa_t *lCS_to_GCS_TranslationAoA = uL_AngleOfArrival->lcs_to_gcs_translation_aoa; + f1ap_lcs_to_gcs_translationaoa_t *f1_lCS_to_GCS_TranslationAoA = f1_uL_AngleOfArrival->lcs_to_gcs_translation_aoa; + + lCS_to_GCS_TranslationAoA->alpha = f1_lCS_to_GCS_TranslationAoA->alpha; + lCS_to_GCS_TranslationAoA->beta = f1_lCS_to_GCS_TranslationAoA->beta; + lCS_to_GCS_TranslationAoA->gamma = f1_lCS_to_GCS_TranslationAoA->gamma; + } + break; + } + // UL SRS RSRP + case F1AP_MEASURED_RESULTS_VALUE_PR_UL_SRS_RSRP: + measuredResultsValue->choice.ul_srs_rsrp = f1_measuredResultsValue->choice.ul_srs_rsrp; + break; + // UL RTOA + case F1AP_MEASURED_RESULTS_VALUE_PR_UL_RTOA: { + f1ap_ul_rtoa_measurement_t *f1_uL_RTOA = &f1_measuredResultsValue->choice.ul_rtoa; + f1ap_ul_rtoa_measurement_t *uL_RTOA = &measuredResultsValue->choice.ul_rtoa; + f1ap_ul_rtoa_measurement_item_t *f1_ul_rtoa_meas_item = &f1_uL_RTOA->ul_rtoa_measurement_item; + f1ap_ul_rtoa_measurement_item_t *ul_rtoa_meas_item = &uL_RTOA->ul_rtoa_measurement_item; + + switch (f1_ul_rtoa_meas_item->present) { + case F1AP_ULRTOAMEAS_PR_NOTHING: + break; + case F1AP_ULRTOAMEAS_PR_K0: + ul_rtoa_meas_item->choice.k0 = f1_ul_rtoa_meas_item->choice.k0; + break; + case F1AP_ULRTOAMEAS_PR_K1: + ul_rtoa_meas_item->choice.k1 = f1_ul_rtoa_meas_item->choice.k1; + break; + case F1AP_ULRTOAMEAS_PR_K2: + ul_rtoa_meas_item->choice.k2 = f1_ul_rtoa_meas_item->choice.k2; + break; + case F1AP_ULRTOAMEAS_PR_K3: + ul_rtoa_meas_item->choice.k3 = f1_ul_rtoa_meas_item->choice.k3; + break; + case F1AP_ULRTOAMEAS_PR_K4: + ul_rtoa_meas_item->choice.k4 = f1_ul_rtoa_meas_item->choice.k4; + break; + case F1AP_ULRTOAMEAS_PR_K5: + ul_rtoa_meas_item->choice.k5 = f1_ul_rtoa_meas_item->choice.k5; + break; + default: + AssertFatal(false, "Illegal uL_RTOA_MeasurementItem %d\n", f1_uL_RTOA->ul_rtoa_measurement_item.present); + break; + } + break; + } + // gNB RX-TX Time Diff + case F1AP_MEASURED_RESULTS_VALUE_PR_GNB_RXTXTIMEDIFF: { + f1ap_gnb_rx_tx_time_diff_t *f1_gNB_RxTxTimeDiff = &f1_measuredResultsValue->choice.gnb_rx_tx_time_diff; + f1ap_gnb_rx_tx_time_diff_t *gNB_RxTxTimeDiff = &measuredResultsValue->choice.gnb_rx_tx_time_diff; + f1_gNB_RxTxTimeDiff->rx_tx_time_diff.present = gNB_RxTxTimeDiff->rx_tx_time_diff.present; + + f1ap_gnb_rx_tx_time_diff_meas_t *f1_rx_tx_time_diff = &f1_gNB_RxTxTimeDiff->rx_tx_time_diff; + f1ap_gnb_rx_tx_time_diff_meas_t *rx_tx_time_diff = &gNB_RxTxTimeDiff->rx_tx_time_diff; + + switch (f1_rx_tx_time_diff->present) { + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_NOTHING: + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K0: + rx_tx_time_diff->choice.k0 = f1_rx_tx_time_diff->choice.k0; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K1: + rx_tx_time_diff->choice.k1 = f1_rx_tx_time_diff->choice.k1; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K2: + rx_tx_time_diff->choice.k2 = f1_rx_tx_time_diff->choice.k2; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K3: + rx_tx_time_diff->choice.k3 = f1_rx_tx_time_diff->choice.k3; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K4: + rx_tx_time_diff->choice.k4 = f1_rx_tx_time_diff->choice.k4; + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K5: + rx_tx_time_diff->choice.k5 = f1_rx_tx_time_diff->choice.k5; + break; + default: + AssertFatal(false, "Illegal rxTxTimeDiff value %d\n", f1_rx_tx_time_diff->present); + break; + } + break; + } + default: + AssertFatal(false, "Illegal measuredResultsValue %d\n", f1_measuredResultsValue->present); + break; + } + + // timeStamp + f1ap_time_stamp_t *f1_timeStamp = &f1_pos_measurement_result_item->time_stamp; + f1ap_time_stamp_t *timeStamp = &pos_measurement_result_item->time_stamp; + timeStamp->system_frame_number = f1_timeStamp->system_frame_number; + f1ap_time_stamp_slot_index_t *f1_slot_index = &f1_timeStamp->slot_index; + f1ap_time_stamp_slot_index_t *slot_index = &timeStamp->slot_index; + switch (f1_slot_index->present) { + case F1AP_TIME_STAMP_SLOT_INDEX_PR_NOTHING: + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_15: + slot_index->choice.scs_15 = f1_slot_index->choice.scs_15; + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_30: + slot_index->choice.scs_30 = f1_slot_index->choice.scs_30; + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_60: + slot_index->choice.scs_60 = f1_slot_index->choice.scs_60; + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_120: + slot_index->choice.scs_120 = f1_slot_index->choice.scs_120; + break; + default: + AssertFatal(false, "Illegal slotIndex value %d\n", f1_slot_index->present); + break; + } + } + return out; +} + +static bool eq_positioning_measurement_result(const f1ap_pos_measurement_result_t *f1_posMeasurementResult, + const f1ap_pos_measurement_result_t *posMeasurementResult) +{ + uint32_t pos_meas_result_length = f1_posMeasurementResult->pos_measurement_result_item_length; + _F1_EQ_CHECK_INT(posMeasurementResult->pos_measurement_result_item_length, pos_meas_result_length); + for (int i = 0; i < pos_meas_result_length; i++) { + f1ap_pos_measurement_result_item_t *f1_pos_measurement_result_item = &f1_posMeasurementResult->pos_measurement_result_item[i]; + f1ap_pos_measurement_result_item_t *pos_measurement_result_item = &posMeasurementResult->pos_measurement_result_item[i]; + + // measuredResultsValue + f1ap_measured_results_value_t *f1_measuredResultsValue = &f1_pos_measurement_result_item->measured_results_value; + f1ap_measured_results_value_t *measuredResultsValue = &pos_measurement_result_item->measured_results_value; + + _F1_EQ_CHECK_INT(measuredResultsValue->present, f1_measuredResultsValue->present); + switch (measuredResultsValue->present) { + case F1AP_MEASURED_RESULTS_VALUE_PR_NOTHING: + // nothing to check + break; + // Angle of Arrival + case F1AP_MEASURED_RESULTS_VALUE_PR_UL_ANGLEOFARRIVAL: { + f1ap_ul_aoa_t *f1_uL_AngleOfArrival = &f1_measuredResultsValue->choice.ul_angle_of_arrival; + f1ap_ul_aoa_t *uL_AngleOfArrival = &measuredResultsValue->choice.ul_angle_of_arrival; + + _F1_EQ_CHECK_INT(uL_AngleOfArrival->azimuth_aoa, f1_uL_AngleOfArrival->azimuth_aoa); + if (f1_uL_AngleOfArrival->zenith_aoa) { + _F1_EQ_CHECK_INT(*uL_AngleOfArrival->zenith_aoa, *f1_uL_AngleOfArrival->zenith_aoa); + } + if (f1_uL_AngleOfArrival->lcs_to_gcs_translation_aoa) { + f1ap_lcs_to_gcs_translationaoa_t *lCS_to_GCS_TranslationAoA = uL_AngleOfArrival->lcs_to_gcs_translation_aoa; + f1ap_lcs_to_gcs_translationaoa_t *f1_lCS_to_GCS_TranslationAoA = f1_uL_AngleOfArrival->lcs_to_gcs_translation_aoa; + + _F1_EQ_CHECK_INT(lCS_to_GCS_TranslationAoA->alpha, f1_lCS_to_GCS_TranslationAoA->alpha); + _F1_EQ_CHECK_INT(lCS_to_GCS_TranslationAoA->beta, f1_lCS_to_GCS_TranslationAoA->beta); + _F1_EQ_CHECK_INT(lCS_to_GCS_TranslationAoA->gamma, f1_lCS_to_GCS_TranslationAoA->gamma); + } + break; + } + // UL SRS RSRP + case F1AP_MEASURED_RESULTS_VALUE_PR_UL_SRS_RSRP: + _F1_EQ_CHECK_INT(measuredResultsValue->choice.ul_srs_rsrp, f1_measuredResultsValue->choice.ul_srs_rsrp); + break; + // UL RTOA + case F1AP_MEASURED_RESULTS_VALUE_PR_UL_RTOA: { + f1ap_ul_rtoa_measurement_t *f1_uL_RTOA = &f1_measuredResultsValue->choice.ul_rtoa; + f1ap_ul_rtoa_measurement_t *uL_RTOA = &measuredResultsValue->choice.ul_rtoa; + + f1ap_ul_rtoa_measurement_item_t *f1_ul_rtoa_meas_item = &f1_uL_RTOA->ul_rtoa_measurement_item; + f1ap_ul_rtoa_measurement_item_t *ul_rtoa_meas_item = &uL_RTOA->ul_rtoa_measurement_item; + + switch (f1_ul_rtoa_meas_item->present) { + case F1AP_ULRTOAMEAS_PR_NOTHING: + break; + case F1AP_ULRTOAMEAS_PR_K0: + _F1_EQ_CHECK_INT(ul_rtoa_meas_item->choice.k0, f1_ul_rtoa_meas_item->choice.k0); + break; + case F1AP_ULRTOAMEAS_PR_K1: + _F1_EQ_CHECK_INT(ul_rtoa_meas_item->choice.k1, f1_ul_rtoa_meas_item->choice.k1); + break; + case F1AP_ULRTOAMEAS_PR_K2: + _F1_EQ_CHECK_INT(ul_rtoa_meas_item->choice.k2, f1_ul_rtoa_meas_item->choice.k2); + break; + case F1AP_ULRTOAMEAS_PR_K3: + _F1_EQ_CHECK_INT(ul_rtoa_meas_item->choice.k3, f1_ul_rtoa_meas_item->choice.k3); + break; + case F1AP_ULRTOAMEAS_PR_K4: + _F1_EQ_CHECK_INT(ul_rtoa_meas_item->choice.k4, f1_ul_rtoa_meas_item->choice.k4); + break; + case F1AP_ULRTOAMEAS_PR_K5: + _F1_EQ_CHECK_INT(ul_rtoa_meas_item->choice.k5, f1_ul_rtoa_meas_item->choice.k5); + break; + default: + AssertError(false, return false, "Illegal uL_RTOA_MeasurementItem %d\n", f1_ul_rtoa_meas_item->present); + break; + } + break; + } + // gNB RX-TX Time Diff + case F1AP_MEASURED_RESULTS_VALUE_PR_GNB_RXTXTIMEDIFF: { + f1ap_gnb_rx_tx_time_diff_t *f1_gNB_RxTxTimeDiff = &f1_measuredResultsValue->choice.gnb_rx_tx_time_diff; + f1ap_gnb_rx_tx_time_diff_t *gNB_RxTxTimeDiff = &measuredResultsValue->choice.gnb_rx_tx_time_diff; + f1ap_gnb_rx_tx_time_diff_meas_t *f1_rx_tx_time_diff = &f1_gNB_RxTxTimeDiff->rx_tx_time_diff; + f1ap_gnb_rx_tx_time_diff_meas_t *rx_tx_time_diff = &gNB_RxTxTimeDiff->rx_tx_time_diff; + _F1_EQ_CHECK_INT(f1_rx_tx_time_diff->present, rx_tx_time_diff->present); + switch (f1_rx_tx_time_diff->present) { + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_NOTHING: + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K0: + _F1_EQ_CHECK_INT(rx_tx_time_diff->choice.k0, f1_rx_tx_time_diff->choice.k0); + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K1: + _F1_EQ_CHECK_INT(rx_tx_time_diff->choice.k1, f1_rx_tx_time_diff->choice.k1); + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K2: + _F1_EQ_CHECK_INT(rx_tx_time_diff->choice.k2, f1_rx_tx_time_diff->choice.k2); + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K3: + _F1_EQ_CHECK_INT(rx_tx_time_diff->choice.k3, f1_rx_tx_time_diff->choice.k3); + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K4: + _F1_EQ_CHECK_INT(rx_tx_time_diff->choice.k4, f1_rx_tx_time_diff->choice.k4); + break; + case F1AP_GNBRXTXTIMEDIFFMEAS_PR_K5: + _F1_EQ_CHECK_INT(rx_tx_time_diff->choice.k5, f1_rx_tx_time_diff->choice.k5); + break; + default: + AssertError(false, return false, "Illegal rxTxTimeDiff value %d\n", f1_rx_tx_time_diff->present); + break; + } + break; + } + default: + AssertError(false, return false, "Illegal measuredResultsValue %d\n", f1_measuredResultsValue->present); + break; + } + + // timeStamp + f1ap_time_stamp_t *f1_timeStamp = &f1_pos_measurement_result_item->time_stamp; + f1ap_time_stamp_t *timeStamp = &pos_measurement_result_item->time_stamp; + _F1_EQ_CHECK_INT(timeStamp->system_frame_number, f1_timeStamp->system_frame_number); + f1ap_time_stamp_slot_index_t *f1_slot_index = &f1_timeStamp->slot_index; + f1ap_time_stamp_slot_index_t *slot_index = &timeStamp->slot_index; + + switch (f1_slot_index->present) { + case F1AP_TIME_STAMP_SLOT_INDEX_PR_NOTHING: + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_15: + _F1_EQ_CHECK_INT(slot_index->choice.scs_15, f1_slot_index->choice.scs_15); + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_30: + _F1_EQ_CHECK_INT(slot_index->choice.scs_30, f1_slot_index->choice.scs_30); + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_60: + _F1_EQ_CHECK_INT(slot_index->choice.scs_60, f1_slot_index->choice.scs_60); + break; + case F1AP_TIME_STAMP_SLOT_INDEX_PR_SCS_120: + _F1_EQ_CHECK_INT(slot_index->choice.scs_120, f1_slot_index->choice.scs_120); + break; + default: + AssertError(false, return false, "Illegal slotIndex value %d\n", f1_slot_index->present); + break; + } + } + return true; +} + /** * @brief Encode F1 positioning information request to ASN.1 */ @@ -4799,3 +5421,221 @@ void free_positioning_measurement_req(f1ap_positioning_measurement_req_t *msg) free(msg->srs_configuration); } } + +/** + * @brief Encode F1 positioning measurement response to ASN.1 + */ +F1AP_F1AP_PDU_t *encode_positioning_measurement_resp(const f1ap_positioning_measurement_resp_t *msg) +{ + F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + + /* Message Type */ + pdu->present = F1AP_F1AP_PDU_PR_successfulOutcome; + asn1cCalloc(pdu->choice.successfulOutcome, tmp); + tmp->procedureCode = F1AP_ProcedureCode_id_PositioningMeasurementExchange; + tmp->criticality = F1AP_Criticality_reject; + tmp->value.present = F1AP_SuccessfulOutcome__value_PR_PositioningMeasurementResponse; + F1AP_PositioningMeasurementResponse_t *out = &tmp->value.choice.PositioningMeasurementResponse; + + /* mandatory : TransactionID */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_PositioningMeasurementResponseIEs_t, ie1); + ie1->id = F1AP_ProtocolIE_ID_id_TransactionID; + ie1->criticality = F1AP_Criticality_reject; + ie1->value.present = F1AP_PositioningMeasurementResponseIEs__value_PR_TransactionID; + ie1->value.choice.TransactionID = msg->transaction_id; + + /* mandatory : LMF_MeasurementID */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_PositioningMeasurementResponseIEs_t, ie2); + ie2->id = F1AP_ProtocolIE_ID_id_LMF_MeasurementID; + ie2->criticality = F1AP_Criticality_reject; + ie2->value.present = F1AP_PositioningMeasurementResponseIEs__value_PR_LMF_MeasurementID; + ie2->value.choice.LMF_MeasurementID = msg->lmf_measurement_id; + + /* mandatory : RAN_MeasurementID */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_PositioningMeasurementResponseIEs_t, ie3); + ie3->id = F1AP_ProtocolIE_ID_id_RAN_MeasurementID; + ie3->criticality = F1AP_Criticality_reject; + ie3->value.present = F1AP_PositioningMeasurementResponseIEs__value_PR_RAN_MeasurementID; + ie3->value.choice.RAN_MeasurementID = msg->ran_measurement_id; + + /* Positioning Measurement Result List */ + if (msg->pos_measurement_result_list) { + asn1cSequenceAdd(out->protocolIEs.list, F1AP_PositioningMeasurementResponseIEs_t, ie4); + ie4->id = F1AP_ProtocolIE_ID_id_PosMeasurementResultList; + ie4->criticality = F1AP_Criticality_reject; + ie4->value.present = F1AP_PositioningMeasurementResponseIEs__value_PR_PosMeasurementResultList; + const f1ap_pos_measurement_result_list_t *in_list = msg->pos_measurement_result_list; + for (int i = 0; i < in_list->pos_measurement_result_list_length; i++) { + // mandatory : Positioning Measurement Result List Item + asn1cSequenceAdd(ie4->value.choice.PosMeasurementResultList.list, F1AP_PosMeasurementResultList_Item_t, out_item); + f1ap_pos_measurement_result_list_item_t *in_item = &in_list->pos_measurement_result_list_item[i]; + F1AP_PosMeasurementResult_t *out_result = &out_item->posMeasurementResult; + f1ap_pos_measurement_result_t *in_result = &in_item->pos_measurement_result; + *out_result = encode_positioning_measurement_result(in_result); + out_item->tRPID = in_item->trp_id; + } + } + + return pdu; +} + +/** + * @brief Decode F1 Positioning Measurement response + */ +bool decode_positioning_measurement_resp(const F1AP_F1AP_PDU_t *pdu, f1ap_positioning_measurement_resp_t *out) +{ + DevAssert(out != NULL); + memset(out, 0, sizeof(*out)); + + F1AP_PositioningMeasurementResponse_t *in = &pdu->choice.successfulOutcome->value.choice.PositioningMeasurementResponse; + F1AP_PositioningMeasurementResponseIEs_t *ie; + + F1AP_LIB_FIND_IE(F1AP_PositioningMeasurementResponseIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_TransactionID, true); + F1AP_LIB_FIND_IE(F1AP_PositioningMeasurementResponseIEs_t, + ie, + &in->protocolIEs.list, + F1AP_ProtocolIE_ID_id_LMF_MeasurementID, + true); + F1AP_LIB_FIND_IE(F1AP_PositioningMeasurementResponseIEs_t, + ie, + &in->protocolIEs.list, + F1AP_ProtocolIE_ID_id_RAN_MeasurementID, + true); + F1AP_LIB_FIND_IE(F1AP_PositioningMeasurementResponseIEs_t, + ie, + &in->protocolIEs.list, + F1AP_ProtocolIE_ID_id_PosMeasurementResultList, + false); + + 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_PositioningMeasurementResponseIEs__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_PositioningMeasurementResponseIEs__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_PositioningMeasurementResponseIEs__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_PositioningMeasurementResponseIEs__value_PR_PosMeasurementResultList); + F1AP_PosMeasurementResultList_t *in_list = &ie->value.choice.PosMeasurementResultList; + uint32_t pos_meas_result_list_len = in_list->list.count; + out->pos_measurement_result_list = calloc_or_fail(1, sizeof(*out->pos_measurement_result_list)); + f1ap_pos_measurement_result_list_t *out_list = out->pos_measurement_result_list; + out_list->pos_measurement_result_list_length = pos_meas_result_list_len; + out_list->pos_measurement_result_list_item = + calloc_or_fail(pos_meas_result_list_len, sizeof(*out_list->pos_measurement_result_list_item)); + for (int i = 0; i < pos_meas_result_list_len; i++) { + F1AP_PosMeasurementResultList_Item_t *in_item = in_list->list.array[i]; + f1ap_pos_measurement_result_list_item_t *out_item = &out_list->pos_measurement_result_list_item[i]; + _F1_CHECK_EXP(decode_positioning_measurement_result(&in_item->posMeasurementResult, &out_item->pos_measurement_result)); + out_item->trp_id = in_item->tRPID; + } + break; + case F1AP_ProtocolIE_ID_id_CriticalityDiagnostics: + PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld not handled, skipping\n", ie->id); + break; + default: + PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); + break; + } + } + + return true; +} + +/** + * @brief F1 Positioning Measurement response deep copy + */ +f1ap_positioning_measurement_resp_t cp_positioning_measurement_resp(const f1ap_positioning_measurement_resp_t *orig) +{ + // copy all mandatory fields that are not dynamic memory + f1ap_positioning_measurement_resp_t cp = { + .transaction_id = orig->transaction_id, + .lmf_measurement_id = orig->lmf_measurement_id, + .ran_measurement_id = orig->ran_measurement_id, + }; + + if (orig->pos_measurement_result_list) { + 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 response equality check + */ +bool eq_positioning_measurement_resp(const f1ap_positioning_measurement_resp_t *a, const f1ap_positioning_measurement_resp_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); + + if ((a->pos_measurement_result_list == NULL) != (b->pos_measurement_result_list == NULL)) { + return false; + } + if (a->pos_measurement_result_list) { + 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 response + */ +void free_positioning_measurement_resp(f1ap_positioning_measurement_resp_t *msg) +{ + if (msg->pos_measurement_result_list) { + 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 089066f899..aefe893a29 100644 --- a/openair2/F1AP/lib/f1ap_positioning.h +++ b/openair2/F1AP/lib/f1ap_positioning.h @@ -114,4 +114,10 @@ f1ap_positioning_measurement_req_t cp_positioning_measurement_req(const f1ap_pos bool eq_positioning_measurement_req(const f1ap_positioning_measurement_req_t *a, const f1ap_positioning_measurement_req_t *b); void free_positioning_measurement_req(f1ap_positioning_measurement_req_t *msg); +// positioning measurement response +struct F1AP_F1AP_PDU *encode_positioning_measurement_resp(const f1ap_positioning_measurement_resp_t *msg); +bool decode_positioning_measurement_resp(const struct F1AP_F1AP_PDU *pdu, f1ap_positioning_measurement_resp_t *out); +f1ap_positioning_measurement_resp_t cp_positioning_measurement_resp(const f1ap_positioning_measurement_resp_t *orig); +bool eq_positioning_measurement_resp(const f1ap_positioning_measurement_resp_t *a, const f1ap_positioning_measurement_resp_t *b); +void free_positioning_measurement_resp(f1ap_positioning_measurement_resp_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 28e351e25d..64f95b2967 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -2033,6 +2033,108 @@ static void test_f1ap_positioning_measurement_request() printf("%s() successful\n", __func__); } +static void test_f1ap_positioning_measurement_response() +{ + f1ap_positioning_measurement_resp_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_resp(&orig); + F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); + f1ap_msg_free(f1enc); + + f1ap_positioning_measurement_resp_t decoded = {0}; + bool ret = decode_positioning_measurement_resp(f1dec, &decoded); + AssertFatal(ret, "decode_positioning_measurement_resp(): could not decode message\n"); + f1ap_msg_free(f1dec); + + ret = eq_positioning_measurement_resp(&orig, &decoded); + AssertFatal(ret, "eq_positioning_measurement_resp(): decoded message doesn't match\n"); + free_positioning_measurement_resp(&decoded); + + f1ap_positioning_measurement_resp_t cp = cp_positioning_measurement_resp(&orig); + ret = eq_positioning_measurement_resp(&orig, &cp); + AssertFatal(ret, "eq_positioning_measurement_resp(): copied message doesn't match\n"); + free_positioning_measurement_resp(&orig); + free_positioning_measurement_resp(&cp); + + printf("%s() successful\n", __func__); +} + int main() { test_initial_ul_rrc_message_transfer(); @@ -2071,5 +2173,6 @@ int main() test_f1ap_trp_information_response(); test_f1ap_trp_information_failure(); test_f1ap_positioning_measurement_request(); + test_f1ap_positioning_measurement_response(); return 0; }