From e5afdcdd4329b01f815489070ba96b3a52e143dc Mon Sep 17 00:00:00 2001 From: batuhan duyuler Date: Tue, 4 Jun 2024 07:41:43 +0200 Subject: [PATCH 1/3] Move cell-value lookup functions (SSB ARFCN, ...) to rrc_gNB_du.c The functions are needed in a later commit in both rrc_gNB_du.c and rrc_gNB.c, so move to the rrc_gNB_du.c file concerned with DUs (cells). --- openair2/RRC/NR/rrc_gNB.c | 18 ------------------ openair2/RRC/NR/rrc_gNB_du.c | 17 +++++++++++++++++ openair2/RRC/NR/rrc_gNB_du.h | 4 ++++ 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 9cfbb1a17a..7a4cb5d340 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -176,24 +176,6 @@ void nr_rrc_transfer_protected_rrc_message(const gNB_RRC_INST *rrc, &data); } -static int get_dl_band(const f1ap_served_cell_info_t *cell_info) -{ - return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.freqinfo.band : cell_info->fdd.dl_freqinfo.band; -} - -static int get_ssb_scs(const f1ap_served_cell_info_t *cell_info) -{ - return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.tbw.scs : cell_info->fdd.dl_tbw.scs; -} - -static int get_ssb_arfcn(const nr_rrc_du_container_t *du) -{ - DevAssert(du != NULL && du->mtc != NULL); - /* format has been verified when accepting MeasurementTimingConfiguration */ - NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; - return mtlist->list.array[0]->frequencyAndTiming->carrierFreq; -} - ///---------------------------------------------------------------------------------------------------------------/// ///---------------------------------------------------------------------------------------------------------------/// diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 3bce7964b0..92b37b48cc 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -28,6 +28,23 @@ #include "openair2/F1AP/f1ap_ids.h" #include "executables/softmodem-common.h" +int get_dl_band(const struct f1ap_served_cell_info_t *cell_info) +{ + return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.freqinfo.band : cell_info->fdd.dl_freqinfo.band; +} + +int get_ssb_scs(const struct f1ap_served_cell_info_t *cell_info) +{ + return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.tbw.scs : cell_info->fdd.dl_tbw.scs; +} + +int get_ssb_arfcn(const struct nr_rrc_du_container_t *du) +{ + DevAssert(du != NULL && du->mtc != NULL); + /* format has been verified when accepting MeasurementTimingConfiguration */ + NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; + return mtlist->list.array[0]->frequencyAndTiming->carrierFreq; +} static int du_compare(const nr_rrc_du_container_t *a, const nr_rrc_du_container_t *b) { diff --git a/openair2/RRC/NR/rrc_gNB_du.h b/openair2/RRC/NR/rrc_gNB_du.h index 618450f774..35ec3aeb1b 100644 --- a/openair2/RRC/NR/rrc_gNB_du.h +++ b/openair2/RRC/NR/rrc_gNB_du.h @@ -42,4 +42,8 @@ struct nr_rrc_du_container_t *get_du_by_assoc_id(struct gNB_RRC_INST_s *rrc, sct void dump_du_info(const struct gNB_RRC_INST_s *rrc, FILE *f); +int get_dl_band(const struct f1ap_served_cell_info_t *cell_info); +int get_ssb_scs(const struct f1ap_served_cell_info_t *cell_info); +int get_ssb_arfcn(const struct nr_rrc_du_container_t *du); + #endif /* RRC_GNB_DU_H_ */ From cec6b8925a845e7fc5b263fe4e94c1ea7808a067 Mon Sep 17 00:00:00 2001 From: batuhan duyuler Date: Tue, 28 May 2024 11:21:21 +0200 Subject: [PATCH 2/3] Preparation of Neighbour Configuration & Measurement Configuration | Parsing the configuration - neighbour_config.conf file includes neighbour and measurement related parameters Neighbour List: - nr_cellid: the identity of existing cell who has neighbour - neighbour_cell_configuration(list): includes the identity parameters of the neighbour cell such: - gNBID (incase of N2 Handover / to be identified from AMF) - nr_cell_id (target gNB needs to consider) - physical_cellId (needed for measurement reporting) - absoluteFrequencySSB (needed for measurement reporting) - subcarrierSpacing (needed for measurement reporting) - plmn / tracking area code (incase of N2 Handover / needed for AMF to identify / allow HO) NR Measurement Configuration - Periodical - enable (default is 0, 1 enables the reporting) - includeBeamMeasurements, maxNrOfRS_IndexesToReport (configurable reporting parameters) - A2 - enable (default is 0, 1 enables the reporting) - threshold , timeToTrigger (configurable reporting parameters) - A3 - cell_id (the nr_cellid of neighbour cell. So this A3 parameters will be used for the neighbour - [-1] means default. so it can be applied to any neighbour if there is no specific config - Neighbour specific A3 Threshold can be used or default can be selected depending to the neighbour planning - The neighbour-config.conf specifies, apply default A3 for neighbours 3 and 4 | apply specific one for neighbour 2 - offset, hysteresis, timeToTrigger (configurable reporting parameters) the neighbour_config.conf can be included in the gNB.conf file. Example is given: gnb.sa.band78.106prb.rfsim.neighbour.conf A sequence array container is used for neighbour cell configuration. RRC structure has this container. A Measurement configuration structure is defined. This structure has Periodical / A2 Event structures and also a sequence container for A3 Event List. These structures are filled during the initialization. While F1 Setup Response is coming from DU to CU, the intra frequency / inter frequency neighbour decision is given for the existing cell. Also new neighbours could be added here if needed. --- .../gnb.sa.band78.106prb.rfsim.neighbour.conf | 321 ++++++++++++++++++ ci-scripts/conf_files/neighbour-config.conf | 76 +++++ openair2/GNB_APP/gnb_config.c | 155 ++++++++- openair2/GNB_APP/gnb_paramdef.h | 87 ++++- openair2/RRC/NR/nr_rrc_defs.h | 43 +++ openair2/RRC/NR/rrc_gNB_du.c | 83 +++++ openair2/RRC/NR/rrc_gNB_du.h | 2 + 7 files changed, 765 insertions(+), 2 deletions(-) create mode 100644 ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.neighbour.conf create mode 100644 ci-scripts/conf_files/neighbour-config.conf diff --git a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.neighbour.conf b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.neighbour.conf new file mode 100644 index 0000000000..a5ae3fc995 --- /dev/null +++ b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.neighbour.conf @@ -0,0 +1,321 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; sd = 0x1; }) }); + + @include "neighbour-config.conf" + nr_cellid = 1; + + ////////// Physical parameters: + + do_CSIRS = 0; + do_SRS = 0; + min_rxtxtime = 6; + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 0; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 641280; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640008; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + # Dedicated Serving Cell Configuration + servingCellConfigDedicated = ({ + # BWP-Downlink + # BWP 1 Configuration + dl_bwp-Id_1 = 1; + dl_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + dl_bwp-Id_2 = 2; + dl_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + dl_bwp-Id_3 = 3; + dl_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_bwp3_subcarrierSpacing = 1; + + firstActiveDownlinkBWP-Id = 1; #BWP-Id + defaultDownlinkBWP-Id = 1; #BWP-Id + + # bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30, + # ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500, + # ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8, + # spare7, spare6, spare5, spare4, spare3, spare2, spare1 } + + # UplinkConfig + # BWP-Uplink + # BWP 1 Configuration + ul_bwp-Id_1 = 1; + ul_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp1_subcarrierSpacing = 1; + + # BWP 2 Configuration + ul_bwp-Id_2 = 2; + ul_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp2_subcarrierSpacing = 1; + + # BWP 3 Configuration + ul_bwp-Id_3 = 3; + ul_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW) + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_bwp3_subcarrierSpacing = 1; + + firstActiveUplinkBWP-Id = 1; #BWP-Id + } + ); + + + # ------- SCTP definitions + SCTP : { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ( { ipv4 = "192.168.70.132"; }); + + + NETWORK_INTERFACES : { + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 250; + pucch_TargetSNRx10 = 200; + ulsch_max_frame_inactivity = 0; +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 120; + pucch0_dtx_threshold = 100; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 +} +); + +RUs = ({ + local_rf = "yes"; + nb_tx = 1; + nb_rx = 1; + att_tx = 0; + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 114; + eNB_instances = [0]; + #beamforming 1x4 matrix: + bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000]; + clock_src = "internal"; +}); + +THREAD_STRUCT = ( + { + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_SINGLE_THREAD"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; + } +); + +rfsimulator: { + serveraddr = "server"; + serverport = 4043; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs" +} + +security = { +# preferred ciphering algorithms +# the first one of the list that an UE supports in chosen +# valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + +# preferred integrity algorithms +# the first one of the list that an UE supports in chosen +# valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + +# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter +# what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : { + global_log_level ="info"; + hw_log_level ="info"; + phy_log_level ="info"; + mac_log_level ="info"; + rlc_log_level ="info"; + pdcp_log_level ="info"; + nr_rrc_log_level ="info"; + rrc_log_level ="info"; + ngap_log_level ="debug"; + f1ap_log_level ="debug"; +}; diff --git a/ci-scripts/conf_files/neighbour-config.conf b/ci-scripts/conf_files/neighbour-config.conf new file mode 100644 index 0000000000..12b023de7a --- /dev/null +++ b/ci-scripts/conf_files/neighbour-config.conf @@ -0,0 +1,76 @@ +neighbour_list = ( + { + nr_cellid = 1; + neighbour_cell_configuration = ( + { + gNB_ID = 0xb00; + nr_cellid = 2; + physical_cellId = 0x002; + absoluteFrequencySSB = 641280 ; + subcarrierSpacing = 1; #30 KHz + plmn = { mcc = 216; mnc = 03; mnc_length = 2}; + tracking_area_code = 2; + }, + { + gNB_ID = 0xa00; + nr_cellid = 3; + physical_cellId = 0x003; + absoluteFrequencySSB = 641280 ; + subcarrierSpacing = 1; #30 KHz + plmn = { mcc = 999; mnc = 99; mnc_length = 2}; + tracking_area_code = 3; + }, + { + gNB_ID = 0xc00; + nr_cellid = 4; + physical_cellId = 0x004; + absoluteFrequencySSB = 641280 ; + subcarrierSpacing = 1; #30 KHz + plmn = { mcc = 999; mnc = 99; mnc_length = 2}; + tracking_area_code = 4; + } + ) + }, + { + nr_cellid = 2; + neighbour_cell_configuration = ( + { + gNB_ID = 0xe00; + nr_cellid = 1; + physical_cellId = 0x000; + absoluteFrequencySSB = 641280 ; + subcarrierSpacing = 1; #30 KHz + plmn = { mcc = 001; mnc = 01; mnc_length = 2}; + tracking_area_code = 1; + } + ) + } + ); + + +nr_measurement_configuration = { + Periodical = { + enable = 1; + includeBeamMeasurements = 1; + maxNrofRS_IndexesToReport = 4; + }; + + A2 = { + enable = 1; + threshold = 60; + timeToTrigger = 1; + }; + + A3 = ({ + cell_id = -1; #Default + offset = 10; + hysteresis = 0; + timeToTrigger = 1 + }, { + cell_id = 2; + offset = 5; + hysteresis = 1; + timeToTrigger = 2 + }) +}; + diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index c1d15d810b..13e9acaf6e 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -85,6 +85,7 @@ #include "NR_EUTRA-MBSFN-SubframeConfig.h" #include "uper_decoder.h" #include "uper_encoder.h" +#include "common/utils/ds/seq_arr.h" #include "RRC/NR/MESSAGES/asn1_msg.h" #include "RRC/NR/nr_rrc_extern.h" @@ -1593,6 +1594,155 @@ static void check_ssb_raster(uint64_t freq, int band, int scs) gscn, freq, band); } +static int sort_neighbour_cell_config_by_cell_id(const void *a, const void *b) +{ + const neighbour_cell_configuration_t *config_a = (const neighbour_cell_configuration_t *)a; + const neighbour_cell_configuration_t *config_b = (const neighbour_cell_configuration_t *)b; + + if (config_a->nr_cell_id < config_b->nr_cell_id) { + return -1; + } else if (config_a->nr_cell_id > config_b->nr_cell_id) { + return 1; + } + + return 0; +} + +static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc) +{ + char gnbpath[MAX_OPTNAME_SIZE + 8]; + sprintf(gnbpath, "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx); + + paramdef_t neighbour_list_params[] = GNB_NEIGHBOUR_LIST_PARAM_LIST; + paramlist_def_t neighbour_list_param_list = {GNB_CONFIG_STRING_NEIGHBOUR_LIST, NULL, 0}; + + config_getlist(config_get_if(), &neighbour_list_param_list, neighbour_list_params, sizeofArray(neighbour_list_params), gnbpath); + if (neighbour_list_param_list.numelt < 1) + return; + + rrc->neighbour_cell_configuration = malloc(sizeof(seq_arr_t)); + seq_arr_init(rrc->neighbour_cell_configuration, sizeof(neighbour_cell_configuration_t)); + + for (int elm = 0; elm < neighbour_list_param_list.numelt; ++elm) { + neighbour_cell_configuration_t *cell = calloc(1, sizeof(neighbour_cell_configuration_t)); + AssertFatal(cell != NULL, "out of memory\n"); + cell->nr_cell_id = (uint64_t)*neighbour_list_param_list.paramarray[elm][0].u64ptr; + + char neighbourpath[MAX_OPTNAME_SIZE + 8]; + sprintf(neighbourpath, "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_NEIGHBOUR_LIST, elm); + paramdef_t NeighbourCellParams[] = GNBNEIGHBOURCELLPARAMS_DESC; + paramlist_def_t NeighbourCellParamList = {GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, NULL, 0}; + config_getlist(config_get_if(), &NeighbourCellParamList, NeighbourCellParams, sizeofArray(NeighbourCellParams), neighbourpath); + LOG_D(GNB_APP, "HO LOG: For the Cell: %d Neighbour Cell ELM NUM: %d\n", cell->nr_cell_id, NeighbourCellParamList.numelt); + if (NeighbourCellParamList.numelt < 1) + continue; + + cell->neighbour_cells = malloc(sizeof(seq_arr_t)); + AssertFatal(cell->neighbour_cells != NULL, "Memory exhausted!!!"); + seq_arr_init(cell->neighbour_cells, sizeof(nr_neighbour_gnb_configuration_t)); + for (int l = 0; l < NeighbourCellParamList.numelt; ++l) { + nr_neighbour_gnb_configuration_t *neighbourCell = calloc(1, sizeof(nr_neighbour_gnb_configuration_t)); + AssertFatal(neighbourCell != NULL, "out of memory\n"); + neighbourCell->gNB_ID = *(NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr); + neighbourCell->nrcell_id = (uint64_t) * (NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr); + neighbourCell->physicalCellId = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr; + neighbourCell->subcarrierSpacing = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_SCS_IDX].uptr; + neighbourCell->absoluteFrequencySSB = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr; + neighbourCell->tac = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_TAC_IDX].uptr; + + char neighbour_plmn_path[CONFIG_MAXOPTLENGTH]; + sprintf(neighbour_plmn_path, + "%s.%s.[%i].%s", + neighbourpath, + GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, + l, + GNB_CONFIG_STRING_NEIGHBOUR_PLMN); + + paramdef_t NeighbourPlmn[] = GNBPLMNPARAMS_DESC; + config_get(config_get_if(), NeighbourPlmn, sizeofArray(NeighbourPlmn), neighbour_plmn_path); + + neighbourCell->plmn.mcc = *NeighbourPlmn[GNB_MOBILE_COUNTRY_CODE_IDX].uptr; + neighbourCell->plmn.mnc = *NeighbourPlmn[GNB_MOBILE_NETWORK_CODE_IDX].uptr; + neighbourCell->plmn.mnc_digit_length = *NeighbourPlmn[GNB_MNC_DIGIT_LENGTH].uptr; + seq_arr_push_back(cell->neighbour_cells, neighbourCell, sizeof(nr_neighbour_gnb_configuration_t)); + } + + seq_arr_push_back(rrc->neighbour_cell_configuration, cell, sizeof(neighbour_cell_configuration_t)); + } + void *base = seq_arr_front(rrc->neighbour_cell_configuration); + size_t nmemb = seq_arr_size(rrc->neighbour_cell_configuration); + size_t element_size = sizeof(neighbour_cell_configuration_t); + + qsort(base, nmemb, element_size, sort_neighbour_cell_config_by_cell_id); +} + +static void fill_measurement_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc) +{ + char measurement_path[MAX_OPTNAME_SIZE + 8]; + sprintf(measurement_path, "%s.[%i].%s", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_MEASUREMENT_CONFIGURATION); + + nr_measurement_configuration_t *measurementConfig = &rrc->measurementConfiguration; + // Periodical Event Configuration + char periodic_event_path[MAX_OPTNAME_SIZE + 8]; + sprintf(periodic_event_path, + "%s.[%i].%s.%s", + GNB_CONFIG_STRING_GNB_LIST, + gnb_idx, + GNB_CONFIG_STRING_MEASUREMENT_CONFIGURATION, + MEASUREMENT_EVENTS_PERIODICAL); + paramdef_t Periodical_EventParams[] = MEASUREMENT_PERIODICAL_GLOBALPARAMS_DESC; + config_get(config_get_if(), Periodical_EventParams, sizeofArray(Periodical_EventParams), periodic_event_path); + if (*Periodical_EventParams[MEASUREMENT_EVENTS_ENABLE_IDX].i64ptr) { + nr_per_event_t *periodic_event = (nr_per_event_t *)calloc(1, sizeof(nr_per_event_t)); + periodic_event->includeBeamMeasurements = *Periodical_EventParams[MEASUREMENT_EVENTS_INCLUDE_BEAM_MEAS_IDX].i64ptr; + periodic_event->maxReportCells = *Periodical_EventParams[MEASUREMENT_EVENTS_MAX_RS_INDEX_TO_REPORT].i64ptr; + + measurementConfig->per_event = periodic_event; + } + + // A2 Event Configuration + char a2_path[MAX_OPTNAME_SIZE + 8]; + sprintf(a2_path, + "%s.[%i].%s.%s", + GNB_CONFIG_STRING_GNB_LIST, + gnb_idx, + GNB_CONFIG_STRING_MEASUREMENT_CONFIGURATION, + MEASUREMENT_EVENTS_A2); + paramdef_t A2_EventParams[] = MEASUREMENT_A2_GLOBALPARAMS_DESC; + config_get(config_get_if(), A2_EventParams, sizeofArray(A2_EventParams), a2_path); + if (*A2_EventParams[MEASUREMENT_EVENTS_ENABLE_IDX].i64ptr) { + nr_a2_event_t *a2_event = (nr_a2_event_t *)calloc(1, sizeof(nr_a2_event_t)); + a2_event->threshold_RSRP = *A2_EventParams[MEASUREMENT_EVENTS_A2_THRESHOLD_IDX].i64ptr; + a2_event->timeToTrigger = *A2_EventParams[MEASUREMENT_EVENTS_TIMETOTRIGGER_IDX].i64ptr; + + measurementConfig->a2_event = a2_event; + } + // A3 Event Configuration + paramlist_def_t A3_EventList = {MEASUREMENT_EVENTS_A3, NULL, 0}; + paramdef_t A3_EventParams[] = MEASUREMENT_A3_GLOBALPARAMS_DESC; + config_getlist(config_get_if(), &A3_EventList, A3_EventParams, sizeofArray(A3_EventParams), measurement_path); + LOG_D(GNB_APP, "HO LOG: A3 Configuration Exists: %d\n", A3_EventList.numelt); + + if (A3_EventList.numelt < 1) + return; + + measurementConfig->a3_event_list = malloc(sizeof(seq_arr_t)); + seq_arr_init(measurementConfig->a3_event_list, sizeof(nr_a3_event_t)); + for (int i = 0; i < A3_EventList.numelt; i++) { + nr_a3_event_t *a3_event = (nr_a3_event_t *)calloc(1, sizeof(nr_a3_event_t)); + AssertFatal(a3_event != NULL, "out of memory\n"); + a3_event->cell_id = *A3_EventList.paramarray[i][MEASUREMENT_EVENTS_CELL_ID_IDX].i64ptr; + a3_event->timeToTrigger = *A3_EventList.paramarray[i][MEASUREMENT_EVENTS_TIMETOTRIGGER_IDX].i64ptr; + a3_event->a3_offset = *A3_EventList.paramarray[i][MEASUREMENT_EVENTS_OFFSET_IDX].i64ptr; + a3_event->hysteresis = *A3_EventList.paramarray[i][MEASUREMENT_EVENTS_HYSTERESIS_IDX].i64ptr; + + if (a3_event->cell_id == -1) + measurementConfig->is_default_a3_configuration_exists = true; + + seq_arr_push_back(measurementConfig->a3_event_list, a3_event, sizeof(nr_a3_event_t)); + } +} + void RCconfig_NRRRC(gNB_RRC_INST *rrc) { @@ -1682,7 +1832,10 @@ void RCconfig_NRRRC(gNB_RRC_INST *rrc) char gnbpath[MAX_OPTNAME_SIZE + 8]; sprintf(gnbpath,"%s.[%i]",GNB_CONFIG_STRING_GNB_LIST,k); - + fill_neighbour_cell_configuration(k, rrc); + + fill_measurement_configuration(k, rrc); + paramdef_t PLMNParams[] = GNBPLMNPARAMS_DESC; paramlist_def_t PLMNParamList = {GNB_CONFIG_STRING_PLMN_LIST, NULL, 0}; diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index 6491ddb755..5382b41a34 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -254,7 +254,92 @@ typedef enum { } /*-------------------------------------------------------------------------------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/* Neighbour Cell Configurations*/ +#define GNB_CONFIG_STRING_NEIGHBOUR_LIST "neighbour_list" +// clang-format off +#define GNB_NEIGHBOUR_LIST_PARAM_LIST { \ +/* optname helpstr paramflags XXXptr def val type numelt */ \ + {GNB_CONFIG_STRING_NRCELLID, "cell nrCell Id which has neighbours", PARAMFLAG_MANDATORY, .u64ptr=NULL, .defint64val=0, TYPE_UINT64, 0}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID, "neighbour cell physical id", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \ +} +// clang-format on +#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST "neighbour_cell_configuration" + +#define GNB_CONFIG_STRING_NEIGHBOUR_GNB_ID "gNB_ID" +#define GNB_CONFIG_STRING_NEIGHBOUR_NR_CELLID "nr_cellid" +#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID "physical_cellId" +#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_ABS_FREQ_SSB "absoluteFrequencySSB" +#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_SCS "subcarrierSpacing" +#define GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE "tracking_area_code" +#define GNB_CONFIG_STRING_NEIGHBOUR_PLMN "plmn" + +#define GNB_CONFIG_N_CELL_GNB_ID_IDX 0 +#define GNB_CONFIG_N_CELL_NR_CELLID_IDX 1 +#define GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX 2 +#define GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX 3 +#define GNB_CONFIG_N_CELL_SCS_IDX 4 +#define GNB_CONFIG_N_CELL_TAC_IDX 5 +// clang-format off +#define GNBNEIGHBOURCELLPARAMS_DESC { \ +/* optname helpstr paramflags XXXptr def val type numelt */ \ + {GNB_CONFIG_STRING_GNB_ID, "neighbour cell's gNB ID", PARAMFLAG_MANDATORY, .uptr=NULL, .defintval=0, TYPE_UINT, 0}, \ + {GNB_CONFIG_STRING_NRCELLID, "neighbour cell nrCell Id", PARAMFLAG_MANDATORY, .u64ptr=NULL, .defint64val=0, TYPE_UINT64, 0}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID, "neighbour cell physical id", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_ABS_FREQ_SSB, "neighbour cell abs freq ssb", PARAMFLAG_MANDATORY, .i64ptr=NULL, .defint64val=0, TYPE_INT64, 0}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_SCS, "neighbour cell scs", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE, "neighbour cell tracking area", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \ +} +// clang-format on + +/* New Measurement Configurations*/ + +#define GNB_CONFIG_STRING_MEASUREMENT_CONFIGURATION "nr_measurement_configuration" +#define MEASUREMENT_EVENTS_PERIODICAL "Periodical" +#define MEASUREMENT_EVENTS_A2 "A2" +#define MEASUREMENT_EVENTS_A3 "A3" + +#define MEASUREMENT_EVENTS_OFFSET "offset" +#define MEASUREMENT_EVENTS_HYSTERESIS "hysteresis" +#define MEASUREMENT_EVENTS_TIME_TO_TRIGGER "time_to_trigger" +#define MEASUREMENT_EVENTS_THRESHOLD "threshold" +#define MEASUREMENT_EVENTS_PERIODICAL_BEAM_MEASUREMENT "includeBeamMeasurements" +#define MEASUREMENT_EVENTS_PERIODICAL_NR_OF_RS_INDEXES "maxNrofRS_IndexesToReport" +#define MEASUREMENT_EVENTS_CELL_ID "cell_id" +#define MEASUREMENT_EVENT_ENABLE "enable" +// clang-format off +#define MEASUREMENT_A3_GLOBALPARAMS_DESC \ + { \ + {MEASUREMENT_EVENTS_CELL_ID, "neighbour cellId for A3Report", 0, .i64ptr = NULL, .defint64val = -1, TYPE_INT64, 0}, \ + {MEASUREMENT_EVENTS_TIME_TO_TRIGGER, "a3 time to trigger", 0, .i64ptr = NULL, .defint64val = 1, TYPE_INT64, 0}, \ + {MEASUREMENT_EVENTS_OFFSET, "a3 offset", 0, .i64ptr = NULL, .defint64val = 60, TYPE_INT64, 0}, \ + {MEASUREMENT_EVENTS_HYSTERESIS, "a3 hysteresis", 0, .i64ptr = NULL, .defint64val = 0, TYPE_INT64, 0}, \ + } + +#define MEASUREMENT_A2_GLOBALPARAMS_DESC \ + { \ + {MEASUREMENT_EVENT_ENABLE, "enable the event", 0, .i64ptr = NULL, .defint64val = 1, TYPE_INT64, 0}, \ + {MEASUREMENT_EVENTS_TIME_TO_TRIGGER, "a2 time to trigger", 0, .i64ptr = NULL, .defint64val = 1, TYPE_INT64, 0}, \ + {MEASUREMENT_EVENTS_THRESHOLD, "a2 threshold", 0, .i64ptr = NULL, .defint64val = 60, TYPE_INT64, 0}, \ + } + +#define MEASUREMENT_PERIODICAL_GLOBALPARAMS_DESC \ + { \ + {MEASUREMENT_EVENT_ENABLE, "enable the event", 0, .i64ptr = NULL, .defint64val = 1, TYPE_INT64, 0}, \ + {MEASUREMENT_EVENTS_PERIODICAL_BEAM_MEASUREMENT, "includeBeamMeasurements", PARAMFLAG_BOOL, .i64ptr = NULL, .defint64val = 1, TYPE_INT64, 0}, \ + {MEASUREMENT_EVENTS_PERIODICAL_NR_OF_RS_INDEXES, "maxNrofRS_IndexesToReport", 0, .i64ptr = NULL, .defint64val = 4, TYPE_INT64, 0}, \ + } +// clang-format on + +#define MEASUREMENT_EVENTS_CELL_ID_IDX 0 +#define MEASUREMENT_EVENTS_ENABLE_IDX 0 +#define MEASUREMENT_EVENTS_TIMETOTRIGGER_IDX 1 +#define MEASUREMENT_EVENTS_A2_THRESHOLD_IDX 2 +#define MEASUREMENT_EVENTS_OFFSET_IDX 2 +#define MEASUREMENT_EVENTS_HYSTERESIS_IDX 3 +#define MEASUREMENT_EVENTS_INCLUDE_BEAM_MEAS_IDX 1 +#define MEASUREMENT_EVENTS_MAX_RS_INDEX_TO_REPORT 2 /* PLMN ID configuration */ diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index b2003e8d85..fabcd51df0 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -37,6 +37,7 @@ #include "collection/tree.h" #include "collection/linear_alloc.h" +#include "common/utils/ds/seq_arr.h" #include "nr_rrc_common.h" #include "ds/byte_array.h" @@ -359,6 +360,46 @@ typedef struct { int do_drb_integrity; } nr_security_configuration_t; +typedef struct { + long maxReportCells; + bool includeBeamMeasurements; +} nr_per_event_t; + +typedef struct { + long threshold_RSRP; + long timeToTrigger; +} nr_a2_event_t; + +typedef struct { + int cell_id; + long a3_offset; + long hysteresis; + long timeToTrigger; +} nr_a3_event_t; + +typedef struct { + nr_per_event_t *per_event; + nr_a2_event_t *a2_event; + seq_arr_t *a3_event_list; + bool is_default_a3_configuration_exists; +} nr_measurement_configuration_t; + +typedef struct { + uint32_t gNB_ID; + uint64_t nrcell_id; + int physicalCellId; + int absoluteFrequencySSB; + int subcarrierSpacing; + plmn_identity_t plmn; + uint32_t tac; + bool isIntraFrequencyNeighbour; +} nr_neighbour_gnb_configuration_t; + +typedef struct neighbour_cell_configuration_s { + int nr_cell_id; + seq_arr_t *neighbour_cells; +} neighbour_cell_configuration_t; + typedef struct nr_mac_rrc_dl_if_s { f1_setup_response_func_t f1_setup_response; f1_setup_failure_func_t f1_setup_failure; @@ -423,6 +464,8 @@ typedef struct gNB_RRC_INST_s { nr_mac_rrc_dl_if_t mac_rrc; cucp_cuup_if_t cucp_cuup; + seq_arr_t *neighbour_cell_configuration; + nr_measurement_configuration_t measurementConfiguration; RB_HEAD(rrc_du_tree, nr_rrc_du_container_t) dus; // DUs, indexed by assoc_id size_t num_dus; diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 92b37b48cc..b24e6f594c 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -27,6 +27,8 @@ #include "openair2/F1AP/f1ap_common.h" #include "openair2/F1AP/f1ap_ids.h" #include "executables/softmodem-common.h" +#include "common/utils/ds/seq_arr.h" +#include "common/utils/alg/foreach.h" int get_dl_band(const struct f1ap_served_cell_info_t *cell_info) { @@ -121,6 +123,80 @@ static NR_MeasurementTimingConfiguration_t *extract_mtc(uint8_t *buf, int buf_le return mtc; } +static int nr_cell_id_match(const void *key, const void *element) +{ + const int *key_id = (const int *)key; + const neighbour_cell_configuration_t *config_element = (const neighbour_cell_configuration_t *)element; + + if (*key_id < config_element->nr_cell_id) { + return -1; + } else if (*key_id == config_element->nr_cell_id) { + return 0; + } + + return 1; +} + +static neighbour_cell_configuration_t *get_cell_neighbour_list(const gNB_RRC_INST *rrc, const f1ap_served_cell_info_t *cell_info) +{ + void *base = seq_arr_front(rrc->neighbour_cell_configuration); + size_t nmemb = seq_arr_size(rrc->neighbour_cell_configuration); + size_t size = sizeof(neighbour_cell_configuration_t); + + void *it = bsearch((void *)&cell_info->nr_cellid, base, nmemb, size, nr_cell_id_match); + + return (neighbour_cell_configuration_t *)it; +} + +const struct f1ap_served_cell_info_t *get_cell_information_by_phycellId(int phyCellId) +{ + gNB_RRC_INST *rrc = RC.nrrrc[0]; + nr_rrc_du_container_t *it = NULL; + RB_FOREACH (it, rrc_du_tree, &rrc->dus) { + for (int cellIdx = 0; cellIdx < it->setup_req->num_cells_available; cellIdx++) { + const f1ap_served_cell_info_t *cell_info = &(it->setup_req->cell[cellIdx].info); + if (cell_info->nr_pci == phyCellId) { + LOG_D(NR_RRC, "HO LOG: Found cell with phyCellId %d\n", phyCellId); + return cell_info; + } + } + } + return NULL; +} + +static void is_intra_frequency_neighbour(void *ssb_arfcn, void *neighbour_cell) +{ + uint32_t *ssb_arfcn_ptr = (uint32_t *)ssb_arfcn; + nr_neighbour_gnb_configuration_t *neighbour_cell_ptr = (nr_neighbour_gnb_configuration_t *)neighbour_cell; + + if (*ssb_arfcn_ptr == neighbour_cell_ptr->absoluteFrequencySSB) { + LOG_D(NR_RRC, "HO LOG: found intra frequency neighbour %lu!\n", neighbour_cell_ptr->nrcell_id); + neighbour_cell_ptr->isIntraFrequencyNeighbour = true; + } +} +/** + * @brief Labels neighbour cells if they are intra frequency to prepare meas config only for intra frequency ho + * @param[in] rrc Pointer to RRC instance + * @param[in] cell_info Pointer to cell information + */ +static void label_intra_frequency_neighbours(gNB_RRC_INST *rrc, + const nr_rrc_du_container_t *du, + const f1ap_served_cell_info_t *cell_info) +{ + if (!rrc->neighbour_cell_configuration) + return; + + neighbour_cell_configuration_t *neighbour_cell_config = get_cell_neighbour_list(rrc, cell_info); + if (!neighbour_cell_config) + return; + + LOG_D(NR_RRC, "HO LOG: Cell: %lu has neighbour cell configuration!\n", cell_info->nr_cellid); + uint32_t ssb_arfcn = get_ssb_arfcn(du); + + seq_arr_t *cell_neighbour_list = neighbour_cell_config->neighbour_cells; + for_each(cell_neighbour_list, (void *)&ssb_arfcn, is_intra_frequency_neighbour); +} + void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) { AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n"); @@ -229,6 +305,9 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) .num_SI = 0, }; + if (du->mib != NULL && du->sib1 != NULL) + label_intra_frequency_neighbours(rrc, du, cell_info); + f1ap_setup_resp_t resp = {.transaction_id = req->transaction_id, .num_cells_to_activate = 1, .cells_to_activate[0] = cell}; @@ -350,6 +429,10 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update LOG_I(RRC, "update system information of DU %ld\n", du->setup_req->gNB_DU_id); } } + if (du->mib != NULL && du->sib1 != NULL) { + const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; + label_intra_frequency_neighbours(rrc, du, cell_info); + } } if (conf_up->num_cells_to_delete > 0) { diff --git a/openair2/RRC/NR/rrc_gNB_du.h b/openair2/RRC/NR/rrc_gNB_du.h index 35ec3aeb1b..3ee0b611ef 100644 --- a/openair2/RRC/NR/rrc_gNB_du.h +++ b/openair2/RRC/NR/rrc_gNB_du.h @@ -32,6 +32,7 @@ struct f1ap_lost_connection_t; struct gNB_RRC_INST_s; struct nr_rrc_du_container_t; struct f1ap_gnb_du_configuration_update_s; +struct f1ap_served_cell_info_t; void rrc_gNB_process_f1_setup_req(struct f1ap_setup_req_s *req, sctp_assoc_t assoc_id); void rrc_CU_process_f1_lost_connection(struct gNB_RRC_INST_s *rrc, struct f1ap_lost_connection_t *lc, sctp_assoc_t assoc_id); @@ -39,6 +40,7 @@ void rrc_gNB_process_f1_du_configuration_update(struct f1ap_gnb_du_configuration struct nr_rrc_du_container_t *get_du_for_ue(struct gNB_RRC_INST_s *rrc, uint32_t ue_id); struct nr_rrc_du_container_t *get_du_by_assoc_id(struct gNB_RRC_INST_s *rrc, sctp_assoc_t assoc_id); +const struct f1ap_served_cell_info_t *get_cell_information_by_phycellId(int phyCellId); void dump_du_info(const struct gNB_RRC_INST_s *rrc, FILE *f); From c51b2a64fbc52e3cc2f077c12d2c5fc1988b29c5 Mon Sep 17 00:00:00 2001 From: batuhan duyuler Date: Tue, 28 May 2024 11:21:55 +0200 Subject: [PATCH 3/3] Prepare Measurement Configuration according to neighbour measurement configuration Neighbor configuration can be given by gNB.conf get_MeasConfig function is extended as it will prepare A2 / A3 Report Configs. Single Meas Obj is used for all. rrc_gNB_process_MeasurementReport function is extended to process A2 / A3 measurement events. helper functions are written to fetch neighbour cell informations from sequence containers. (get_neighbour_cell_information, get_neighbour_config) --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 253 ++++++++++++++++++++++------ openair2/RRC/NR/MESSAGES/asn1_msg.h | 9 +- openair2/RRC/NR/rrc_gNB.c | 197 ++++++++++++++++++++-- 3 files changed, 396 insertions(+), 63 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 3e814748ee..fe41e9e210 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -736,7 +736,7 @@ int do_RRCReconfiguration(const gNB_RRC_UE_t *UE, dl_dcch_msg.message.choice.c1->choice.rrcReconfiguration->criticalExtensions.choice.rrcReconfiguration = ie; - if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg); } @@ -1093,15 +1093,180 @@ int do_RRCReestablishmentComplete(uint8_t *buffer, size_t buffer_size, int64_t r return((enc_rval.encoded+7)/8); } -NR_MeasConfig_t *get_defaultMeasConfig(const NR_MeasTiming_t *mt, int band, int scs) +static NR_ReportConfigToAddMod_t *prepare_periodic_event_report(const nr_per_event_t *per_event) { - DevAssert(mt != NULL && mt->frequencyAndTiming != NULL); - const struct NR_MeasTiming__frequencyAndTiming *ft = mt->frequencyAndTiming; - const NR_SSB_MTC_t *ssb_mtc = &ft->ssb_MeasurementTimingConfiguration; + NR_ReportConfigToAddMod_t *rc = calloc(1, sizeof(*rc)); + rc->reportConfigId = 1; + rc->reportConfig.present = NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR; + + NR_PeriodicalReportConfig_t *prc = calloc(1, sizeof(*prc)); + prc->rsType = NR_NR_RS_Type_ssb; + prc->reportInterval = NR_ReportInterval_ms1024; + prc->reportAmount = NR_PeriodicalReportConfig__reportAmount_infinity; + prc->reportQuantityCell.rsrp = true; + prc->reportQuantityCell.rsrq = true; + prc->reportQuantityCell.sinr = true; + prc->reportQuantityRS_Indexes = calloc(1, sizeof(*prc->reportQuantityRS_Indexes)); + prc->reportQuantityRS_Indexes->rsrp = true; + prc->reportQuantityRS_Indexes->rsrq = true; + prc->reportQuantityRS_Indexes->sinr = true; + asn1cCallocOne(prc->maxNrofRS_IndexesToReport, per_event->maxReportCells); + prc->maxReportCells = per_event->maxReportCells; + prc->includeBeamMeasurements = per_event->includeBeamMeasurements; + + NR_ReportConfigNR_t *rcnr = calloc(1, sizeof(*rcnr)); + rcnr->reportType.present = NR_ReportConfigNR__reportType_PR_periodical; + rcnr->reportType.choice.periodical = prc; + + rc->reportConfig.choice.reportConfigNR = rcnr; + return rc; +} + +static NR_ReportConfigToAddMod_t *prepare_a2_event_report(const nr_a2_event_t *a2_event) +{ + NR_ReportConfigToAddMod_t *rc_A2 = calloc(1, sizeof(*rc_A2)); + rc_A2->reportConfigId = 2; + rc_A2->reportConfig.present = NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR; + NR_EventTriggerConfig_t *etrc_A2 = calloc(1, sizeof(*etrc_A2)); + etrc_A2->eventId.present = NR_EventTriggerConfig__eventId_PR_eventA2; + etrc_A2->eventId.choice.eventA2 = calloc(1, sizeof(*etrc_A2->eventId.choice.eventA2)); + etrc_A2->eventId.choice.eventA2->a2_Threshold.present = NR_MeasTriggerQuantity_PR_rsrp; + etrc_A2->eventId.choice.eventA2->a2_Threshold.choice.rsrp = a2_event->threshold_RSRP; + etrc_A2->eventId.choice.eventA2->reportOnLeave = false; + etrc_A2->eventId.choice.eventA2->hysteresis = 0; + etrc_A2->eventId.choice.eventA2->timeToTrigger = a2_event->timeToTrigger; + etrc_A2->rsType = NR_NR_RS_Type_ssb; + etrc_A2->reportInterval = NR_ReportInterval_ms480; + etrc_A2->reportAmount = NR_EventTriggerConfig__reportAmount_r4; + etrc_A2->reportQuantityCell.rsrp = true; + etrc_A2->reportQuantityCell.rsrq = true; + etrc_A2->reportQuantityCell.sinr = true; + asn1cCallocOne(etrc_A2->maxNrofRS_IndexesToReport, 4); + etrc_A2->maxReportCells = 1; + etrc_A2->includeBeamMeasurements = false; + NR_ReportConfigNR_t *rcnr_A2 = calloc(1, sizeof(*rcnr_A2)); + rcnr_A2->reportType.present = NR_ReportConfigNR__reportType_PR_eventTriggered; + rcnr_A2->reportType.choice.eventTriggered = etrc_A2; + rc_A2->reportConfig.choice.reportConfigNR = rcnr_A2; + + return rc_A2; +} + +static NR_ReportConfigToAddMod_t *prepare_a3_event_report(const nr_a3_event_t *a3_event) +{ + NR_ReportConfigToAddMod_t *rc_A3 = calloc(1, sizeof(*rc_A3)); + rc_A3->reportConfigId = + a3_event->cell_id == -1 + ? 3 + : a3_event->cell_id + 4; // 3 is default A3 Report Config ID. So cellId(0) specific Report Config ID starts from 4 + rc_A3->reportConfig.present = NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR; + NR_EventTriggerConfig_t *etrc_A3 = calloc(1, sizeof(*etrc_A3)); + etrc_A3->eventId.present = NR_EventTriggerConfig__eventId_PR_eventA3; + etrc_A3->eventId.choice.eventA3 = calloc(1, sizeof(*etrc_A3->eventId.choice.eventA3)); + etrc_A3->eventId.choice.eventA3->a3_Offset.present = NR_MeasTriggerQuantityOffset_PR_rsrp; + etrc_A3->eventId.choice.eventA3->a3_Offset.choice.rsrp = a3_event->a3_offset; + etrc_A3->eventId.choice.eventA3->reportOnLeave = true; + etrc_A3->eventId.choice.eventA3->hysteresis = a3_event->hysteresis; + etrc_A3->eventId.choice.eventA3->timeToTrigger = a3_event->timeToTrigger; + etrc_A3->rsType = NR_NR_RS_Type_ssb; + etrc_A3->reportInterval = NR_ReportInterval_ms1024; + etrc_A3->reportAmount = NR_EventTriggerConfig__reportAmount_r4; + etrc_A3->reportQuantityCell.rsrp = true; + etrc_A3->reportQuantityCell.rsrq = true; + etrc_A3->reportQuantityCell.sinr = true; + asn1cCallocOne(etrc_A3->maxNrofRS_IndexesToReport, 4); + etrc_A3->maxReportCells = 4; + etrc_A3->includeBeamMeasurements = false; + NR_ReportConfigNR_t *rcnr_A3 = calloc(1, sizeof(*rcnr_A3)); + rcnr_A3->reportType.present = NR_ReportConfigNR__reportType_PR_eventTriggered; + rcnr_A3->reportType.choice.eventTriggered = etrc_A3; + rc_A3->reportConfig.choice.reportConfigNR = rcnr_A3; + return rc_A3; +} + +const nr_a3_event_t *get_a3_configuration(int nr_cellid) +{ + gNB_RRC_INST *rrc = RC.nrrrc[0]; + nr_measurement_configuration_t *measurementConfiguration = &rrc->measurementConfiguration; + if (!measurementConfiguration->a3_event_list) + return NULL; + + for (uint8_t i = 0; i < measurementConfiguration->a3_event_list->size; i++) { + nr_a3_event_t *a3_event = (nr_a3_event_t *)seq_arr_at(measurementConfiguration->a3_event_list, i); + if (a3_event->cell_id == nr_cellid) + return a3_event; + } + + if (measurementConfiguration->is_default_a3_configuration_exists) + return get_a3_configuration(-1); + + return NULL; +} + +NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt, + int band, + int scs, + const nr_measurement_configuration_t *const measurementConfiguration, + const seq_arr_t *const neighbourConfiguration) +{ + if (!measurementConfiguration) + return NULL; + + if (!measurementConfiguration->a2_event && !measurementConfiguration->per_event && !measurementConfiguration->a3_event_list) { + LOG_D(NR_RRC, "NR Measurements are not configured in the conf file\n"); + return NULL; + } + + if (!measurementConfiguration->a2_event && !measurementConfiguration->per_event && measurementConfiguration->a3_event_list + && !neighbourConfiguration) { + LOG_I(NR_RRC, "A2 and Periodical Events are off. A3 Can not be prepared without neighbours!\n"); + return NULL; + } NR_MeasConfig_t *mc = calloc(1, sizeof(*mc)); mc->measObjectToAddModList = calloc(1, sizeof(*mc->measObjectToAddModList)); mc->reportConfigToAddModList = calloc(1, sizeof(*mc->reportConfigToAddModList)); + mc->measIdToAddModList = calloc(1, sizeof(*mc->measIdToAddModList)); + + if (measurementConfiguration->per_event) { + NR_ReportConfigToAddMod_t *rc_PER = prepare_periodic_event_report(measurementConfiguration->per_event); + asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_PER); + } + + if (measurementConfiguration->a2_event) { + LOG_D(NR_RRC, "HO LOG: Preparing A2 Event Measurement Configuration!\n"); + NR_ReportConfigToAddMod_t *rc_A2 = prepare_a2_event_report(measurementConfiguration->a2_event); + asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_A2); + } + + if (neighbourConfiguration && measurementConfiguration->a3_event_list && measurementConfiguration->a3_event_list->size > 0) { + /* Loop through neighbours and find related A3 configuration + If no related A3 but there is default add the default one. + If default one added once as a report, no need to add it again && duplication. + */ + LOG_D(NR_RRC, "HO LOG: Preparing A3 Event Measurement Configuration!\n"); + bool is_default_a3_added = false; + for (uint8_t neighbourIdx = 0; neighbourIdx < neighbourConfiguration->size; neighbourIdx++) { + const nr_neighbour_gnb_configuration_t *neighbourCell = + (const nr_neighbour_gnb_configuration_t *)seq_arr_at(neighbourConfiguration, neighbourIdx); + if (!neighbourCell->isIntraFrequencyNeighbour) + continue; + + const nr_a3_event_t *a3Event = get_a3_configuration(neighbourCell->nrcell_id); + if (!a3Event || is_default_a3_added) + continue; + + if (a3Event->cell_id == -1) + is_default_a3_added = true; + + NR_ReportConfigToAddMod_t *rc_A3 = prepare_a3_event_report(a3Event); + asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_A3); + } + } + + DevAssert(mt != NULL && mt->frequencyAndTiming != NULL); + const struct NR_MeasTiming__frequencyAndTiming *ft = mt->frequencyAndTiming; + const NR_SSB_MTC_t *ssb_mtc = &ft->ssb_MeasurementTimingConfiguration; // Measurement Objects: Specifies what is to be measured. For NR and inter-RAT E-UTRA measurements, this may include // cell-specific offsets, blacklisted cells to be ignored and whitelisted cells to consider for measurements. @@ -1122,62 +1287,48 @@ NR_MeasConfig_t *get_defaultMeasConfig(const NR_MeasTiming_t *mt, int band, int monr1->quantityConfigIndex = 1; monr1->ext1 = calloc(1, sizeof(*monr1->ext1)); asn1cCallocOne(monr1->ext1->freqBandIndicatorNR, band); + + if (neighbourConfiguration && measurementConfiguration->a3_event_list) { + for (uint8_t nCell = 0; nCell < neighbourConfiguration->size; nCell++) { + const nr_neighbour_gnb_configuration_t *neighbourCell = + (const nr_neighbour_gnb_configuration_t *)seq_arr_at(neighbourConfiguration, nCell); + if (!neighbourCell->isIntraFrequencyNeighbour) + continue; + + if (monr1->cellsToAddModList == NULL) { + monr1->cellsToAddModList = calloc(1, sizeof(*monr1->cellsToAddModList)); + } + + NR_CellsToAddMod_t *cell = calloc(1, sizeof(*cell)); + cell->physCellId = neighbourCell->physicalCellId; + ASN_SEQUENCE_ADD(&monr1->cellsToAddModList->list, cell); + } + } + mo1->measObject.choice.measObjectNR = monr1; asn1cSeqAdd(&mc->measObjectToAddModList->list, mo1); - - // Reporting Configuration: Specifies how reporting should be done. This could be periodic or event-triggered. - NR_ReportConfigToAddMod_t *rc = calloc(1, sizeof(*rc)); - rc->reportConfigId = 1; - rc->reportConfig.present = NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR; - NR_PeriodicalReportConfig_t *prc = calloc(1, sizeof(*prc)); - prc->rsType = NR_NR_RS_Type_ssb; - prc->reportInterval = NR_ReportInterval_ms1024; - prc->reportAmount = NR_PeriodicalReportConfig__reportAmount_infinity; - prc->reportQuantityCell.rsrp = true; - prc->reportQuantityCell.rsrq = true; - prc->reportQuantityCell.sinr = true; - prc->reportQuantityRS_Indexes = calloc(1, sizeof(*prc->reportQuantityRS_Indexes)); - prc->reportQuantityRS_Indexes->rsrp = true; - prc->reportQuantityRS_Indexes->rsrq = true; - prc->reportQuantityRS_Indexes->sinr = true; - asn1cCallocOne(prc->maxNrofRS_IndexesToReport, 4); - prc->maxReportCells = 4; - prc->includeBeamMeasurements = true; + // Preparation of measId + for (uint8_t reportIdx = 0; reportIdx < mc->reportConfigToAddModList->list.count; reportIdx++) { + const NR_ReportConfigId_t reportId = mc->reportConfigToAddModList->list.array[reportIdx]->reportConfigId; + NR_MeasIdToAddMod_t *measid = calloc(1, sizeof(NR_MeasIdToAddMod_t)); + measid->measId = reportIdx + 1; + measid->reportConfigId = reportId; + measid->measObjectId = 1; + asn1cSeqAdd(&mc->measIdToAddModList->list, measid); + } - NR_ReportConfigNR_t *rcnr = calloc(1, sizeof(*rcnr)); - rcnr->reportType.present = NR_ReportConfigNR__reportType_PR_periodical; - rcnr->reportType.choice.periodical = prc; - - - rc->reportConfig.choice.reportConfigNR = rcnr; - asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc); - - // Measurement ID: Identifies how to report measurements of a specific object. This is a many-to-many mapping: a - // measurement object could have multiple reporting configurations, a reporting configuration could apply to multiple - // objects. A unique ID is used for each object-to-report-config association. When UE sends a MeasurementReport - // message, a single ID and related measurements are included in the message. - mc->measIdToAddModList = calloc(1, sizeof(*mc->measIdToAddModList)); - NR_MeasIdToAddMod_t *measid = calloc(1, sizeof(*measid)); - measid->measId = 1; - measid->measObjectId = 1; - measid->reportConfigId = 1; - asn1cSeqAdd(&mc->measIdToAddModList->list, measid); - - // Quantity Configuration: Specifies parameters for layer 3 filtering of measurements. Only after filtering, reporting - // criteria are evaluated. The formula used is F_n = (1-a)F_(n-1) + a*M_n, where M is the latest measurement, F is the - // filtered measurement, and ais based on configured filter coefficient. mc->quantityConfig = calloc(1, sizeof(*mc->quantityConfig)); mc->quantityConfig->quantityConfigNR_List = calloc(1, sizeof(*mc->quantityConfig->quantityConfigNR_List)); - NR_QuantityConfigNR_t *qcnr3 = calloc(1, sizeof(*qcnr3)); - asn1cCallocOne(qcnr3->quantityConfigCell.ssb_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6); - asn1cCallocOne(qcnr3->quantityConfigCell.csi_RS_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6); - asn1cSeqAdd(&mc->quantityConfig->quantityConfigNR_List->list, qcnr3); + NR_QuantityConfigNR_t *qcnr = calloc(1, sizeof(*qcnr)); + asn1cCallocOne(qcnr->quantityConfigCell.ssb_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6); + asn1cCallocOne(qcnr->quantityConfigCell.csi_RS_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6); + asn1cSeqAdd(&mc->quantityConfig->quantityConfigNR_List->list, qcnr); return mc; } -void free_defaultMeasConfig(NR_MeasConfig_t *mc) +void free_MeasConfig(NR_MeasConfig_t *mc) { ASN_STRUCT_FREE(asn_DEF_NR_MeasConfig, mc); } diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index c53a541c90..0d8b4c547f 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -143,8 +143,13 @@ int do_RRCReestablishment(rrc_gNB_ue_context_t *const ue_context_pP, int do_RRCReestablishmentComplete(uint8_t *buffer, size_t buffer_size, int64_t rrc_TransactionIdentifier); -NR_MeasConfig_t *get_defaultMeasConfig(const NR_MeasTiming_t *mt, int band, int scs); -void free_defaultMeasConfig(NR_MeasConfig_t *mc); +const nr_a3_event_t *get_a3_configuration(int nr_cellid); +NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt, + int band, + int scs, + const nr_measurement_configuration_t *const measurementConfiguration, + const seq_arr_t *const neighbourConfiguration); +void free_MeasConfig(NR_MeasConfig_t *mc); int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi); #endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */ diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 7a4cb5d340..d22e25187d 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -141,6 +141,42 @@ static void freeDRBlist(NR_DRB_ToAddModList_t *list) return; } +const neighbour_cell_configuration_t *get_neighbour_config(int serving_cell_nr_cellid) +{ + const gNB_RRC_INST *rrc = RC.nrrrc[0]; + seq_arr_t *neighbour_cell_configuration = rrc->neighbour_cell_configuration; + if (!neighbour_cell_configuration) + return NULL; + + for (int cellIdx = 0; cellIdx < neighbour_cell_configuration->size; cellIdx++) { + neighbour_cell_configuration_t *neighbour_config = + (neighbour_cell_configuration_t *)seq_arr_at(neighbour_cell_configuration, cellIdx); + if (neighbour_config->nr_cell_id == serving_cell_nr_cellid) + return neighbour_config; + } + return NULL; +} + +const nr_neighbour_gnb_configuration_t *get_neighbour_cell_information(int serving_cell_nr_cellid, int neighbour_cell_phy_id) +{ + const gNB_RRC_INST *rrc = RC.nrrrc[0]; + seq_arr_t *neighbour_cell_configuration = rrc->neighbour_cell_configuration; + for (int cellIdx = 0; cellIdx < neighbour_cell_configuration->size; cellIdx++) { + neighbour_cell_configuration_t *neighbour_config = + (neighbour_cell_configuration_t *)seq_arr_at(neighbour_cell_configuration, cellIdx); + if (!neighbour_config) + continue; + + for (int neighbourIdx = 0; neighbourIdx < neighbour_config->neighbour_cells->size; neighbourIdx++) { + nr_neighbour_gnb_configuration_t *neighbour = + (nr_neighbour_gnb_configuration_t *)seq_arr_at(neighbour_config->neighbour_cells, neighbourIdx); + if (neighbour != NULL && neighbour->physicalCellId == neighbour_cell_phy_id) + return neighbour; + } + } + return NULL; +} + typedef struct deliver_dl_rrc_message_data_s { const gNB_RRC_INST *rrc; f1ap_dl_rrc_message_t *dl_rrc; @@ -504,14 +540,25 @@ void rrc_gNB_generate_dedicatedRRCReconfiguration(const protocol_ctxt_t *const c DevAssert(du != NULL); f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; NR_MeasConfig_t *measconfig = NULL; + if (du->mtc != NULL) { int scs = get_ssb_scs(cell_info); int band = get_dl_band(cell_info); const NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; const NR_MeasTiming_t *mt = mtlist->list.array[0]; - measconfig = get_defaultMeasConfig(mt, band, scs); + const neighbour_cell_configuration_t *neighbour_config = get_neighbour_config(cell_info->nr_cellid); + seq_arr_t *neighbour_cells = NULL; + if (neighbour_config) + neighbour_cells = neighbour_config->neighbour_cells; + + measconfig = get_MeasConfig(mt, band, scs, &rrc->measurementConfiguration, neighbour_cells); } + if (ue_p->measConfig) + free_MeasConfig(ue_p->measConfig); + + ue_p->measConfig = measconfig; + uint8_t buffer[RRC_BUF_SIZE] = {0}; NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false); NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, false); @@ -527,8 +574,7 @@ void rrc_gNB_generate_dedicatedRRCReconfiguration(const protocol_ctxt_t *const c measconfig, dedicatedNAS_MessageList, cellGroupConfig); - LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Reconfiguration\n"); - free_defaultMeasConfig(measconfig); + LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n"); freeSRBlist(SRBs); freeDRBlist(DRBs); ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, ue_p->DRB_ReleaseList); @@ -1148,14 +1194,9 @@ fallback_rrc_setup: return; } -static void rrc_gNB_process_MeasurementReport(rrc_gNB_ue_context_t *ue_context, NR_MeasurementReport_t *measurementReport) +static void process_Periodical_Measurement_Report(rrc_gNB_ue_context_t *ue_context, NR_MeasurementReport_t *measurementReport) { - if (LOG_DEBUGFLAG(DEBUG_ASN1)) - xer_fprint(stdout, &asn_DEF_NR_MeasurementReport, (void *)measurementReport); - - DevAssert(measurementReport->criticalExtensions.present == NR_MeasurementReport__criticalExtensions_PR_measurementReport - && measurementReport->criticalExtensions.choice.measurementReport != NULL); - + // LOG_I(NR_RRC, "Periodical Event Report! Do Nothing for now...\n"); gNB_RRC_UE_t *ue_ctxt = &ue_context->ue_context; ASN_STRUCT_FREE(asn_DEF_NR_MeasResults, ue_ctxt->measResults); ue_ctxt->measResults = NULL; @@ -1168,6 +1209,141 @@ static void rrc_gNB_process_MeasurementReport(rrc_gNB_ue_context_t *ue_context, measurementReport->criticalExtensions.choice.measurementReport = NULL; } +static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report, NR_MeasurementReport_t *measurementReport) +{ + NR_EventTriggerConfig_t *event_triggered = report->reportType.choice.eventTriggered; + + int servingCellRSRP = 0; + int neighbourCellRSRP = 0; + int servingCellId = -1; + + switch (event_triggered->eventId.present) { + case NR_EventTriggerConfig__eventId_PR_eventA2: + LOG_I(NR_RRC, "\nHO LOG: Event A2 (Serving becomes worse than threshold)\n"); + break; + + case NR_EventTriggerConfig__eventId_PR_eventA3: { + LOG_I(NR_RRC, "\nHO LOG: Event A3 Report - Neighbour Becomes Better than Serving!\n"); + const NR_MeasResults_t *measResults = &measurementReport->criticalExtensions.choice.measurementReport->measResults; + + for (int serving_cell_idx = 0; serving_cell_idx < measResults->measResultServingMOList.list.count; serving_cell_idx++) { + const NR_MeasResultServMO_t *meas_result_serv_MO = measResults->measResultServingMOList.list.array[serving_cell_idx]; + servingCellId = *(meas_result_serv_MO->measResultServingCell.physCellId); + if (meas_result_serv_MO->measResultServingCell.measResult.cellResults.resultsSSB_Cell) { + servingCellRSRP = *(meas_result_serv_MO->measResultServingCell.measResult.cellResults.resultsSSB_Cell->rsrp) - 157; + } else { + servingCellRSRP = *(meas_result_serv_MO->measResultServingCell.measResult.cellResults.resultsCSI_RS_Cell->rsrp) - 157; + } + LOG_D(NR_RRC, "Serving Cell RSRP: %d\n", servingCellRSRP); + } + + if (measResults->measResultNeighCells == NULL) + break; + + if (measResults->measResultNeighCells->present != NR_MeasResults__measResultNeighCells_PR_measResultListNR) + break; + + const NR_MeasResultListNR_t *measResultListNR = measResults->measResultNeighCells->choice.measResultListNR; + for (int neigh_meas_idx = 0; neigh_meas_idx < measResultListNR->list.count; neigh_meas_idx++) { + const NR_MeasResultNR_t *meas_result_neigh_cell = (measResultListNR->list.array[neigh_meas_idx]); + const int neighbourCellId = *(meas_result_neigh_cell->physCellId); + + // TS 138 133 Table 10.1.6.1-1: SS-RSRP and CSI-RSRP measurement report mapping + const struct NR_MeasResultNR__measResult__cellResults *cellResults = &(meas_result_neigh_cell->measResult.cellResults); + + if (cellResults->resultsSSB_Cell) { + neighbourCellRSRP = *(cellResults->resultsSSB_Cell->rsrp) - 157; + } else { + neighbourCellRSRP = *(cellResults->resultsCSI_RS_Cell->rsrp) - 157; + } + + LOG_I(NR_RRC, + "HO LOG: Measurement Report has came for the neighbour: %d with RSRP: %d\n", + neighbourCellId, + neighbourCellRSRP); + + const f1ap_served_cell_info_t *neighbour_cell_du_context = get_cell_information_by_phycellId(neighbourCellId); + const f1ap_served_cell_info_t *serving_cell_du_context = get_cell_information_by_phycellId(servingCellId); + const nr_neighbour_gnb_configuration_t *neighbour = + get_neighbour_cell_information(serving_cell_du_context->nr_cellid, neighbourCellId); + // CU does not have f1 connection with neighbour cell context. So check does serving cell has this phyCellId as a + // neighbour. + if (!neighbour_cell_du_context && neighbour) { + // No F1 connection but static neighbour configuration is available + const nr_a3_event_t *a3_event_configuration = get_a3_configuration(neighbour->nrcell_id); + // Additional check - This part can be modified according to additional cell specific Handover Margin + if (a3_event_configuration + && ((a3_event_configuration->a3_offset + a3_event_configuration->hysteresis) + < (neighbourCellRSRP - servingCellRSRP))) { + LOG_D(NR_RRC, "HO LOG: Trigger N2 HO for the neighbour gnb: %u cell: %lu\n", neighbour->gNB_ID, neighbour->nrcell_id); + } + } + } + + } break; + default: + LOG_D(NR_RRC, "NR_EventTriggerConfig__eventId_PR_NOTHING or Other event report\n"); + break; + } +} + +static void rrc_gNB_process_MeasurementReport(rrc_gNB_ue_context_t *ue_context, NR_MeasurementReport_t *measurementReport) +{ + LOG_D(NR_RRC, "Process Measurement Report\n"); + if (LOG_DEBUGFLAG(DEBUG_ASN1)) + xer_fprint(stdout, &asn_DEF_NR_MeasurementReport, (void *)measurementReport); + + DevAssert(measurementReport->criticalExtensions.present == NR_MeasurementReport__criticalExtensions_PR_measurementReport + && measurementReport->criticalExtensions.choice.measurementReport != NULL); + + gNB_RRC_UE_t *ue_ctxt = &ue_context->ue_context; + NR_MeasConfig_t *meas_config = ue_ctxt->measConfig; + if (meas_config == NULL) { + LOG_I(NR_RRC, "Unexpected Measurement Report from UE with id: %d\n", ue_ctxt->rrc_ue_id); + return; + } + + NR_MeasurementReport_IEs_t *measurementReport_IEs = measurementReport->criticalExtensions.choice.measurementReport; + const NR_MeasId_t measId = measurementReport_IEs->measResults.measId; + + NR_MeasIdToAddMod_t *meas_id_s = NULL; + for (int meas_idx = 0; meas_idx < meas_config->measIdToAddModList->list.count; meas_idx++) { + if (measId == meas_config->measIdToAddModList->list.array[meas_idx]->measId) { + meas_id_s = meas_config->measIdToAddModList->list.array[meas_idx]; + break; + } + } + + if (meas_id_s == NULL) { + LOG_E(NR_RRC, "Incoming Meas ID with id: %d Can not Found!\n", (int)measId); + return; + } + + LOG_D(NR_RRC, "HO LOG: Meas Id is found: %d\n", (int)meas_id_s->measId); + + struct NR_ReportConfigToAddMod__reportConfig *report_config = NULL; + for (int rep_id = 0; rep_id < meas_config->reportConfigToAddModList->list.count; rep_id++) { + if (meas_id_s->reportConfigId == meas_config->reportConfigToAddModList->list.array[rep_id]->reportConfigId) { + report_config = &meas_config->reportConfigToAddModList->list.array[rep_id]->reportConfig; + } + } + + if (report_config == NULL) { + LOG_E(NR_RRC, "There is no related report configuration for this measId!\n"); + return; + } + + if (report_config->choice.reportConfigNR->reportType.present == NR_ReportConfigNR__reportType_PR_periodical) + return process_Periodical_Measurement_Report(ue_context, measurementReport); + + if (report_config->choice.reportConfigNR->reportType.present != NR_ReportConfigNR__reportType_PR_eventTriggered) { + LOG_D(NR_RRC, "Incoming Report Type: %d is not supported! \n", report_config->choice.reportConfigNR->reportType.present); + return; + } + + process_Event_Based_Measurement_Report(report_config->choice.reportConfigNR, measurementReport); +} + static int handle_rrcReestablishmentComplete(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_p, const NR_RRCReestablishmentComplete_t *reestablishment_complete) @@ -1783,6 +1959,7 @@ static void rrc_delete_ue_data(gNB_RRC_UE_t *UE) ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr); ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); ASN_STRUCT_FREE(asn_DEF_NR_MeasResults, UE->measResults); + free_MeasConfig(UE->measConfig); } void rrc_remove_ue(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *ue_context_p)