From af4a030997ed99792a1fa325596355a8d9346cbb Mon Sep 17 00:00:00 2001 From: Rakesh Mundlamuri Date: Mon, 22 Dec 2025 21:37:49 +0530 Subject: [PATCH] Add F1AP trp information request message enc/dec + test Reference: 3GPP 38.473 v16.21.0 Mandatory fields: Transcation ID (supported) TRP list (range : 0..1) > TRP list Item (range : 1..65535) >> TRP ID (supported) TRP Information Type List (range : 1) > TRP Information Type Item (range : 1..64) >> TRP Information Type Item (supported) --- openair2/COMMON/f1ap_messages_types.h | 35 +++++ openair2/F1AP/lib/f1ap_lib_includes.h | 1 + openair2/F1AP/lib/f1ap_positioning.c | 186 ++++++++++++++++++++++++++ openair2/F1AP/lib/f1ap_positioning.h | 7 + openair2/F1AP/tests/f1ap_lib_test.c | 44 ++++++ 5 files changed, 273 insertions(+) diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index 4ad12954f2..a86617c495 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -1047,6 +1047,31 @@ typedef struct f1ap_abort_transmission_s { f1ap_abort_transmission_u choice; } f1ap_abort_transmission_t; +typedef struct f1ap_trp_list_item_s { + uint32_t trp_id; +} f1ap_trp_list_item_t; + +typedef struct f1ap_trp_list_s { + f1ap_trp_list_item_t *trp_list_item; + uint32_t trp_list_length; +} f1ap_trp_list_t; + +typedef enum f1ap_trp_information_type_item_e { + F1AP_TRP_INFORMATION_TYPE_ITEM_NR_PCI, + F1AP_TRP_INFORMATION_TYPE_ITEM_NG_RAN_CGI, + F1AP_TRP_INFORMATION_TYPE_ITEM_NR_ARFCN, + F1AP_TRP_INFORMATION_TYPE_ITEM_PRS_CONFIG, + F1AP_TRP_INFORMATION_TYPE_ITEM_SSB_CONFIG, + F1AP_TRP_INFORMATION_TYPE_ITEM_SFN_INIT_TIME, + F1AP_TRP_INFORMATION_TYPE_ITEM_SPATIAL_DIRECTION_INFO, + F1AP_TRP_INFORMATION_TYPE_ITEM_GEO_COORDINATES +} f1ap_trp_information_type_item_pr; + +typedef struct f1ap_trp_information_type_list_s { + f1ap_trp_information_type_item_pr *trp_information_type_item; + uint8_t trp_information_type_list_length; +} f1ap_trp_information_type_list_t; + typedef struct f1ap_positioning_information_req_s { // IE 9.3.1.4 (mandatory) uint32_t gNB_CU_ue_id; @@ -1119,4 +1144,14 @@ typedef struct f1ap_positioning_information_update_s { f1ap_srs_configuration_t *srs_configuration; } f1ap_positioning_information_update_t; +typedef struct f1ap_trp_information_req_s { + // IE 9.3.1.23 (mandatory) + uint8_t transaction_id; + bool has_trp_list; + // mandatory + f1ap_trp_list_t trp_list; + // mandatory + f1ap_trp_information_type_list_t trp_information_type_list; +} f1ap_trp_information_req_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 fa24b65918..a3fe9ce0d9 100644 --- a/openair2/F1AP/lib/f1ap_lib_includes.h +++ b/openair2/F1AP/lib/f1ap_lib_includes.h @@ -73,5 +73,6 @@ #include "F1AP_PosResourceSetTypeAP.h" #include "F1AP_SemipersistentSRS.h" #include "F1AP_AperiodicSRS.h" +#include "F1AP_TRPListItem.h" #endif /* F1AP_LIB_INCLUDES_H_ */ diff --git a/openair2/F1AP/lib/f1ap_positioning.c b/openair2/F1AP/lib/f1ap_positioning.c index d58794fa34..3f85a50890 100644 --- a/openair2/F1AP/lib/f1ap_positioning.c +++ b/openair2/F1AP/lib/f1ap_positioning.c @@ -3190,3 +3190,189 @@ void free_positioning_information_update(f1ap_positioning_information_update_t * free(msg->srs_configuration); } } + +/** + * @brief Encode F1 TRP information request to ASN.1 + */ +F1AP_F1AP_PDU_t *encode_trp_information_req(const f1ap_trp_information_req_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_TRPInformationExchange; + tmp->criticality = F1AP_Criticality_reject; + tmp->value.present = F1AP_InitiatingMessage__value_PR_TRPInformationRequest; + F1AP_TRPInformationRequest_t *out = &tmp->value.choice.TRPInformationRequest; + + /* mandatory : TransactionID */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_TRPInformationRequestIEs_t, ie1); + ie1->id = F1AP_ProtocolIE_ID_id_TransactionID; + ie1->criticality = F1AP_Criticality_reject; + ie1->value.present = F1AP_TRPInformationRequestIEs__value_PR_TransactionID; + ie1->value.choice.TransactionID = msg->transaction_id; + + /* TRPList */ + if (msg->has_trp_list) { + asn1cSequenceAdd(out->protocolIEs.list, F1AP_TRPInformationRequestIEs_t, ie2); + ie2->id = F1AP_ProtocolIE_ID_id_TRPList; + ie2->criticality = F1AP_Criticality_ignore; + ie2->value.present = F1AP_TRPInformationRequestIEs__value_PR_TRPList; + for (int i = 0; i < msg->trp_list.trp_list_length; i++) { + // mandatory : TRP List Item (M) + asn1cSequenceAdd(ie2->value.choice.TRPList.list, F1AP_TRPListItem_t, trplistitem); + trplistitem->tRPID = msg->trp_list.trp_list_item[i].trp_id; + } + } + + /* mandatory : TRPInformationTypeList */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_TRPInformationRequestIEs_t, ie3); + ie3->id = F1AP_ProtocolIE_ID_id_TRPInformationTypeListTRPReq; + ie3->criticality = F1AP_Criticality_reject; + ie3->value.present = F1AP_TRPInformationRequestIEs__value_PR_TRPInformationTypeListTRPReq; + for (int i = 0; i < msg->trp_information_type_list.trp_information_type_list_length; i++) { + // TRPInformationTypeItem (M) + asn1cSequenceAdd(ie3->value.choice.TRPInformationTypeListTRPReq.list, F1AP_TRPInformationTypeItemTRPReq_t, trpinfotypeitem); + trpinfotypeitem->id = F1AP_ProtocolIE_ID_id_TRPInformationTypeItem; + trpinfotypeitem->criticality = F1AP_Criticality_reject; + trpinfotypeitem->value.present = F1AP_TRPInformationTypeItemTRPReq__value_PR_TRPInformationTypeItem; + trpinfotypeitem->value.choice.TRPInformationTypeItem = msg->trp_information_type_list.trp_information_type_item[i]; + } + + return pdu; +} + +/** + * @brief Decode F1 TRP information request + */ +bool decode_trp_information_req(const F1AP_F1AP_PDU_t *pdu, f1ap_trp_information_req_t *out) +{ + DevAssert(out != NULL); + memset(out, 0, sizeof(*out)); + + F1AP_TRPInformationRequest_t *in = &pdu->choice.initiatingMessage->value.choice.TRPInformationRequest; + F1AP_TRPInformationRequestIEs_t *ie; + + F1AP_LIB_FIND_IE(F1AP_TRPInformationRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_TransactionID, true); + F1AP_LIB_FIND_IE(F1AP_TRPInformationRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_TRPList, true); + F1AP_LIB_FIND_IE(F1AP_TRPInformationRequestIEs_t, + ie, + &in->protocolIEs.list, + F1AP_ProtocolIE_ID_id_TRPInformationTypeListTRPReq, + 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_TRPInformationRequestIEs__value_PR_TransactionID); + out->transaction_id = ie->value.choice.TransactionID; + break; + case F1AP_ProtocolIE_ID_id_TRPList: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_TRPInformationRequestIEs__value_PR_TRPList); + out->has_trp_list = true; + uint32_t trp_list_length = ie->value.choice.TRPList.list.count; + AssertError(trp_list_length > 0, return false, "at least 1 TRP must be present"); + out->trp_list.trp_list_length = trp_list_length; + out->trp_list.trp_list_item = calloc_or_fail(trp_list_length, sizeof(*out->trp_list.trp_list_item)); + F1AP_TRPList_t *TRPList = &ie->value.choice.TRPList; + for (int i = 0; i < trp_list_length; i++) { + out->trp_list.trp_list_item[i].trp_id = TRPList->list.array[i]->tRPID; + } + break; + case F1AP_ProtocolIE_ID_id_TRPInformationTypeListTRPReq: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_TRPInformationRequestIEs__value_PR_TRPInformationTypeListTRPReq); + uint8_t trp_info_type_list_length = ie->value.choice.TRPInformationTypeListTRPReq.list.count; + AssertError(trp_info_type_list_length > 0, return false, "at least 1 TRP Information Type must be present"); + f1ap_trp_information_type_list_t *trp_information_type_list = &out->trp_information_type_list; + trp_information_type_list->trp_information_type_list_length = trp_info_type_list_length; + trp_information_type_list->trp_information_type_item = + calloc_or_fail(trp_info_type_list_length, sizeof(*trp_information_type_list->trp_information_type_item)); + for (int i = 0; i < trp_info_type_list_length; i++) { + F1AP_TRPInformationTypeItemTRPReq_t *f1_trpinfotypeitem = + (F1AP_TRPInformationTypeItemTRPReq_t *)ie->value.choice.TRPInformationTypeListTRPReq.list.array[i]; + _F1_EQ_CHECK_LONG(f1_trpinfotypeitem->id, F1AP_ProtocolIE_ID_id_TRPInformationTypeItem); + _F1_EQ_CHECK_INT(f1_trpinfotypeitem->value.present, F1AP_TRPInformationTypeItemTRPReq__value_PR_TRPInformationTypeItem); + trp_information_type_list->trp_information_type_item[i] = f1_trpinfotypeitem->value.choice.TRPInformationTypeItem; + } + break; + default: + PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); + break; + } + } + + return true; +} + +/** + * @brief F1 TRP information request deep copy + */ +f1ap_trp_information_req_t cp_trp_information_req(const f1ap_trp_information_req_t *orig) +{ + /* copy all mandatory fields that are not dynamic memory */ + f1ap_trp_information_req_t cp = { + .transaction_id = orig->transaction_id, + .has_trp_list = orig->has_trp_list, + }; + + if (orig->has_trp_list) { + uint32_t trp_list_length = orig->trp_list.trp_list_length; + cp.trp_list.trp_list_length = trp_list_length; + cp.trp_list.trp_list_item = calloc_or_fail(trp_list_length, sizeof(*cp.trp_list.trp_list_item)); + for (int i = 0; i < trp_list_length; i++) { + cp.trp_list.trp_list_item[i].trp_id = orig->trp_list.trp_list_item[i].trp_id; + } + } + + const f1ap_trp_information_type_list_t *trp_information_type_list = &orig->trp_information_type_list; + f1ap_trp_information_type_list_t *trp_information_type_list_cp = &cp.trp_information_type_list; + uint8_t trp_info_type_list_length = trp_information_type_list->trp_information_type_list_length; + trp_information_type_list_cp->trp_information_type_list_length = trp_info_type_list_length; + trp_information_type_list_cp->trp_information_type_item = + calloc_or_fail(trp_info_type_list_length, sizeof(*trp_information_type_list_cp->trp_information_type_item)); + for (int i = 0; i < trp_info_type_list_length; i++) { + trp_information_type_list_cp->trp_information_type_item[i] = trp_information_type_list->trp_information_type_item[i]; + } + + return cp; +} + +/** + * @brief F1 TRP information request equality check + */ +bool eq_trp_information_req(const f1ap_trp_information_req_t *a, const f1ap_trp_information_req_t *b) +{ + _F1_EQ_CHECK_INT(a->transaction_id, b->transaction_id); + _F1_EQ_CHECK_INT(a->has_trp_list, b->has_trp_list); + if (a->has_trp_list) { + _F1_EQ_CHECK_INT(a->trp_list.trp_list_length, b->trp_list.trp_list_length); + for (int i = 0; i < a->trp_list.trp_list_length; i++) { + _F1_EQ_CHECK_INT(a->trp_list.trp_list_item[i].trp_id, b->trp_list.trp_list_item[i].trp_id); + } + } + + const f1ap_trp_information_type_list_t *trp_information_type_list_a = &a->trp_information_type_list; + const f1ap_trp_information_type_list_t *trp_information_type_list_b = &b->trp_information_type_list; + _F1_EQ_CHECK_INT(trp_information_type_list_a->trp_information_type_list_length, + trp_information_type_list_b->trp_information_type_list_length); + for (int i = 0; i < trp_information_type_list_a->trp_information_type_list_length; i++) { + _F1_EQ_CHECK_INT(trp_information_type_list_a->trp_information_type_item[i], + trp_information_type_list_b->trp_information_type_item[i]); + } + + return true; +} + +/** + * @brief Free Allocated F1 TRP information request + */ +void free_trp_information_req(f1ap_trp_information_req_t *msg) +{ + if (msg->has_trp_list) { + free(msg->trp_list.trp_list_item); + } + free(msg->trp_information_type_list.trp_information_type_item); +} diff --git a/openair2/F1AP/lib/f1ap_positioning.h b/openair2/F1AP/lib/f1ap_positioning.h index 2dc8bf5dd5..0bbd6721c2 100644 --- a/openair2/F1AP/lib/f1ap_positioning.h +++ b/openair2/F1AP/lib/f1ap_positioning.h @@ -86,4 +86,11 @@ bool eq_positioning_information_update(const f1ap_positioning_information_update const f1ap_positioning_information_update_t *b); void free_positioning_information_update(f1ap_positioning_information_update_t *msg); +// trp information request +struct F1AP_F1AP_PDU *encode_trp_information_req(const f1ap_trp_information_req_t *msg); +bool decode_trp_information_req(const struct F1AP_F1AP_PDU *pdu, f1ap_trp_information_req_t *out); +f1ap_trp_information_req_t cp_trp_information_req(const f1ap_trp_information_req_t *orig); +bool eq_trp_information_req(const f1ap_trp_information_req_t *a, const f1ap_trp_information_req_t *b); +void free_trp_information_req(f1ap_trp_information_req_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 e2adbd13a3..169c6709b3 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -1823,6 +1823,49 @@ static void test_f1ap_positioning_information_update() printf("%s() successful\n", __func__); } +static void test_f1ap_trp_information_request() +{ + f1ap_trp_information_req_t orig = { + .transaction_id = 12, + .has_trp_list = true, + }; + + uint32_t trp_list_length = 4; + orig.trp_list.trp_list_length = trp_list_length; + orig.trp_list.trp_list_item = calloc_or_fail(trp_list_length, sizeof(*orig.trp_list.trp_list_item)); + for (int i = 0; i < trp_list_length; i++) { + orig.trp_list.trp_list_item[i].trp_id = i + 1; + } + uint8_t trp_info_type_list_length = 2; + f1ap_trp_information_type_list_t *trp_information_type_list = &orig.trp_information_type_list; + trp_information_type_list->trp_information_type_list_length = trp_info_type_list_length; + trp_information_type_list->trp_information_type_item = + calloc_or_fail(trp_info_type_list_length, sizeof(*trp_information_type_list->trp_information_type_item)); + trp_information_type_list->trp_information_type_item[0] = F1AP_TRP_INFORMATION_TYPE_ITEM_NR_ARFCN; + trp_information_type_list->trp_information_type_item[1] = F1AP_TRP_INFORMATION_TYPE_ITEM_PRS_CONFIG; + + F1AP_F1AP_PDU_t *f1enc = encode_trp_information_req(&orig); + F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); + f1ap_msg_free(f1enc); + + f1ap_trp_information_req_t decoded = {0}; + bool ret = decode_trp_information_req(f1dec, &decoded); + AssertFatal(ret, "decode_trp_information_req(): could not decode message\n"); + f1ap_msg_free(f1dec); + + ret = eq_trp_information_req(&orig, &decoded); + AssertFatal(ret, "eq_trp_information_req(): decoded message doesn't match\n"); + free_trp_information_req(&decoded); + + f1ap_trp_information_req_t cp = cp_trp_information_req(&orig); + ret = eq_trp_information_req(&orig, &cp); + AssertFatal(ret, "eq_trp_information_req(): copied message doesn't match\n"); + free_trp_information_req(&orig); + free_trp_information_req(&cp); + + printf("%s() successful\n", __func__); +} + int main() { test_initial_ul_rrc_message_transfer(); @@ -1857,5 +1900,6 @@ int main() test_f1ap_positioning_activation_failure(); test_f1ap_positioning_deactivation(); test_f1ap_positioning_information_update(); + test_f1ap_trp_information_request(); return 0; }