From 11855a0bc14165534fe40dfb59f41912b23038e4 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 4 Jul 2024 14:25:11 +0200 Subject: [PATCH 01/26] increase UL pdu length to uint32 in similarity to what done on the gnb --- .../nfapi/public_inc/fapi_nr_ue_interface.h | 2 +- openair2/LAYER2/NR_MAC_UE/mac_proto.h | 2 +- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 34 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h index a98595fdd5..627aa909b8 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h @@ -162,7 +162,7 @@ typedef struct { } fapi_nr_tx_config_t; typedef struct { - uint16_t pdu_length; + uint32_t pdu_length; uint8_t* fapiTxPdu; } fapi_nr_tx_request_body_t; diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index 88973af849..1307c428df 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -190,7 +190,7 @@ uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, sub_frame_t subframe, uint8_t gNB_index, uint8_t *ulsch_buffer, - uint16_t buflen); + uint32_t buflen); void set_harq_status(NR_UE_MAC_INST_t *mac, uint8_t pucch_id, diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 20b0e80f61..651be20442 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -1399,7 +1399,7 @@ void nr_ue_ul_scheduler(NR_UE_MAC_INST_t *mac, nr_uplink_indication_t *ul_info) while (ulcfg_pdu->pdu_type != FAPI_NR_END) { uint8_t *ulsch_input_buffer = ulsch_input_buffer_array[number_of_pdus]; if (ulcfg_pdu->pdu_type == FAPI_NR_UL_CONFIG_TYPE_PUSCH) { - uint16_t TBS_bytes = ulcfg_pdu->pusch_config_pdu.pusch_data.tb_size; + uint32_t TBS_bytes = ulcfg_pdu->pusch_config_pdu.pusch_data.tb_size; LOG_D(NR_MAC, "harq_id %d, new_data_indicator %d, TBS_bytes %d (ra_state %d)\n", ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id, @@ -2748,7 +2748,7 @@ static int nr_ue_get_sdu_mac_ce_pre(NR_UE_MAC_INST_t *mac, sub_frame_t subframe, uint8_t gNB_index, uint8_t *ulsch_buffer, - uint16_t buflen, + uint32_t buflen, NR_UE_MAC_CE_INFO *mac_ce_p) { int num_lcg_id_with_data = 0; @@ -2811,11 +2811,11 @@ static void nr_ue_get_sdu_mac_ce_post(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, uint8_t *ulsch_buffer, - uint16_t buflen, + uint32_t buflen, NR_UE_MAC_CE_INFO *mac_ce_p) { // Compute BSR Values and update Nb LCGID with data after multiplexing - unsigned short padding_len = 0; + uint32_t padding_len = 0; int num_lcg_id_with_data = 0; int lcg_id_bsr_trunc = 0; NR_UE_SCHEDULING_INFO *sched_info = &mac->scheduling_info; @@ -2999,8 +2999,8 @@ uint32_t get_count_lcids_same_priority(uint8_t start, uint8_t total_active_lcids long get_num_bytes_to_reqlc(NR_UE_MAC_INST_t *mac, uint8_t same_priority_count, uint8_t lc_num, - uint16_t buflen_remain_ep, - int16_t buflen_remain, + uint32_t buflen_remain_ep, + int32_t buflen_remain, uint8_t round_id, uint32_t *bytes_read_fromlc, long *target) @@ -3016,7 +3016,7 @@ long get_num_bytes_to_reqlc(NR_UE_MAC_INST_t *mac, long num_remaining_bytes = 0; long num_bytes_requested = 0; if (round_id == 0) { // initial round - uint16_t pdu_remain = (same_priority_count > 1) ? buflen_remain_ep : buflen_remain; + uint32_t pdu_remain = (same_priority_count > 1) ? buflen_remain_ep : buflen_remain; num_bytes_requested = (pdu_remain < pbr) ? min(pdu_remain, lcid_remain_buffer) : min(pbr, lcid_remain_buffer); num_remaining_bytes = *target - bytes_read_fromlc[lc_num - 1]; num_bytes_requested = min(num_bytes_requested, num_remaining_bytes); @@ -3068,13 +3068,13 @@ static bool fill_mac_sdu(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, uint8_t gNB_index, - uint16_t buflen, - int16_t *buflen_remain, + uint32_t buflen, + int32_t *buflen_remain, int lcid, uint8_t **pdu, uint32_t *counter, uint8_t count_same_priority_lcids, - uint16_t buflen_ep, + uint32_t buflen_ep, uint32_t *lcids_bytes_tot, uint16_t *num_sdus, NR_UE_MAC_CE_INFO *mac_ce_p, @@ -3225,11 +3225,11 @@ uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, slot_t slot, uint8_t gNB_index, uint8_t *ulsch_buffer, - uint16_t buflen) + uint32_t buflen) { NR_UE_MAC_CE_INFO mac_ce_info; NR_UE_MAC_CE_INFO *mac_ce_p=&mac_ce_info; - int16_t buflen_remain = 0; + int32_t buflen_remain = 0; mac_ce_p->bsr_len = 0; mac_ce_p->bsr_ce_len = 0; mac_ce_p->bsr_header_len = 0; @@ -3260,7 +3260,7 @@ uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, // in the first run all the lc are allocated as per bj and prioritized bit rate but in subsequent runs, no need to consider uint32_t lcp_allocation_counter = 0; // bj and prioritized bit rate but just consider priority - uint16_t buflen_ep = 0; // this variable holds the length in bytes in mac pdu when multiple equal priority channels are present + uint32_t buflen_ep = 0; // this variable holds the length in bytes in mac pdu when multiple equal priority channels are present // because as per standard(TS38.321), all equal priority channels should be served equally // nr_ue_get_sdu_mac_ce_pre updates all mac_ce related header field related to length @@ -3382,12 +3382,12 @@ uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, // Compute final offset for padding and fill remainder of ULSCH with 0 if (buflen_remain > 0) { - LOG_D(NR_MAC, "In %s filling remainder %d bytes to the UL PDU \n", __FUNCTION__, buflen_remain); + LOG_D(NR_MAC, "Filling remainder %d bytes to the UL PDU \n", buflen_remain); ((NR_MAC_SUBHEADER_FIXED *) pdu)->R = 0; ((NR_MAC_SUBHEADER_FIXED *) pdu)->LCID = UL_SCH_LCID_PADDING; #ifdef ENABLE_MAC_PAYLOAD_DEBUG - LOG_I(NR_MAC, "In %s: padding MAC sub-header with length %ld bytes \n", __FUNCTION__, sizeof(NR_MAC_SUBHEADER_FIXED)); + LOG_I(NR_MAC, "Padding MAC sub-header with length %ld bytes \n", sizeof(NR_MAC_SUBHEADER_FIXED)); log_dump(NR_MAC, pdu, sizeof(NR_MAC_SUBHEADER_FIXED), LOG_DUMP_CHAR, "\n"); #endif @@ -3403,13 +3403,13 @@ uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, } #ifdef ENABLE_MAC_PAYLOAD_DEBUG - LOG_I(NR_MAC, "In %s: MAC padding sub-PDU with length %d bytes \n", __FUNCTION__, buflen_remain); + LOG_I(NR_MAC, "MAC padding sub-PDU with length %d bytes \n", buflen_remain); log_dump(NR_MAC, pdu, buflen_remain, LOG_DUMP_CHAR, "\n"); #endif } #ifdef ENABLE_MAC_PAYLOAD_DEBUG - LOG_I(NR_MAC, "In %s: dumping MAC PDU with length %d: \n", __FUNCTION__, buflen); + LOG_I(NR_MAC, "Dumping MAC PDU with length %d: \n", buflen); log_dump(NR_MAC, ulsch_buffer, buflen, LOG_DUMP_CHAR, "\n"); #endif From 5f754729cfd00acc770cea0a216b2986bf20cd57 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 5 Jul 2024 17:00:56 +0200 Subject: [PATCH 02/26] improve assertion for msg1_SubcarrierSpacing in UE init RA --- openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index 1fc6f4b6e8..0820c40f3c 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -74,7 +74,8 @@ void init_RA(NR_UE_MAC_INST_t *mac, fapi_nr_config_request_t *cfg = &mac->phy_config.config_req; prach_resources->RA_PREAMBLE_BACKOFF = 0; - AssertFatal(nr_rach_ConfigCommon, "Cannot handle scenario without nr_rach_ConfigCommon\n"); + AssertFatal(nr_rach_ConfigCommon && nr_rach_ConfigCommon->msg1_SubcarrierSpacing, + "Cannot handle yet the scenario without msg1_SubcarrierSpacing (L839)\n"); NR_SubcarrierSpacing_t prach_scs = *nr_rach_ConfigCommon->msg1_SubcarrierSpacing; int n_prbs = get_N_RA_RB(prach_scs, mac->current_UL_BWP->scs); int start_prb = rach_ConfigGeneric->msg1_FrequencyStart + mac->current_UL_BWP->BWPStart; From b14a783e865a38d639c9610abda5496977d79e98 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 9 Jul 2024 15:37:06 +0200 Subject: [PATCH 03/26] fix for wrong frequency in 5g_rfsimulator SA test --- .../5g_rfsimulator/docker-compose.yaml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml index e46e9b1ab4..06c945a250 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml @@ -109,7 +109,7 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001100 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001100 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time depends_on: - oai-gnb networks: @@ -133,7 +133,7 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001101 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001101 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time depends_on: - oai-gnb networks: @@ -157,7 +157,7 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001102 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001102 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time depends_on: - oai-gnb networks: @@ -181,7 +181,7 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001103 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001103 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time depends_on: - oai-gnb networks: @@ -205,7 +205,7 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001104 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001104 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time depends_on: - oai-gnb networks: @@ -229,7 +229,7 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001105 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001105 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time depends_on: - oai-gnb networks: @@ -253,7 +253,7 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001106 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001106 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time depends_on: - oai-gnb networks: @@ -277,7 +277,7 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001107 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001107 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time depends_on: - oai-gnb networks: @@ -301,7 +301,7 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001108 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001108 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time depends_on: - oai-gnb networks: @@ -325,7 +325,7 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001109 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: -E --sa --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001109 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time depends_on: - oai-gnb networks: From 26175c5413219f26d299d242ebdbd29796b23e4a Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 7 May 2024 19:49:35 +0200 Subject: [PATCH 04/26] NR UE: add NTN specific parameter cellSpecificKoffset_r17 and use it for UL scheduling According to the RRC specification, cellSpecificKoffset is: Scheduling offset used for the timing relationships that are modified for NTN (see TS 38.213 [13]). The unit of the field K_offset is number of slots for a given subcarrier spacing of 15 kHz. If the field is absent UE assumes value 0 This parameter `cellSpecificKoffset_r17` can be set via UE command line parameter `--ntn-koffset`. --- executables/nr-uesoftmodem.h | 5 +- openair2/LAYER2/NR_MAC_COMMON/nr_mac.h | 1 + openair2/LAYER2/NR_MAC_UE/config_ue.c | 18 +++++++ openair2/LAYER2/NR_MAC_UE/mac_proto.h | 5 +- openair2/LAYER2/NR_MAC_UE/main_ue_nr.c | 18 ++++++- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 52 ++++++++++++++------ openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 10 ++-- 7 files changed, 84 insertions(+), 25 deletions(-) diff --git a/executables/nr-uesoftmodem.h b/executables/nr-uesoftmodem.h index 28de10154d..c95a6dc98c 100644 --- a/executables/nr-uesoftmodem.h +++ b/executables/nr-uesoftmodem.h @@ -12,6 +12,7 @@ #define CONFIG_HLP_DLSCH_PARA "number of threads for dlsch processing 0 for no parallelization\n" #define CONFIG_HLP_OFFSET_DIV "Divisor for computing OFDM symbol offset in Rx chain (num samples in CP/). Default value is 8. To set the sample offset to 0, set this value ~ 10e6\n" #define CONFIG_HLP_MAX_LDPC_ITERATIONS "Maximum LDPC decoder iterations\n" +#define CONFIG_HLP_NTN_KOFFSET "NTN cellSpecificKoffset-r17 (number of slots for a given subcarrier spacing of 15 kHz)\n" /***************************************************************************************************************************************/ /* command line options definitions, CMDLINE_XXXX_DESC macros are used to initialize paramdef_t arrays which are then used as argument @@ -63,7 +64,8 @@ {"chest-time", CONFIG_HLP_CHESTTIME, 0, .iptr=&(nrUE_params.chest_time), .defintval=0, TYPE_INT, 0}, \ {"ue-timing-correction-disable", CONFIG_HLP_DISABLETIMECORR, PARAMFLAG_BOOL, .iptr=&(nrUE_params.no_timing_correction), .defintval=0, TYPE_INT, 0}, \ {"SLC", CONFIG_HLP_SLF, 0, .u64ptr=&(sidelink_frequency[0][0]), .defuintval=2600000000,TYPE_UINT64,0}, \ - {"num-ues", NULL, 0, .iptr=&(NB_UE_INST), .defuintval=1, TYPE_INT,0}, \ + {"num-ues", NULL, 0, .iptr=&(NB_UE_INST), .defuintval=1, TYPE_INT, 0}, \ + {"ntn-koffset", CONFIG_HLP_NTN_KOFFSET, 0, .uptr=&(nrUE_params.ntn_koffset), .defuintval=0, TYPE_UINT, 0}, \ } // clang-format on @@ -84,6 +86,7 @@ typedef struct { int N_RB_DL; int ssb_start_subcarrier; int ldpc_offload_flag; + unsigned int ntn_koffset; } nrUE_params_t; extern uint64_t get_nrUE_optmask(void); extern uint64_t set_nrUE_optmask(uint64_t bitmask); diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h index 6973f06103..46ef4050e9 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h @@ -597,6 +597,7 @@ typedef struct { int n_ul_bwp; int dl_bw_tbslbrm; int ul_bw_tbslbrm; + NR_NTN_Config_r17_t *ntn_Config_r17; } NR_UE_ServingCell_Info_t; typedef enum { diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index d8b1ac158d..ccff0020c3 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -409,6 +409,13 @@ static void config_common_ue(NR_UE_MAC_INST_t *mac, // prach_fd_occasion->num_unused_root_sequences = ??? } } + + // NTN Config + if (scc->ext2) { + UPDATE_IE(mac->sc_info.ntn_Config_r17, scc->ext2->ntn_Config_r17, NR_NTN_Config_r17_t); + } else { + asn1cFreeStruc(asn_DEF_NR_NTN_Config_r17, mac->sc_info.ntn_Config_r17); + } } void release_common_ss_cset(NR_BWP_PDCCH_t *pdcch) @@ -1585,6 +1592,17 @@ void nr_rrc_mac_config_req_sib1(module_id_t module_id, mac->phy_config_request_sent = true; } +void nr_rrc_mac_config_req_sib19_r17(module_id_t module_id, + NR_SIB19_r17_t *sib19_r17) +{ + NR_UE_MAC_INST_t *mac = get_mac_inst(module_id); + + // ntn-Config-r17 + UPDATE_IE(mac->sc_info.ntn_Config_r17, sib19_r17->ntn_Config_r17, NR_NTN_Config_r17_t); + + // TODO handle other SIB19 elements +} + static void handle_reconfiguration_with_sync(NR_UE_MAC_INST_t *mac, int cc_idP, const NR_ReconfigurationWithSync_t *reconfigurationWithSync) diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index 6b849415f8..79a367de4c 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -90,6 +90,9 @@ void nr_rrc_mac_config_req_sib1(module_id_t module_id, NR_SI_SchedulingInfo_t *si_SchedulingInfo, NR_ServingCellConfigCommonSIB_t *scc); +void nr_rrc_mac_config_req_sib19_r17(module_id_t module_id, + NR_SIB19_r17_t *sib19_r17); + void nr_rrc_mac_config_req_reset(module_id_t module_id, NR_UE_MAC_reset_cause_t cause); /**\brief initialization NR UE MAC instance(s)*/ @@ -196,7 +199,7 @@ void set_harq_status(NR_UE_MAC_INST_t *mac, uint8_t pucch_id, uint8_t harq_id, int8_t delta_pucch, - uint8_t data_toul_fb, + uint16_t data_toul_fb, uint8_t dai, int n_CCE, int N_CCE, diff --git a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c index 4d8b79a2ab..2230d92729 100644 --- a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c +++ b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c @@ -35,7 +35,7 @@ #include "radio/COMMON/common_lib.h" //#undef MALLOC #include "assertions.h" -#include "executables/softmodem-common.h" +#include "executables/nr-uesoftmodem.h" #include "nr_rlc/nr_rlc_oai_api.h" #include "RRC/NR_UE/rrc_proto.h" #include @@ -84,6 +84,21 @@ void nr_ue_init_mac(NR_UE_MAC_INST_t *mac) memset(&mac->scheduling_info.sr_info[i], 0, sizeof(mac->scheduling_info.sr_info[i])); mac->pucch_power_control_initialized = false; + + // Fake SIB19 reception for NTN + // TODO: remove this and implement the actual SIB19 reception instead! + if (get_nrUE_params()->ntn_koffset) { + NR_SIB19_r17_t *sib19_r17 = calloc(1, sizeof(*sib19_r17)); + sib19_r17->ntn_Config_r17 = calloc(1, sizeof(*sib19_r17->ntn_Config_r17)); + + // NTN cellSpecificKoffset-r17 + if (get_nrUE_params()->ntn_koffset) { + asn1cCallocOne(sib19_r17->ntn_Config_r17->cellSpecificKoffset_r17, get_nrUE_params()->ntn_koffset); + } + + nr_rrc_mac_config_req_sib19_r17(mac->ue_id, sib19_r17); + asn1cFreeStruc(asn_DEF_NR_SIB19_r17, sib19_r17); + } } void nr_ue_mac_default_configs(NR_UE_MAC_INST_t *mac) @@ -246,6 +261,7 @@ void release_mac_configuration(NR_UE_MAC_INST_t *mac, asn1cFreeStruc(asn_DEF_NR_PUSCH_CodeBlockGroupTransmission, sc->pusch_CGB_Transmission); asn1cFreeStruc(asn_DEF_NR_CSI_MeasConfig, sc->csi_MeasConfig); asn1cFreeStruc(asn_DEF_NR_CSI_AperiodicTriggerStateList, sc->aperiodicTriggerStateList); + asn1cFreeStruc(asn_DEF_NR_NTN_Config_r17, sc->ntn_Config_r17); free(sc->xOverhead_PDSCH); free(sc->nrofHARQ_ProcessesForPDSCH); free(sc->rateMatching_PUSCH); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 2954cf246b..af148e6bd5 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -148,6 +148,13 @@ static nr_dci_format_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, const uint8_t *dci_pdu, const int slot); +static int get_NTN_UE_Koffset(NR_NTN_Config_r17_t *ntn_Config_r17, int scs) +{ + if (ntn_Config_r17 && ntn_Config_r17->cellSpecificKoffset_r17) + return *ntn_Config_r17->cellSpecificKoffset_r17 << scs; + return 0; +} + int get_rnti_type(const NR_UE_MAC_INST_t *mac, const uint16_t rnti) { const RA_config_t *ra = &mac->ra; @@ -492,7 +499,8 @@ static int nr_ue_process_dci_ul_00(NR_UE_MAC_INST_t *mac, frame_t frame_tx; int slot_tx; - if (-1 == nr_ue_pusch_scheduler(mac, 0, frame, slot, &frame_tx, &slot_tx, tda_info.k2)) { + const int ntn_ue_koffset = get_NTN_UE_Koffset(mac->sc_info.ntn_Config_r17, mac->current_UL_BWP->scs); + if (-1 == nr_ue_pusch_scheduler(mac, 0, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset)) { LOG_E(MAC, "Cannot schedule PUSCH\n"); return -1; } @@ -589,7 +597,8 @@ static int nr_ue_process_dci_ul_01(NR_UE_MAC_INST_t *mac, tda_info.k2 = csi_K2; } - if (-1 == nr_ue_pusch_scheduler(mac, 0, frame, slot, &frame_tx, &slot_tx, tda_info.k2)) { + const int ntn_ue_koffset = get_NTN_UE_Koffset(mac->sc_info.ntn_Config_r17, mac->current_UL_BWP->scs); + if (-1 == nr_ue_pusch_scheduler(mac, 0, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset)) { LOG_E(MAC, "Cannot schedule PUSCH\n"); return -1; } @@ -903,10 +912,15 @@ static int nr_ue_process_dci_dl_10(NR_UE_MAC_INST_t *mac, return -1; } + /* PDSCH_TO_HARQ_FEEDBACK_TIME_IND */ + // according to TS 38.213 9.2.3 + const int ntn_ue_koffset = get_NTN_UE_Koffset(mac->sc_info.ntn_Config_r17, dlsch_pdu->SubcarrierSpacing); + const uint16_t feedback_ti = 1 + dci->pdsch_to_harq_feedback_timing_indicator.val + ntn_ue_koffset; + if (rnti_type != TYPE_RA_RNTI_ && rnti_type != TYPE_SI_RNTI_) { - AssertFatal(1 + dci->pdsch_to_harq_feedback_timing_indicator.val > DURATION_RX_TO_TX, + AssertFatal(feedback_ti > DURATION_RX_TO_TX, "PDSCH to HARQ feedback time (%d) needs to be higher than DURATION_RX_TO_TX (%d).\n", - 1 + dci->pdsch_to_harq_feedback_timing_indicator.val, + feedback_ti, DURATION_RX_TO_TX); // set the harq status at MAC for feedback const int tpc[] = {-1, 0, 1, 3}; @@ -914,7 +928,7 @@ static int nr_ue_process_dci_dl_10(NR_UE_MAC_INST_t *mac, dci->pucch_resource_indicator, dci->harq_pid, tpc[dci->tpc], - 1 + dci->pdsch_to_harq_feedback_timing_indicator.val, + feedback_ti, dci->dai[0].val, dci_ind->n_CCE, dci_ind->N_CCE, @@ -948,9 +962,9 @@ static int nr_ue_process_dci_dl_10(NR_UE_MAC_INST_t *mac, dlsch_pdu->scaling_factor_S, dci->tpc, dci->pucch_resource_indicator, - 1 + dci->pdsch_to_harq_feedback_timing_indicator.val); + feedback_ti); - dlsch_pdu->k1_feedback = 1 + dci->pdsch_to_harq_feedback_timing_indicator.val; + dlsch_pdu->k1_feedback = feedback_ti; LOG_D(MAC, "(nr_ue_procedures.c) pdu_type=%d\n\n", dl_conf_req->pdu_type); @@ -1239,7 +1253,8 @@ static int nr_ue_process_dci_dl_11(NR_UE_MAC_INST_t *mac, /* PDSCH_TO_HARQ_FEEDBACK_TIME_IND */ // according to TS 38.213 Table 9.2.3-1 - uint8_t feedback_ti = pucch_Config->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0]; + const int ntn_ue_koffset = get_NTN_UE_Koffset(mac->sc_info.ntn_Config_r17, dlsch_pdu->SubcarrierSpacing); + const uint16_t feedback_ti = pucch_Config->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0] + ntn_ue_koffset; AssertFatal(feedback_ti > DURATION_RX_TO_TX, "PDSCH to HARQ feedback time (%d) needs to be higher than DURATION_RX_TO_TX (%d). Min feedback time set in config " @@ -1436,7 +1451,7 @@ void set_harq_status(NR_UE_MAC_INST_t *mac, uint8_t pucch_id, uint8_t harq_id, int8_t delta_pucch, - uint8_t data_toul_fb, + uint16_t data_toul_fb, uint8_t dai, int n_CCE, int N_CCE, @@ -1457,7 +1472,7 @@ void set_harq_status(NR_UE_MAC_INST_t *mac, current_harq->ul_frame = frame; current_harq->ul_slot = slot + data_toul_fb; if (current_harq->ul_slot >= slots_per_frame) { - current_harq->ul_frame = (frame + 1) % 1024; + current_harq->ul_frame = (frame + current_harq->ul_slot / slots_per_frame) % MAX_FRAME_NUMBER; current_harq->ul_slot %= slots_per_frame; } // counter DAI in DCI ranges from 0 to 3 @@ -3544,9 +3559,13 @@ void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_i NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &mac->ul_time_alignment; ul_time_alignment->tag_id = tag; ul_time_alignment->ta_command = ta; - ul_time_alignment->frame = frameP; - ul_time_alignment->slot = slot; ul_time_alignment->ta_apply = adjustment_ta; + + const int ntn_ue_koffset = get_NTN_UE_Koffset(mac->sc_info.ntn_Config_r17, mac->current_UL_BWP->scs); + const int n_slots_frame = nr_slots_per_frame[mac->current_UL_BWP->scs]; + ul_time_alignment->frame = (frameP + (slot + ntn_ue_koffset) / n_slots_frame) % MAX_FRAME_NUMBER; + ul_time_alignment->slot = (slot + ntn_ue_koffset) % n_slots_frame; + /* #ifdef DEBUG_HEADER_PARSING LOG_D(MAC, "[UE] CE %d : UE Timing Advance : %d\n", i, pduP[1]); @@ -4023,9 +4042,12 @@ static void nr_ue_process_rar(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *d LOG_E(MAC, "Cannot schedule Msg3. Something wrong in TDA information\n"); return; } - ret = nr_ue_pusch_scheduler(mac, is_Msg3, frame, slot, &frame_tx, &slot_tx, tda_info.k2); - ul_time_alignment->frame = frame_tx; - ul_time_alignment->slot = slot_tx; + const int ntn_ue_koffset = get_NTN_UE_Koffset(mac->sc_info.ntn_Config_r17, mac->current_UL_BWP->scs); + ret = nr_ue_pusch_scheduler(mac, is_Msg3, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset); + + const int n_slots_frame = nr_slots_per_frame[current_UL_BWP->scs]; + ul_time_alignment->frame = (frame_tx + (slot_tx + ntn_ue_koffset) / n_slots_frame) % MAX_FRAME_NUMBER; + ul_time_alignment->slot = (slot_tx + ntn_ue_koffset) % n_slots_frame; if (ret != -1) { uint16_t rnti = mac->crnti; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index b88ca7e5f3..a5c7982ddf 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -1112,7 +1112,7 @@ void nr_ue_aperiodic_srs_scheduling(NR_UE_MAC_INST_t *mac, long resource_trigger LOG_E(NR_MAC, "Slot for scheduling aperiodic SRS %d is not an UL slot\n", sched_slot); return; } - int sched_frame = frame + (slot + slot_offset >= n_slots_frame) % 1024; + int sched_frame = frame + (slot + slot_offset / n_slots_frame) % MAX_FRAME_NUMBER; fapi_nr_ul_config_request_pdu_t *pdu = lockGet_ul_config(mac, sched_frame, sched_slot, FAPI_NR_UL_CONFIG_TYPE_SRS); if (!pdu) return; @@ -1571,11 +1571,7 @@ int nr_ue_pusch_scheduler(const NR_UE_MAC_INST_t *mac, DURATION_RX_TO_TX); *slot_tx = (current_slot + k2 + delta) % nr_slots_per_frame[mu]; - if (current_slot + k2 + delta >= nr_slots_per_frame[mu]){ - *frame_tx = (current_frame + 1) % 1024; - } else { - *frame_tx = current_frame; - } + *frame_tx = (current_frame + (current_slot + k2 + delta) / nr_slots_per_frame[mu]) % MAX_FRAME_NUMBER; } else { @@ -1593,7 +1589,7 @@ int nr_ue_pusch_scheduler(const NR_UE_MAC_INST_t *mac, // Calculate TX slot and frame *slot_tx = (current_slot + k2) % nr_slots_per_frame[mu]; - *frame_tx = ((current_slot + k2) > (nr_slots_per_frame[mu]-1)) ? (current_frame + 1) % 1024 : current_frame; + *frame_tx = (current_frame + (current_slot + k2) / nr_slots_per_frame[mu]) % MAX_FRAME_NUMBER; } From 910d37dd7aca370ecb6478b62f98824009033c30 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 10 Jul 2024 15:08:32 +0200 Subject: [PATCH 05/26] handling BWP for DCI 00 reception depending on SS type according to the standard --- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index b88ca7e5f3..f640dbe2c7 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -662,8 +662,13 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->pusch_uci.harq_ack_bit_length = 0; - pusch_config_pdu->bwp_start = current_UL_BWP->BWPStart; - pusch_config_pdu->bwp_size = current_UL_BWP->BWPSize; + if (dci_format == NR_UL_DCI_FORMAT_0_0 && ss_type == NR_SearchSpace__searchSpaceType_PR_common) { + pusch_config_pdu->bwp_start = sc_info->initial_ul_BWPStart; + pusch_config_pdu->bwp_size = sc_info->initial_ul_BWPSize; + } else { + pusch_config_pdu->bwp_start = current_UL_BWP->BWPStart; + pusch_config_pdu->bwp_size = current_UL_BWP->BWPSize; + } pusch_config_pdu->start_symbol_index = tda_info->startSymbolIndex; pusch_config_pdu->nr_of_symbols = tda_info->nrOfSymbols; @@ -753,7 +758,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, if (nr_ue_process_dci_freq_dom_resource_assignment(pusch_config_pdu, NULL, NULL, - current_UL_BWP->BWPSize, + pusch_config_pdu->bwp_size, 0, 0, dci->frequency_domain_assignment) < 0) { @@ -773,12 +778,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, /* MCS TABLE */ long *mcs_table_config = pusch_Config ? (tp_enabled ? pusch_Config->mcs_TableTransformPrecoder : pusch_Config->mcs_Table) : NULL; - pusch_config_pdu->mcs_table = get_pusch_mcs_table(mcs_table_config, - tp_enabled, - dci_format, - rnti_type, - ss_type, - false); + pusch_config_pdu->mcs_table = get_pusch_mcs_table(mcs_table_config, tp_enabled, dci_format, rnti_type, ss_type, false); /* NDI */ NR_UL_HARQ_INFO_t *harq = &mac->ul_harq_info[dci->harq_pid]; From 021f97cf10adffb15397b1d9ae05321d78ede337 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 7 May 2024 19:53:54 +0200 Subject: [PATCH 06/26] NR UE: increase DURATION_RX_TO_TX by cellSpecificKoffset_r17 --- executables/nr-uesoftmodem.c | 6 ++++++ openair1/PHY/INIT/nr_parms.c | 6 +++--- openair1/PHY/defs_nr_common.h | 2 +- openair1/PHY/impl_defs_top.h | 21 ++++++++++++++++----- openair1/SIMULATION/NR_PHY/dlsim.c | 3 +++ openair1/SIMULATION/NR_PHY/ulsim.c | 3 +++ 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 262f0b1631..047ef6c557 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -159,6 +159,9 @@ int oaisim_flag = 0; int emulate_rf = 0; uint32_t N_RB_DL = 106; +// NTN cellSpecificKoffset-r17, but in slots for DL SCS +unsigned int NTN_UE_Koffset = 0; + /* see file openair2/LAYER2/MAC/main.c for why abstraction_flag is needed * this is very hackish - find a proper solution */ @@ -591,6 +594,9 @@ int main(int argc, char **argv) } } + // NTN cellSpecificKoffset-r17, but in slots for DL SCS + NTN_UE_Koffset = nrUE_params.ntn_koffset << PHY_vars_UE_g[0][0]->frame_parms.numerology_index; + init_openair0(); lock_memory_to_ram(); diff --git a/openair1/PHY/INIT/nr_parms.c b/openair1/PHY/INIT/nr_parms.c index 4fc91037b8..6a2c300b72 100644 --- a/openair1/PHY/INIT/nr_parms.c +++ b/openair1/PHY/INIT/nr_parms.c @@ -262,15 +262,15 @@ uint32_t get_slot_from_timestamp(openair0_timestamp timestamp_rx, const NR_DL_FR return slot_idx; } -uint32_t get_samples_slot_timestamp(int slot, const NR_DL_FRAME_PARMS *fp, uint8_t sl_ahead) +uint32_t get_samples_slot_timestamp(int slot, const NR_DL_FRAME_PARMS *fp, unsigned int sl_ahead) { uint32_t samp_count = 0; if(!sl_ahead) { - for(uint8_t idx_slot = 0; idx_slot < slot; idx_slot++) + for(unsigned int idx_slot = 0; idx_slot < slot; idx_slot++) samp_count += fp->get_samples_per_slot(idx_slot, fp); } else { - for(uint8_t idx_slot = slot; idx_slot < slot+sl_ahead; idx_slot++) + for(unsigned int idx_slot = slot; idx_slot < slot+sl_ahead; idx_slot++) samp_count += fp->get_samples_per_slot(idx_slot, fp); } return samp_count; diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h index 3d0f2efa88..9c2dff22b8 100644 --- a/openair1/PHY/defs_nr_common.h +++ b/openair1/PHY/defs_nr_common.h @@ -135,7 +135,7 @@ typedef struct NR_DL_FRAME_PARMS NR_DL_FRAME_PARMS; typedef uint32_t (*get_samples_per_slot_t)(int slot, const NR_DL_FRAME_PARMS *fp); typedef uint32_t (*get_slot_from_timestamp_t)(openair0_timestamp timestamp_rx, const NR_DL_FRAME_PARMS *fp); -typedef uint32_t (*get_samples_slot_timestamp_t)(int slot, const NR_DL_FRAME_PARMS *fp, uint8_t sl_ahead); +typedef uint32_t (*get_samples_slot_timestamp_t)(int slot, const NR_DL_FRAME_PARMS *fp, unsigned int sl_ahead); struct NR_DL_FRAME_PARMS { /// frequency range diff --git a/openair1/PHY/impl_defs_top.h b/openair1/PHY/impl_defs_top.h index c06cd13ff7..618a7e6c58 100644 --- a/openair1/PHY/impl_defs_top.h +++ b/openair1/PHY/impl_defs_top.h @@ -268,13 +268,24 @@ #define TDD_CONFIG_NB_FRAMES (2) #define NR_MAX_SLOTS_PER_FRAME (160) /* number of slots per frame */ -/* FFS_NR_TODO it defines ue capability which is the number of slots */ -/* - between reception of pdsch and tarnsmission of its acknowlegment */ -/* - between reception of un uplink grant and its related transmission */ -// should be 2 as per NR standard, but current UE is not able to perform this value +/* FFS_NR_TODO it defines ue capability which is the number of slots */ +/* - between reception of pdsch and transmission of its acknowlegment (k1) */ +/* - between reception of un uplink grant and its related transmission (k2) */ #define NR_UE_CAPABILITY_SLOT_RX_TO_TX (3) -#define DURATION_RX_TO_TX (NR_UE_CAPABILITY_SLOT_RX_TO_TX) +/* When the OAI UE receives RX slot N, it starts sending TX slot N+DURATION_RX_TO_TX. + * Therefore DURATION_RX_TO_TX must not be larger than the minimum k1 and k2 values (NR_UE_CAPABILITY_SLOT_RX_TO_TX). + * In case of NTN, the propagation delay is so large, that the TX slot needs to be transmitted far in advance. + * Therefore, the NTN_UE_Koffset is added to DURATION_RX_TO_TX. + * + * Note: currently, the UE requires this to be a constant. + * But in case of NTN, Koffset is only known after receiving SIB19. + * Therefore, support should be added to allow changing DURATION_RX_TO_TX on reception of SIB19 (via FAPI-like interface). + * E.g. no transmission before successful reception of SIB19, and re-sync with disabled transmission if Koffset changes. + * When this has been implemented, the global variable NTN_UE_Koffset should be removed, too. + */ +extern unsigned int NTN_UE_Koffset; +#define DURATION_RX_TO_TX (NR_UE_CAPABILITY_SLOT_RX_TO_TX + NTN_UE_Koffset) #define NR_MAX_ULSCH_HARQ_PROCESSES (NR_MAX_HARQ_PROCESSES) /* cf 38.214 6.1 UE procedure for receiving the physical uplink shared channel */ #define NR_MAX_DLSCH_HARQ_PROCESSES (NR_MAX_HARQ_PROCESSES) /* cf 38.214 5.1 UE procedure for receiving the physical downlink shared channel */ diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 728163e049..d6be222a9b 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -95,6 +95,9 @@ nfapi_ue_release_request_body_t release_rntis; instance_t DUuniqInstance=0; instance_t CUuniqInstance=0; +// NTN cellSpecificKoffset-r17, but in slots for DL SCS +unsigned int NTN_UE_Koffset = 0; + void nr_derive_key_ng_ran_star(uint16_t pci, uint64_t nr_arfcn_dl, const uint8_t key[32], uint8_t *key_ng_ran_star) { } diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 822153fd14..e0cf501e96 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -93,6 +93,9 @@ nfapi_ue_release_request_body_t release_rntis; instance_t DUuniqInstance=0; instance_t CUuniqInstance=0; +// NTN cellSpecificKoffset-r17, but in slots for DL SCS +unsigned int NTN_UE_Koffset = 0; + void nr_derive_key_ng_ran_star(uint16_t pci, uint64_t nr_arfcn_dl, const uint8_t key[32], uint8_t *key_ng_ran_star) { } From 676ba4355cf4ed232aade3f4f2d72f5b0c2e3256 Mon Sep 17 00:00:00 2001 From: Teodora Date: Wed, 10 Jul 2024 16:06:58 +0200 Subject: [PATCH 07/26] RRC_CONNECTED state signaling for E2 moved - E2SM UE ID requires guami -> therefore RRC_CONNECTED state signaling to RIC is now done after receiving Initial Context Setup Request from AMF, which contains the guami info --- openair2/RRC/NR/rrc_gNB.c | 4 ---- openair2/RRC/NR/rrc_gNB_NGAP.c | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 266728fd54..4174eff42a 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -487,10 +487,6 @@ static void rrc_gNB_process_RRCSetupComplete(const protocol_ctxt_t *const ctxt_p AssertFatal(ctxt_pP->rntiMaybeUEid == ue_context_pP->ue_context.rrc_ue_id, "logic bug: inconsistent IDs, must use CU UE ID!\n"); rrc_gNB_send_NGAP_NAS_FIRST_REQ(ctxt_pP, ue_context_pP, rrcSetupComplete); - -#ifdef E2_AGENT - signal_rrc_state_changed_to(&ue_context_pP->ue_context, RC_SM_RRC_CONNECTED); -#endif } //----------------------------------------------------------------------------- diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 003db92ab7..e1395ce6cd 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -491,6 +491,10 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t UE->initial_pdus[i] = req->pdusession_param[i]; } +#ifdef E2_AGENT + signal_rrc_state_changed_to(UE, RC_SM_RRC_CONNECTED); +#endif + return 0; } From 83c59b05e9160fd69332ec7ed24a2c0b708732a5 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 10 Jul 2024 18:41:11 +0200 Subject: [PATCH 08/26] code cleanup and small fixes --- openair1/PHY/CODING/nr_rate_matching.c | 4 +- openair1/PHY/NR_REFSIG/dmrs_nr.c | 6 +-- openair1/PHY/NR_REFSIG/dmrs_nr.h | 2 +- openair1/PHY/NR_TRANSPORT/nr_scrambling.c | 14 +++---- openair1/SIMULATION/NR_PHY/ulsim.c | 17 +++++++-- .../NR_MAC_COMMON/nr_compute_tbs_common.c | 13 +++++-- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 38 ++++++++++--------- 7 files changed, 54 insertions(+), 40 deletions(-) diff --git a/openair1/PHY/CODING/nr_rate_matching.c b/openair1/PHY/CODING/nr_rate_matching.c index 5a6d2885a5..1ed520cf12 100644 --- a/openair1/PHY/CODING/nr_rate_matching.c +++ b/openair1/PHY/CODING/nr_rate_matching.c @@ -452,7 +452,7 @@ int nr_rate_matching_ldpc(uint32_t Tbslbrm, ind = (index_k0[BG-1][rvidx]*Ncb/N)*Z; #ifdef RM_DEBUG - printf("nr_rate_matching_ldpc: E %u, F %u, Foffset %u, k0 %u, Ncb %u, rvidx %d, Tbslbrm %u\n", E, F, Foffset,ind, Ncb, rvidx, Tbslbrm); + printf("nr_rate_matching_ldpc: E %u, F %u, Foffset %u, k0 %u, Ncb %u, rvidx %d, Tbslbrm %u\n", E, F, Foffset, ind, Ncb, rvidx, Tbslbrm); #endif if (Foffset > E) { @@ -548,7 +548,7 @@ int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm, } #ifdef RM_DEBUG - printf("nr_rate_matching_ldpc_rx: Clear %d, E %u, k0 %u, Ncb %u, rvidx %d, Tbslbrm %u\n", clear, E, ind, Ncb, rvidx, Tbslbrm); + printf("nr_rate_matching_ldpc_rx: Clear %d, E %u, Foffset %u, k0 %u, Ncb %u, rvidx %d, Tbslbrm %u\n", clear, E, Foffset, ind, Ncb, rvidx, Tbslbrm); #endif if (clear == 1) diff --git a/openair1/PHY/NR_REFSIG/dmrs_nr.c b/openair1/PHY/NR_REFSIG/dmrs_nr.c index 4576f322f3..377eb4d89b 100644 --- a/openair1/PHY/NR_REFSIG/dmrs_nr.c +++ b/openair1/PHY/NR_REFSIG/dmrs_nr.c @@ -308,10 +308,10 @@ int8_t get_next_dmrs_symbol_in_slot(uint16_t ul_dmrs_symb_pos, uint8_t counter, /* return the total number of dmrs symbol in a slot */ -uint8_t get_dmrs_symbols_in_slot(uint16_t l_prime_mask, uint16_t nb_symb) +uint8_t get_dmrs_symbols_in_slot(uint16_t l_prime_mask, uint16_t nb_symb, uint8_t start) { uint8_t tmp = 0; - for (int i = 0; i < nb_symb; i++) { + for (int i = start; i < start + nb_symb; i++) { tmp += (l_prime_mask >> i) & 0x01; } return tmp; @@ -351,7 +351,7 @@ void nr_chest_time_domain_avg(NR_DL_FRAME_PARMS *frame_parms, simde__m128i *ul_ch128_1; int16_t *ul_ch16_0; int total_symbols = start_symbol + num_symbols; - int num_dmrs_symb = get_dmrs_symbols_in_slot(dmrs_bitmap, total_symbols); + int num_dmrs_symb = get_dmrs_symbols_in_slot(dmrs_bitmap, total_symbols, start_symbol); int first_dmrs_symb = get_next_dmrs_symbol_in_slot(dmrs_bitmap, start_symbol, total_symbols); AssertFatal(first_dmrs_symb > -1, "No DMRS symbol present in this slot\n"); for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { diff --git a/openair1/PHY/NR_REFSIG/dmrs_nr.h b/openair1/PHY/NR_REFSIG/dmrs_nr.h index 8b9da28abc..2c1005a19b 100644 --- a/openair1/PHY/NR_REFSIG/dmrs_nr.h +++ b/openair1/PHY/NR_REFSIG/dmrs_nr.h @@ -63,7 +63,7 @@ uint8_t allowed_xlsch_re_in_dmrs_symbol(uint16_t k, void nr_gen_ref_conj_symbols(uint32_t *in, uint32_t length, int16_t *output, uint16_t offset, int mod_order); int8_t get_next_dmrs_symbol_in_slot(uint16_t ul_dmrs_symb_pos, uint8_t counter, uint8_t end_symbol); -uint8_t get_dmrs_symbols_in_slot(uint16_t l_prime_mask, uint16_t nb_symb); +uint8_t get_dmrs_symbols_in_slot(uint16_t l_prime_mask, uint16_t nb_symb, uint8_t start); int8_t get_valid_dmrs_idx_for_channel_est(uint16_t dmrs_symb_pos, uint8_t counter); void nr_chest_time_domain_avg(NR_DL_FRAME_PARMS *frame_parms, int32_t **ch_estimates, diff --git a/openair1/PHY/NR_TRANSPORT/nr_scrambling.c b/openair1/PHY/NR_TRANSPORT/nr_scrambling.c index b54a5c2c0c..353db70454 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_scrambling.c +++ b/openair1/PHY/NR_TRANSPORT/nr_scrambling.c @@ -32,16 +32,14 @@ void nr_codeword_scrambling(uint8_t *in, uint32_t* out) { uint32_t x1; - uint32_t x2 = (n_RNTI<<15) + (q<<14) + Nid; - uint32_t s = 0; - - s=lte_gold_generic(&x1, &x2, 1); - for (int i=0; i<((size>>5)+((size&0x1f) > 0 ? 1 : 0)); i++) { + uint32_t x2 = (n_RNTI << 15) + (q << 14) + Nid; + uint32_t s = lte_gold_generic(&x1, &x2, 1); + for (int i = 0; i < ((size >> 5) + ((size & 0x1f) > 0 ? 1 : 0)); i++) { simde__m256i c = ((simde__m256i*)in)[i]; - uint32_t in32 = simde_mm256_movemask_epi8(simde_mm256_slli_epi16(c,7)); - out[i]=(in32^s); + uint32_t in32 = simde_mm256_movemask_epi8(simde_mm256_slli_epi16(c, 7)); + out[i] = (in32 ^ s); DEBUG_SCRAMBLING(LOG_D(PHY, "in[%d] %x => %x\n", i, in32, out[i])); - s=lte_gold_generic(&x1, &x2, 0); + s = lte_gold_generic(&x1, &x2, 0); } } diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 822153fd14..a3b5bb9eb2 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -775,8 +775,7 @@ int main(int argc, char *argv[]) uint8_t length_dmrs = pusch_len1; uint16_t l_prime_mask = get_l_prime(nb_symb_sch, mapping_type, add_pos, length_dmrs, start_symbol, NR_MIB__dmrs_TypeA_Position_pos2); - uint16_t number_dmrs_symbols = get_dmrs_symbols_in_slot(l_prime_mask, nb_symb_sch); - printf("num dmrs sym %d\n",number_dmrs_symbols); + uint16_t number_dmrs_symbols = get_dmrs_symbols_in_slot(l_prime_mask, nb_symb_sch, start_symbol); uint8_t nb_re_dmrs = (dmrs_config_type == pusch_dmrs_type1) ? 6 : 4; uint32_t tbslbrm = 0; @@ -804,8 +803,18 @@ int main(int argc, char *argv[]) nb_re_dmrs = nb_re_dmrs * num_dmrs_cdm_grps_no_data; unsigned int TBS = nr_compute_tbs(mod_order, code_rate, nb_rb, nb_symb_sch, nb_re_dmrs * number_dmrs_symbols, 0, 0, precod_nbr_layers); - printf("[ULSIM]: length_dmrs: %u, l_prime_mask: %u number_dmrs_symbols: %u, mapping_type: %u add_pos: %d \n", length_dmrs, l_prime_mask, number_dmrs_symbols, mapping_type, add_pos); - printf("[ULSIM]: CDM groups: %u, dmrs_config_type: %d, num_rbs: %u, nb_symb_sch: %u\n", num_dmrs_cdm_grps_no_data, dmrs_config_type, nb_rb, nb_symb_sch); + printf("[ULSIM]: length_dmrs: %u, l_prime_mask: %u number_dmrs_symbols: %u, mapping_type: %u add_pos: %d \n", + length_dmrs, + l_prime_mask, + number_dmrs_symbols, + mapping_type, + add_pos); + printf("[ULSIM]: CDM groups: %u, dmrs_config_type: %d, num_rbs: %u, nb_symb_sch: %u, start_symbol %u\n", + num_dmrs_cdm_grps_no_data, + dmrs_config_type, + nb_rb, + nb_symb_sch, + start_symbol); printf("[ULSIM]: MCS: %d, mod order: %u, code_rate: %u\n", Imcs, mod_order, code_rate); uint8_t ulsch_input_buffer[TBS/8]; diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c index aa1042491b..91c8b9b9c4 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c @@ -50,8 +50,14 @@ uint32_t nr_compute_tbs(uint16_t Qm, uint8_t tb_scaling, uint8_t Nl) { - - LOG_D(NR_MAC, "In %s: nb_symb_sch %d, nb_dmrs_prb %d, nb_rb %d, nb_rb_oh %d, tb_scaling %d Nl %d\n", __FUNCTION__, nb_symb_sch, nb_dmrs_prb, nb_rb, nb_rb_oh, tb_scaling, Nl); + LOG_D(NR_MAC, + "nb_symb_sch %d, nb_dmrs_prb %d, nb_rb %d, nb_rb_oh %d, tb_scaling %d Nl %d\n", + nb_symb_sch, + nb_dmrs_prb, + nb_rb, + nb_rb_oh, + tb_scaling, + Nl); const int nb_subcarrier_per_rb = 12; const uint32_t nbp_re = nb_subcarrier_per_rb * nb_symb_sch - nb_dmrs_prb - nb_rb_oh; @@ -91,10 +97,9 @@ uint32_t nr_compute_tbs(uint16_t Qm, } } - LOG_D(NR_MAC, "In %s: Ninfo %u nbp_re %d nb_re %d Qm %d, R %d, tbs %d bits\n", __FUNCTION__, Ninfo, nbp_re, nb_re, Qm, R, nr_tbs); + LOG_D(NR_MAC, "Ninfo %u nbp_re %d nb_re %d Qm %d, R %d, tbs %d bits\n", Ninfo, nbp_re, nb_re, Qm, R, nr_tbs); return nr_tbs; - } //tbslbrm calculation according to 5.4.2.1 of 38.212 diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index b91da1152b..151cee026d 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2473,25 +2473,26 @@ uint8_t getAntPortBitWidth(NR_SetupRelease_DMRS_DownlinkConfig_t *typeA, NR_Setu * *********************************************************************/ -uint8_t get_l0_ul(uint8_t mapping_type, uint8_t dmrs_typeA_position) { - +uint8_t get_l0_ul(uint8_t mapping_type, uint8_t dmrs_typeA_position) +{ return ((mapping_type==typeA)?dmrs_typeA_position:0); - } -int32_t get_l_prime(uint8_t duration_in_symbols, uint8_t mapping_type, pusch_dmrs_AdditionalPosition_t additional_pos, pusch_maxLength_t pusch_maxLength, uint8_t start_symbol, uint8_t dmrs_typeA_position) { - - uint8_t row, colomn; - int32_t l_prime; - - LOG_D(NR_MAC, "In %s: PUSCH NrofSymbols:%d, startSymbol:%d, mappingtype:%d, dmrs_TypeA_Position:%d additional_pos:%d, pusch_maxLength:%d\n", - __FUNCTION__, - duration_in_symbols, - start_symbol, - mapping_type, - dmrs_typeA_position, - additional_pos, - pusch_maxLength); +int32_t get_l_prime(uint8_t duration_in_symbols, + uint8_t mapping_type, + pusch_dmrs_AdditionalPosition_t additional_pos, + pusch_maxLength_t pusch_maxLength, + uint8_t start_symbol, + uint8_t dmrs_typeA_position) +{ + LOG_D(NR_MAC, + "PUSCH NrofSymbols:%d, startSymbol:%d, mappingtype:%d, dmrs_TypeA_Position:%d additional_pos:%d, pusch_maxLength:%d\n", + duration_in_symbols, + start_symbol, + mapping_type, + dmrs_typeA_position, + additional_pos, + pusch_maxLength); // Section 6.4.1.1.3 in Spec 38.211 // For PDSCH Mapping TypeA, ld is duration between first OFDM of the slot and last OFDM symbol of the scheduled PUSCH resources @@ -2499,16 +2500,18 @@ int32_t get_l_prime(uint8_t duration_in_symbols, uint8_t mapping_type, pusch_dmr uint8_t ld = (mapping_type == typeA) ? (duration_in_symbols + start_symbol) : duration_in_symbols; uint8_t l0 = (dmrs_typeA_position == NR_MIB__dmrs_TypeA_Position_pos2) ? 2 : 3 ; - colomn = additional_pos; + uint8_t colomn = additional_pos; if (mapping_type == typeB) colomn += 4; + uint8_t row; if (ld < 4) row = 0; else row = ld - 3; + uint32_t l_prime; if (pusch_maxLength == pusch_len1) { l_prime = table_6_4_1_1_3_3_pusch_dmrs_positions_l[row][colomn]; l0 = 1 << l0; @@ -2525,7 +2528,6 @@ int32_t get_l_prime(uint8_t duration_in_symbols, uint8_t mapping_type, pusch_dmr LOG_D(MAC, " PUSCH DMRS MASK in HEX:%x\n", l_prime); return l_prime; - } /******************************************************************* From 75efcf49b9d660b8146a4936ef69821a5665de66 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 7 May 2024 19:59:03 +0200 Subject: [PATCH 09/26] NR UE: add NTN specific parameter ta-Common and use it for initial timing advance, RA_contention_resolution_timer and RA_window_cnt The value for this parameter can be set via UE command line parameter `--ntn-ta-common` in milliseconds. --- executables/nr-ue.c | 1 + executables/nr-uesoftmodem.h | 3 ++ openair1/SCHED_NR_UE/fapi_nr_ue_l1.c | 1 - openair2/LAYER2/NR_MAC_UE/main_ue_nr.c | 8 ++++- openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c | 32 +++++++++++++++----- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 8a73bdd311..447c99182b 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -149,6 +149,7 @@ void init_nr_ue_vars(PHY_VARS_NR_UE *ue, ue->if_inst = nr_ue_if_module_init(UE_id); ue->dci_thres = 0; ue->target_Nid_cell = -1; + ue->timing_advance = ue->frame_parms.samples_per_subframe * get_nrUE_params()->ntn_ta_common; // initialize all signal buffers init_nr_ue_signal(ue, nb_connected_gNB); diff --git a/executables/nr-uesoftmodem.h b/executables/nr-uesoftmodem.h index c95a6dc98c..8e0650b4e4 100644 --- a/executables/nr-uesoftmodem.h +++ b/executables/nr-uesoftmodem.h @@ -13,6 +13,7 @@ #define CONFIG_HLP_OFFSET_DIV "Divisor for computing OFDM symbol offset in Rx chain (num samples in CP/). Default value is 8. To set the sample offset to 0, set this value ~ 10e6\n" #define CONFIG_HLP_MAX_LDPC_ITERATIONS "Maximum LDPC decoder iterations\n" #define CONFIG_HLP_NTN_KOFFSET "NTN cellSpecificKoffset-r17 (number of slots for a given subcarrier spacing of 15 kHz)\n" +#define CONFIG_HLP_NTN_TA_COMMON "NTN ta-Common, but given in ms\n" /***************************************************************************************************************************************/ /* command line options definitions, CMDLINE_XXXX_DESC macros are used to initialize paramdef_t arrays which are then used as argument @@ -66,6 +67,7 @@ {"SLC", CONFIG_HLP_SLF, 0, .u64ptr=&(sidelink_frequency[0][0]), .defuintval=2600000000,TYPE_UINT64,0}, \ {"num-ues", NULL, 0, .iptr=&(NB_UE_INST), .defuintval=1, TYPE_INT, 0}, \ {"ntn-koffset", CONFIG_HLP_NTN_KOFFSET, 0, .uptr=&(nrUE_params.ntn_koffset), .defuintval=0, TYPE_UINT, 0}, \ + {"ntn-ta-common", CONFIG_HLP_NTN_TA_COMMON, 0, .dblptr=&(nrUE_params.ntn_ta_common), .defdblval=0.0, TYPE_DOUBLE, 0}, \ } // clang-format on @@ -87,6 +89,7 @@ typedef struct { int ssb_start_subcarrier; int ldpc_offload_flag; unsigned int ntn_koffset; + double ntn_ta_common; } nrUE_params_t; extern uint64_t get_nrUE_optmask(void); extern uint64_t set_nrUE_optmask(uint64_t bitmask); diff --git a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c index d01f1e3e94..b408b9fed3 100644 --- a/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c +++ b/openair1/SCHED_NR_UE/fapi_nr_ue_l1.c @@ -373,7 +373,6 @@ static void configure_ta_command(PHY_VARS_NR_UE *ue, fapi_nr_ta_command_pdu *ta_ if (ta_command_pdu->is_rar) { ue->ta_slot = ta_command_pdu->ta_slot; ue->ta_frame = ta_command_pdu->ta_frame; - ue->timing_advance = 0; ue->ta_command = ta_command_pdu->ta_command + 31; // To use TA adjustment algo in ue_ta_procedures() } else { ue->ta_slot = (ta_command_pdu->ta_slot + ul_tx_timing_adjustment) % slots_per_frame; diff --git a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c index 2230d92729..501c1022f1 100644 --- a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c +++ b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c @@ -87,7 +87,7 @@ void nr_ue_init_mac(NR_UE_MAC_INST_t *mac) // Fake SIB19 reception for NTN // TODO: remove this and implement the actual SIB19 reception instead! - if (get_nrUE_params()->ntn_koffset) { + if (get_nrUE_params()->ntn_koffset || get_nrUE_params()->ntn_ta_common) { NR_SIB19_r17_t *sib19_r17 = calloc(1, sizeof(*sib19_r17)); sib19_r17->ntn_Config_r17 = calloc(1, sizeof(*sib19_r17->ntn_Config_r17)); @@ -96,6 +96,12 @@ void nr_ue_init_mac(NR_UE_MAC_INST_t *mac) asn1cCallocOne(sib19_r17->ntn_Config_r17->cellSpecificKoffset_r17, get_nrUE_params()->ntn_koffset); } + // NTN ta-Common-r17 + if (get_nrUE_params()->ntn_ta_common) { + sib19_r17->ntn_Config_r17->ta_Info_r17 = calloc(1, sizeof(*sib19_r17->ntn_Config_r17->ta_Info_r17)); + sib19_r17->ntn_Config_r17->ta_Info_r17->ta_Common_r17 = get_nrUE_params()->ntn_ta_common / 4.072e-6; // ta-Common-r17 is in units of 4.072e-3 µs, ntn_ta_common is in ms + } + nr_rrc_mac_config_req_sib19_r17(mac->ue_id, sib19_r17); asn1cFreeStruc(asn_DEF_NR_SIB19_r17, sib19_r17); } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index ebf3ee2703..11ce2c5c30 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -41,6 +41,13 @@ #include #include "openair2/LAYER2/RLC/rlc.h" +static double get_ta_Common_ms(NR_NTN_Config_r17_t *ntn_Config_r17) +{ + if (ntn_Config_r17 && ntn_Config_r17->ta_Info_r17) + return ntn_Config_r17->ta_Info_r17->ta_Common_r17 * 4.072e-6; // ta_Common_r17 is in units of 4.072e-3 µs + return 0.0; +} + int16_t get_prach_tx_power(NR_UE_MAC_INST_t *mac) { RA_config_t *ra = &mac->ra; @@ -580,13 +587,17 @@ void nr_Msg3_transmitted(NR_UE_MAC_INST_t *mac, uint8_t CC_id, frame_t frameP, s { RA_config_t *ra = &mac->ra; NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon; - long mu = mac->current_UL_BWP->scs; - int subframes_per_slot = nr_slots_per_frame[mu] / 10; + const double ta_Common_ms = get_ta_Common_ms(mac->sc_info.ntn_Config_r17); + const int mu = mac->current_UL_BWP->scs; + const int slots_per_ms = nr_slots_per_frame[mu] / 10; // start contention resolution timer - int RA_contention_resolution_timer_subframes = (nr_rach_ConfigCommon->ra_ContentionResolutionTimer + 1) << 3; - // timer step 1 slot and timer target given by ra_ContentionResolutionTimer - nr_timer_setup(&ra->contention_resolution_timer, RA_contention_resolution_timer_subframes * subframes_per_slot, 1); + const int RA_contention_resolution_timer_ms = (nr_rach_ConfigCommon->ra_ContentionResolutionTimer + 1) << 3; + const int RA_contention_resolution_timer_slots = RA_contention_resolution_timer_ms * slots_per_ms; + const int ta_Common_slots = (int)ceil(ta_Common_ms * slots_per_ms); + + // timer step 1 slot and timer target given by ra_ContentionResolutionTimer + ta-Common-r17 + nr_timer_setup(&ra->contention_resolution_timer, RA_contention_resolution_timer_slots + ta_Common_slots, 1); nr_timer_start(&ra->contention_resolution_timer); ra->ra_state = nrRA_WAIT_CONTENTION_RESOLUTION; @@ -788,11 +799,16 @@ void nr_get_RA_window(NR_UE_MAC_INST_t *mac) NR_RACH_ConfigCommon_t *setup = mac->current_UL_BWP->rach_ConfigCommon; AssertFatal(&setup->rach_ConfigGeneric != NULL, "In %s: FATAL! rach_ConfigGeneric is NULL...\n", __FUNCTION__); NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric; + const double ta_Common_ms = get_ta_Common_ms(mac->sc_info.ntn_Config_r17); + const int mu = mac->current_DL_BWP->scs; + const int slots_per_ms = nr_slots_per_frame[mu] / 10; - int ra_ResponseWindow = rach_ConfigGeneric->ra_ResponseWindow; - int mu = mac->current_DL_BWP->scs; + const int ra_Offset_slots = ra->RA_offset * nr_slots_per_frame[mu]; + const int ta_Common_slots = (int)ceil(ta_Common_ms * slots_per_ms); - ra->RA_window_cnt = ra->RA_offset * nr_slots_per_frame[mu]; // taking into account the 2 frames gap introduced by OAI gNB + ra->RA_window_cnt = ra_Offset_slots + ta_Common_slots; // taking into account the 2 frames gap introduced by OAI gNB + + int ra_ResponseWindow = rach_ConfigGeneric->ra_ResponseWindow; switch (ra_ResponseWindow) { case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl1: From 1e473112597cf4f87cfb76256ac4c1306ba1be26 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Mon, 27 May 2024 16:53:01 +0200 Subject: [PATCH 10/26] NR UE: use sr_ProhibitTimer_v1700 if present --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index ccff0020c3..f723ab2378 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -1820,6 +1820,17 @@ static void configure_maccellgroup(NR_UE_MAC_INST_t *mac, const NR_MAC_CellGroup int target_ms = 0; if (sr->sr_ProhibitTimer) target_ms = 1 << *sr->sr_ProhibitTimer; + if (mcg->ext4 && mcg->ext4->schedulingRequestConfig_v1700) { + const NR_SchedulingRequestConfig_v1700_t *src_v1700 = mcg->ext4->schedulingRequestConfig_v1700; + if (src_v1700->schedulingRequestToAddModListExt_v1700) { + if (i < src_v1700->schedulingRequestToAddModListExt_v1700->list.count) { + const NR_SchedulingRequestToAddModExt_v1700_t *sr_v1700 = src_v1700->schedulingRequestToAddModListExt_v1700->list.array[i]; + if (sr_v1700->sr_ProhibitTimer_v1700) { + target_ms = 192 + 64 * *sr_v1700->sr_ProhibitTimer_v1700; + } + } + } + } // length of slot is (1/2^scs)ms nr_timer_setup(&sr_info->prohibitTimer, target_ms << scs, 1); // 1 slot update rate } From 4ddb2d3bac97973a8f289e9e5ba70b16ae12cda3 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Tue, 7 May 2024 20:35:22 +0200 Subject: [PATCH 11/26] add section with NR UE NTN example to doc/RUNMODEM.md --- doc/RUNMODEM.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/RUNMODEM.md b/doc/RUNMODEM.md index 753efdadaf..9ee9083e6d 100644 --- a/doc/RUNMODEM.md +++ b/doc/RUNMODEM.md @@ -188,6 +188,18 @@ cd cmake_targets sudo ./ran_build/build/nr-softmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf --sa --rfsim --rfsimulator.prop_delay 238.74 ``` +### NR UE + +At UE side, there are two main parameters to cope with the large NTN propagation delay, cellSpecificKoffset and ta-Common. +`cellSpecificKoffset` is the same as for gNB and can be provided to the UE via command line parameter `--ntn-koffset`. +`ta-Common` is a common timong advance and can be provided to the UE via command line parameter `--ntn-ta-common` in milliseconds. + +So an example NR UE command for FDD, 5MHz BW, 15 kHz SCS, GEO satellite 5G NR NTN is this: +``` +cd cmake_targets +sudo ./ran_build/build/nr-uesoftmodem --band 66 -C 2152680000 --CO -400000000 -r 25 --numerology 0 --ssb 48 --sa --rfsim --rfsimulator.prop_delay 238.74 --ntn-koffset 478 --ntn-ta-common 477.48 +``` + # Specific OAI modes ## phy-test setup with OAI UE From 9dc0dc5ffe756815fa4a8e0842cc061c8d1bb0ed Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 11 Jul 2024 14:43:13 +0200 Subject: [PATCH 12/26] NR UE: improve PSS detection by reducing the detection step size from 8 to 4 Until commit d74b2bd19fc7a34741822fdbcc537cd2c2b0d4ae, the PSS detection step size was 4, anyways. The PSS detection step size is a compromise between detection duration and accuracy. The most accurate detection step size would be 1, but this would lead to even longer initial sync time. --- openair1/PHY/NR_UE_TRANSPORT/pss_nr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c b/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c index 6d9aaf7f9a..54d9022e87 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c @@ -560,7 +560,7 @@ static int pss_search_time_nr(const c16_t **rxdata, } for (int pss_index = pss_index_start; pss_index < pss_index_end; pss_index++) { - for (n = 0; n < length; n += 8) { // + for (n = 0; n < length; n += 4) { // int64_t pss_corr_ue=0; /* calculate dot product of primary_synchro_time_nr and rxdata[ar][n] From 359880fa73ade9c2636d4fa917576f5815bf80d6 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 11 Jul 2024 14:48:52 +0200 Subject: [PATCH 13/26] rfsimulator: use ceil() to calculate the integer number of chan_offset samples from the prop_delay_ms The actual propagation delay should not be smaller than the one specified. A too short propagation delay might result in too early reception of Msg1, and with that in a mis-detection of the RAPID. --- radio/rfsimulator/simulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index e524097898..0a2ece7d21 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -983,7 +983,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { rfsimulator->tx_bw=openair0_cfg->tx_bw; rfsimulator_readconfig(rfsimulator); if (rfsimulator->prop_delay_ms > 0.0) - rfsimulator->chan_offset = rfsimulator->sample_rate * rfsimulator->prop_delay_ms / 1000; + rfsimulator->chan_offset = ceil(rfsimulator->sample_rate * rfsimulator->prop_delay_ms / 1000); if (rfsimulator->chan_offset != 0) { if (CirSize < minCirSize + rfsimulator->chan_offset) { CirSize = minCirSize + rfsimulator->chan_offset; From cd8f076979d6f5942ab5d8a8bcfa9d32b90c5327 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Tue, 2 Jul 2024 11:53:05 +0200 Subject: [PATCH 14/26] PUSCH power control state --- .../nfapi/public_inc/fapi_nr_ue_interface.h | 1 - openair1/SIMULATION/NR_PHY/ulsim.c | 1 - openair2/LAYER2/NR_MAC_UE/mac_defs.h | 2 + openair2/LAYER2/NR_MAC_UE/main_ue_nr.c | 1 + .../LAYER2/NR_MAC_UE/nr_ue_power_procedures.c | 18 ++- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 32 ++-- .../tests/test_nr_ue_power_procedures.cpp | 138 ++++++++++++++++++ 7 files changed, 175 insertions(+), 18 deletions(-) diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h index f76647d6af..0206bfa36c 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h @@ -359,7 +359,6 @@ typedef struct //beamforming nfapi_nr_ue_ul_beamforming_t beamforming; //OAI specific - int8_t absolute_delta_PUSCH; int16_t tx_power; fapi_nr_tx_request_body_t tx_request_body; } nfapi_nr_ue_pusch_pdu_t; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 822153fd14..d40d1dc71e 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1119,7 +1119,6 @@ int main(int argc, char *argv[]) pusch_config_pdu->num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data; pusch_config_pdu->nrOfLayers = precod_nbr_layers; pusch_config_pdu->dmrs_ports = ((1 << precod_nbr_layers) - 1); - pusch_config_pdu->absolute_delta_PUSCH = 0; pusch_config_pdu->target_code_rate = code_rate; pusch_config_pdu->tbslbrm = tbslbrm; pusch_config_pdu->ldpcBaseGraph = get_BG(TBS, code_rate); diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h index 5c6b0bd93a..781192fbc2 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h @@ -593,6 +593,8 @@ typedef struct NR_UE_MAC_INST_s { // PUCCH closed loop power control state int G_b_f_c; bool pucch_power_control_initialized; + int f_b_f_c; + bool pusch_power_control_initialized; } NR_UE_MAC_INST_t; /*@}*/ diff --git a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c index 4d8b79a2ab..139d84fe8d 100644 --- a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c +++ b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c @@ -84,6 +84,7 @@ void nr_ue_init_mac(NR_UE_MAC_INST_t *mac) memset(&mac->scheduling_info.sr_info[i], 0, sizeof(mac->scheduling_info.sr_info[i])); mac->pucch_power_control_initialized = false; + mac->pusch_power_control_initialized = false; } void nr_ue_mac_default_configs(NR_UE_MAC_INST_t *mac) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c index 02aea8dd4d..cb60f0236c 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c @@ -553,12 +553,28 @@ int get_pusch_tx_power_ue(NR_UE_MAC_INST_t *mac, // TODO: compute pathoss using correct reference int16_t pathloss = compute_nr_SSB_PL(mac, mac->ssb_measurements.ssb_rsrp_dBm); + int P_CMIN = nr_get_Pcmin(mac->current_UL_BWP->scs, mac->nr_band, mac->current_UL_BWP->BWPSize); + + float pusch_power_without_f_b_f_c = P_O_PUSCH + M_pusch_component + alpha * pathloss + DELTA_TF; int f_b_f_c = 0; if (has_pusch_power_control_config && pusch_Config->pusch_PowerControl->tpc_Accumulation) { f_b_f_c = delta_pusch; } else { - // TODO: PUSCH power control state + if (!mac->pusch_power_control_initialized && is_rar_tx_retx) { + NR_PRACH_RESOURCES_t* prach_resources = &mac->ra.prach_resources; + float DELTA_P_rampup_requested = (prach_resources->RA_PREAMBLE_POWER_RAMPING_COUNTER - 1) * prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP; + float DELTA_P_rampup = P_CMAX - (P_O_PUSCH + M_pusch_component + alpha * pathloss + DELTA_TF + delta_pusch); + DELTA_P_rampup = min(DELTA_P_rampup_requested, max(0, DELTA_P_rampup)); + mac->f_b_f_c = DELTA_P_rampup + delta_pusch; + mac->pusch_power_control_initialized = true; + } else { + if (!((pusch_power_without_f_b_f_c + mac->f_b_f_c >= P_CMAX && delta_pusch > 0) || + (pusch_power_without_f_b_f_c + mac->f_b_f_c <= P_CMIN && delta_pusch < 0))) { + mac->f_b_f_c += delta_pusch; + } + } + f_b_f_c = mac->f_b_f_c; } LOG_D(NR_MAC, "PUSCH tx power components P_O_PUSCH=%d, M_pusch_component=%d, alpha*pathloss=%f, delta_TF=%f, f_b_f_c=%d\n", diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index b88ca7e5f3..3824d80cbd 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -793,20 +793,6 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->pusch_data.rv_index = dci->rv; /* HARQ_PROCESS_NUMBER */ pusch_config_pdu->pusch_data.harq_process_id = dci->harq_pid; - /* TPC_PUSCH */ - // according to TS 38.213 Table Table 7.1.1-1 - if (dci->tpc == 0) { - pusch_config_pdu->absolute_delta_PUSCH = -4; - } - if (dci->tpc == 1) { - pusch_config_pdu->absolute_delta_PUSCH = -1; - } - if (dci->tpc == 2) { - pusch_config_pdu->absolute_delta_PUSCH = 1; - } - if (dci->tpc == 3) { - pusch_config_pdu->absolute_delta_PUSCH = 4; - } if (NR_DMRS_ulconfig != NULL) add_pos = (NR_DMRS_ulconfig->dmrs_AdditionalPosition == NULL) ? 2 : *NR_DMRS_ulconfig->dmrs_AdditionalPosition; @@ -913,6 +899,22 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->pusch_data.tb_size = mac->ul_harq_info[pid].TBS; } + /* TPC_PUSCH */ + int delta_pusch = 0; + if (dci) { + bool stateless_pusch_power_control = mac->current_UL_BWP->pusch_Config != NULL + && mac->current_UL_BWP->pusch_Config->pusch_PowerControl != NULL + && mac->current_UL_BWP->pusch_Config->pusch_PowerControl->tpc_Accumulation != NULL; + int table_38_213_7_1_1_1[2][4] = {{-1, 0, 1, 3}, {-4, -1, 1, 4}}; + if (stateless_pusch_power_control) { + delta_pusch = table_38_213_7_1_1_1[1][dci->tpc]; + } else { + // TODO: This is not entirely correct. In case there is a prevously scheduled PUSCH for a future slot + // we should apply its TPC now. + delta_pusch = table_38_213_7_1_1_1[0][dci->tpc]; + } + } + bool is_rar_tx_retx = rnti_type == TYPE_TC_RNTI_; pusch_config_pdu->tx_power = get_pusch_tx_power_ue(mac, @@ -925,7 +927,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, pusch_config_pdu->target_code_rate, pusch_config_pdu->pusch_uci.beta_offset_csi1, pusch_config_pdu->pusch_data.tb_size << 3, - pusch_config_pdu->absolute_delta_PUSCH, + delta_pusch, is_rar_tx_retx, pusch_config_pdu->transform_precoding); diff --git a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp index 3f865ec597..c594d43082 100644 --- a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp +++ b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp @@ -198,6 +198,7 @@ TEST(pusch_power_control, pusch_power_control_msg3) NR_PUSCH_PowerControl pusch_PowerControl = {0}; pusch_Config.pusch_PowerControl = &pusch_PowerControl; pusch_PowerControl.tpc_Accumulation = (long*)1; + mac.pusch_power_control_initialized = true; mac.frame_type = TDD; // msg3 cofiguration as in 5g_rfsimulator testcase @@ -358,6 +359,143 @@ TEST(pusch_power_control, pusch_power_data) EXPECT_EQ(power, P_CMAX) << "Expecting max tx power because of deltaMCS with CSI-only"; } +TEST(pusch_power_control, pusch_power_control_state_initialization) +{ + NR_UE_MAC_INST_t mac = {0}; + NR_UE_UL_BWP_t current_UL_BWP = {0}; + current_UL_BWP.scs = 1; + current_UL_BWP.BWPSize = 106; + mac.current_UL_BWP = ¤t_UL_BWP; + NR_RACH_ConfigCommon_t nr_rach_ConfigCommon = {0}; + current_UL_BWP.rach_ConfigCommon = &nr_rach_ConfigCommon; + mac.nr_band = 78; + NR_PUSCH_Config_t pusch_Config = {0}; + current_UL_BWP.pusch_Config = &pusch_Config; + NR_PUSCH_PowerControl pusch_PowerControl = {0}; + pusch_Config.pusch_PowerControl = &pusch_PowerControl; + mac.pusch_power_control_initialized = false; + + // msg3 cofiguration as in 5g_rfsimulator testcase + int num_rb = 8; + int start_prb = 0; + uint16_t nb_symb_sch = 3; + uint16_t nb_dmrs_prb = 12; + uint16_t nb_ptrs_prb = 0; + uint16_t Qm = 2; + uint16_t R = 1570; + uint16_t beta_offset_csi1 = 0; + uint32_t sum_bits_in_codeblocks = 56; + int delta_pusch = 0; + bool is_rar_tx_retx = true; + long preambleReceivedTargetPower = -96; + nr_rach_ConfigCommon.rach_ConfigGeneric.preambleReceivedTargetPower = preambleReceivedTargetPower; + + get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + false); + EXPECT_EQ(mac.pusch_power_control_initialized, true); +} + +TEST(pusch_power_control, pusch_power_control_state) +{ + NR_UE_MAC_INST_t mac = {0}; + NR_UE_UL_BWP_t current_UL_BWP = {0}; + current_UL_BWP.scs = 1; + current_UL_BWP.BWPSize = 106; + mac.current_UL_BWP = ¤t_UL_BWP; + NR_RACH_ConfigCommon_t nr_rach_ConfigCommon = {0}; + current_UL_BWP.rach_ConfigCommon = &nr_rach_ConfigCommon; + mac.nr_band = 78; + mac.f_b_f_c = 0; + mac.pusch_power_control_initialized = true; + + bool is_rar_tx_retx = false; + int num_rb = 5; + int start_prb = 0; + uint16_t nb_symb_sch = 3; + uint16_t nb_dmrs_prb = 6; + uint16_t nb_ptrs_prb = 0; + uint16_t Qm = 2; + uint16_t R = 6790; + uint16_t beta_offset_csi1 = 0; + uint32_t sum_bits_in_codeblocks = 192; + int delta_pusch = 1; + bool transform_precoding = false; + NR_PUSCH_Config_t pusch_Config = {0}; + current_UL_BWP.pusch_Config = &pusch_Config; + NR_PUSCH_PowerControl pusch_PowerControl = {0}; + pusch_Config.pusch_PowerControl = &pusch_PowerControl; + long p0_NominalWithGrant = 0; + current_UL_BWP.p0_NominalWithGrant = &p0_NominalWithGrant; + mac.frame_type = TDD; + + int P_CMAX = nr_get_Pcmax(23, mac.nr_band, mac.frame_type, FR1, Qm, false, current_UL_BWP.scs, current_UL_BWP.BWPSize, transform_precoding, num_rb, start_prb); + + int power = get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + transform_precoding); + EXPECT_LE(power, P_CMAX); + EXPECT_EQ(power, 11); + for (int i = 0; i < 20; i++) { + int increased_power = get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + transform_precoding); + EXPECT_GE(increased_power, power); + EXPECT_LE(increased_power, P_CMAX); + power = increased_power; + } + + delta_pusch = -1; + for (int i = 0; i < 20; i++) { + int reduced_power = get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + transform_precoding); + EXPECT_LE(reduced_power, power); + EXPECT_LE(reduced_power, P_CMAX); + power = reduced_power; + } +} + int main(int argc, char** argv) { logInit(); From 04c8f7361636729b1445c22bcff2982bea191f77 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Tue, 2 Jul 2024 16:19:44 +0200 Subject: [PATCH 15/26] Fix a bug where PUSCH max TX power was not dependant on number of PUSCH RBs or qm. --- .../LAYER2/NR_MAC_UE/nr_ue_power_procedures.c | 4 +- .../tests/test_nr_ue_power_procedures.cpp | 75 ++++++++++++++++++- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c index cb60f0236c..01bf59b56f 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c @@ -522,12 +522,12 @@ int get_pusch_tx_power_ue(NR_UE_MAC_INST_t *mac, mac->nr_band, mac->frame_type, mac->frequency_range, - 2, + qm, false, mac->current_UL_BWP->scs, mac->current_UL_BWP->BWPSize, transform_precoding, - 1, + num_rb, start_prb); int P_O_PUSCH = P_O_NOMINAL_PUSCH + P_O_UE_PUSCH; diff --git a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp index c594d43082..a315835df7 100644 --- a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp +++ b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp @@ -439,7 +439,17 @@ TEST(pusch_power_control, pusch_power_control_state) current_UL_BWP.p0_NominalWithGrant = &p0_NominalWithGrant; mac.frame_type = TDD; - int P_CMAX = nr_get_Pcmax(23, mac.nr_band, mac.frame_type, FR1, Qm, false, current_UL_BWP.scs, current_UL_BWP.BWPSize, transform_precoding, num_rb, start_prb); + int P_CMAX = nr_get_Pcmax(23, + mac.nr_band, + mac.frame_type, + FR1, + Qm, + false, + current_UL_BWP.scs, + current_UL_BWP.BWPSize, + transform_precoding, + num_rb, + start_prb); int power = get_pusch_tx_power_ue(&mac, num_rb, @@ -496,6 +506,69 @@ TEST(pusch_power_control, pusch_power_control_state) } } +TEST(pusch_power_control, pusch_power_100_rb) +{ + NR_UE_MAC_INST_t mac = {0}; + NR_UE_UL_BWP_t current_UL_BWP = {0}; + current_UL_BWP.scs = 1; + current_UL_BWP.BWPSize = 106; + mac.current_UL_BWP = ¤t_UL_BWP; + NR_RACH_ConfigCommon_t nr_rach_ConfigCommon = {0}; + current_UL_BWP.rach_ConfigCommon = &nr_rach_ConfigCommon; + mac.nr_band = 78; + mac.f_b_f_c = 0; + mac.pusch_power_control_initialized = true; + + bool is_rar_tx_retx = false; + int num_rb = 5; + int start_prb = 0; + uint16_t nb_symb_sch = 3; + uint16_t nb_dmrs_prb = 6; + uint16_t nb_ptrs_prb = 0; + uint16_t Qm = 2; + uint16_t R = 6790; + uint16_t beta_offset_csi1 = 0; + uint32_t sum_bits_in_codeblocks = 192; + int delta_pusch = 1; + bool transform_precoding = false; + NR_PUSCH_Config_t pusch_Config = {0}; + current_UL_BWP.pusch_Config = &pusch_Config; + NR_PUSCH_PowerControl pusch_PowerControl = {0}; + pusch_Config.pusch_PowerControl = &pusch_PowerControl; + long p0_NominalWithGrant = 0; + current_UL_BWP.p0_NominalWithGrant = &p0_NominalWithGrant; + int power = get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + transform_precoding); + num_rb = 100; + sum_bits_in_codeblocks = nr_compute_tbs(Qm, R, num_rb, nb_symb_sch, nb_dmrs_prb, 0, 0, 1); + + int power_100_prbs = get_pusch_tx_power_ue(&mac, + num_rb, + start_prb, + nb_symb_sch, + nb_dmrs_prb, + nb_ptrs_prb, + Qm, + R, + beta_offset_csi1, + sum_bits_in_codeblocks, + delta_pusch, + is_rar_tx_retx, + transform_precoding); + EXPECT_GT(power_100_prbs, power); +} + int main(int argc, char** argv) { logInit(); From 373b7d9e375fde357c4912f093d3524e46da7fca Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Wed, 10 Jul 2024 17:36:10 +0200 Subject: [PATCH 16/26] Reset NR UE PUSCH power control state if alpha or p0 is modified by gNB --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index d8b1ac158d..8f92ec9a20 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -872,14 +872,21 @@ void nr_rrc_mac_config_req_mib(module_id_t module_id, nr_ue_decode_mib(mac, cc_idP); } -static void setup_puschpowercontrol(NR_PUSCH_PowerControl_t *source, NR_PUSCH_PowerControl_t *target) +static void setup_puschpowercontrol(NR_UE_MAC_INST_t *mac, NR_PUSCH_PowerControl_t *source, NR_PUSCH_PowerControl_t *target) { UPDATE_IE(target->tpc_Accumulation, source->tpc_Accumulation, long); UPDATE_IE(target->msg3_Alpha, source->msg3_Alpha, NR_Alpha_t); if (source->p0_NominalWithoutGrant) UPDATE_IE(target->p0_NominalWithoutGrant, source->p0_NominalWithoutGrant, long); - if (source->p0_AlphaSets) + if (source->p0_AlphaSets) { UPDATE_IE(target->p0_AlphaSets, source->p0_AlphaSets, struct NR_PUSCH_PowerControl__p0_AlphaSets); + if (target->p0_AlphaSets->list.array[0]->alpha) { + mac->f_b_f_c = 0; + } + if (target->p0_AlphaSets->list.array[0]->p0) { + mac->f_b_f_c = 0; + } + } UPDATE_IE(target->twoPUSCH_PC_AdjustmentStates, source->twoPUSCH_PC_AdjustmentStates, long); UPDATE_IE(target->deltaMCS, source->deltaMCS, long); if (source->pathlossReferenceRSToReleaseList) { @@ -901,6 +908,7 @@ static void setup_puschpowercontrol(NR_PUSCH_PowerControl_t *source, NR_PUSCH_Po sri_PUSCH_PowerControlId); } if (source->sri_PUSCH_MappingToAddModList) { + LOG_E(NR_MAC, "NR_SRI_PUSCH_PowerControl not implemented, power control will not work as intended\n"); if (!target->sri_PUSCH_MappingToAddModList) target->sri_PUSCH_MappingToAddModList = calloc(1, sizeof(*target->sri_PUSCH_MappingToAddModList)); ADDMOD_IE_FROMLIST(source->sri_PUSCH_MappingToAddModList, @@ -910,7 +918,7 @@ static void setup_puschpowercontrol(NR_PUSCH_PowerControl_t *source, NR_PUSCH_Po } } -static void setup_puschconfig(NR_PUSCH_Config_t *source, NR_PUSCH_Config_t *target) +static void setup_puschconfig(NR_UE_MAC_INST_t *mac, NR_PUSCH_Config_t *source, NR_PUSCH_Config_t *target) { UPDATE_IE(target->dataScramblingIdentityPUSCH, source->dataScramblingIdentityPUSCH, long); UPDATE_IE(target->txConfig, source->txConfig, long); @@ -927,7 +935,7 @@ static void setup_puschconfig(NR_PUSCH_Config_t *source, NR_PUSCH_Config_t *targ if (source->pusch_PowerControl) { if (!target->pusch_PowerControl) target->pusch_PowerControl = calloc(1, sizeof(*target->pusch_PowerControl)); - setup_puschpowercontrol(source->pusch_PowerControl, target->pusch_PowerControl); + setup_puschpowercontrol(mac, source->pusch_PowerControl, target->pusch_PowerControl); } UPDATE_IE(target->frequencyHopping, source->frequencyHopping, long); if (source->frequencyHoppingOffsetLists) @@ -1415,7 +1423,7 @@ static void configure_dedicated_BWP_ul(NR_UE_MAC_INST_t *mac, int bwp_id, NR_BWP if (ul_dedicated->pusch_Config->present == NR_SetupRelease_PUSCH_Config_PR_setup) { if (!bwp->pusch_Config) bwp->pusch_Config = calloc(1, sizeof(*bwp->pusch_Config)); - setup_puschconfig(ul_dedicated->pusch_Config->choice.setup, bwp->pusch_Config); + setup_puschconfig(mac, ul_dedicated->pusch_Config->choice.setup, bwp->pusch_Config); } } if(ul_dedicated->srs_Config) { From 04e6828c62cd626505a54414799e870882dc82cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Soares=20Silva?= Date: Mon, 1 Jul 2024 10:36:30 +0100 Subject: [PATCH 17/26] Change functions using RX_DATA.indication PDU_Length to expect 32 bits --- nfapi/open-nFAPI/nfapi/src/nfapi_p7.c | 41 +++++++++---------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 4 +- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 2 +- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c index b856e6b648..9622cd7f5e 100644 --- a/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c +++ b/nfapi/open-nFAPI/nfapi/src/nfapi_p7.c @@ -3177,18 +3177,16 @@ uint8_t pack_nr_slot_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t * static uint8_t pack_nr_rx_data_indication_body(nfapi_nr_rx_data_pdu_t *value, uint8_t **ppWritePackedMsg, uint8_t *end) { - AssertFatal(value->pdu_length <= 0xFFFF,"RX_DATA.indication PDU_Length should be within 16 bit, according to SCF222.10.02"); - if(!(push32(value->handle, ppWritePackedMsg, end) && - push16(value->rnti, ppWritePackedMsg, end) && - push8(value->harq_id, ppWritePackedMsg, end) && - push16(value->pdu_length, ppWritePackedMsg, end) && - push8(value->ul_cqi, ppWritePackedMsg, end) && - push16(value->timing_advance, ppWritePackedMsg, end) && - push16(value->rssi, ppWritePackedMsg, end) - )) + if (!(push32(value->handle, ppWritePackedMsg, end) + && push16(value->rnti, ppWritePackedMsg, end) + && push8(value->harq_id, ppWritePackedMsg, end) + && push32(value->pdu_length, ppWritePackedMsg, end) + && push8(value->ul_cqi, ppWritePackedMsg, end) + && push16(value->timing_advance, ppWritePackedMsg, end) + && push16(value->rssi, ppWritePackedMsg, end))) return 0; - if(pusharray8(value->pdu, value->pdu_length, value->pdu_length, ppWritePackedMsg, end) == 0) + if (pusharray8(value->pdu, value->pdu_length, value->pdu_length, ppWritePackedMsg, end) == 0) return 0; return 1; @@ -6066,19 +6064,18 @@ static uint8_t unpack_nr_rx_data_indication_body(nfapi_nr_rx_data_pdu_t *value, uint8_t *end, nfapi_p7_codec_config_t *config) { -if (!(pull32(ppReadPackedMsg, &value->handle, end) && pull16(ppReadPackedMsg, &value->rnti, end) - && pull8(ppReadPackedMsg, &value->harq_id, end) && pull16(ppReadPackedMsg, (uint16_t *)&value->pdu_length, end) - && pull8(ppReadPackedMsg, &value->ul_cqi, end) && pull16(ppReadPackedMsg, &value->timing_advance, end) - && pull16(ppReadPackedMsg, &value->rssi, end))) - return 0; + if (!(pull32(ppReadPackedMsg, &value->handle, end) && pull16(ppReadPackedMsg, &value->rnti, end) + && pull8(ppReadPackedMsg, &value->harq_id, end) && pull32(ppReadPackedMsg, &value->pdu_length, end) + && pull8(ppReadPackedMsg, &value->ul_cqi, end) && pull16(ppReadPackedMsg, &value->timing_advance, end) + && pull16(ppReadPackedMsg, &value->rssi, end))) + return 0; -uint32_t length = value->pdu_length; -value->pdu = nfapi_p7_allocate(sizeof(*value->pdu) * length, config); -if (pullarray8(ppReadPackedMsg, value->pdu, length, length, end) == 0) { - NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s pullarray8 failure\n", __FUNCTION__); - return 0; -} -return 1; + value->pdu = nfapi_p7_allocate(sizeof(*value->pdu) * value->pdu_length, config); + if (pullarray8(ppReadPackedMsg, value->pdu, value->pdu_length, value->pdu_length, end) == 0) { + NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s pullarray8 failure\n", __FUNCTION__); + return 0; + } + return 1; } static uint8_t unpack_nr_rx_data_indication(uint8_t **ppReadPackedMsg, uint8_t *end, nfapi_nr_rx_data_indication_t *msg, nfapi_p7_codec_config_t* config) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 89c684ee41..a7425067f8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -644,7 +644,7 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP, const sub_frame_t slotP, const rnti_t rntiP, uint8_t *sduP, - const uint16_t sdu_lenP, + const uint32_t sdu_lenP, const uint16_t timing_advance, const uint8_t ul_cqi, const uint16_t rssi) @@ -939,7 +939,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, const sub_frame_t slotP, const rnti_t rntiP, uint8_t *sduP, - const uint16_t sdu_lenP, + const uint32_t sdu_lenP, const uint16_t timing_advance, const uint8_t ul_cqi, const uint16_t rssi) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index a23e588a49..fe2fec8ab1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -343,7 +343,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP, const sub_frame_t subframeP, const rnti_t rntiP, uint8_t * sduP, - const uint16_t sdu_lenP, + const uint32_t sdu_lenP, const uint16_t timing_advance, const uint8_t ul_cqi, const uint16_t rssi); From be9b976023f0b0f54a89c21d0985943f2f3076a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Soares=20Silva?= Date: Thu, 11 Jul 2024 14:36:04 +0100 Subject: [PATCH 18/26] Update references of sdu_lenP to uint32_t in _nr_rx_sdu --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index a7425067f8..3e748f4217 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -725,10 +725,10 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP, #if defined(ENABLE_MAC_PAYLOAD_DEBUG) LOG_I(NR_MAC, "Printing received UL MAC payload at gNB side: %d \n"); - for (int i = 0; i < sdu_lenP ; i++) { - //harq_process_ul_ue->a[i] = (unsigned char) rand(); - //printf("a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]); - printf("%02x ",(unsigned char)sduP[i]); + for (uint32_t i = 0; i < sdu_lenP; i++) { + // harq_process_ul_ue->a[i] = (unsigned char) rand(); + // printf("a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]); + printf("%02x ", (unsigned char)sduP[i]); } printf("\n"); @@ -770,8 +770,8 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP, } else if (sduP) { bool no_sig = true; - for (int k = 0; k < sdu_lenP; k++) { - if(sduP[k] != 0) { + for (uint32_t k = 0; k < sdu_lenP; k++) { + if (sduP[k] != 0) { no_sig = false; break; } @@ -856,7 +856,7 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP, } else { LOG_A(NR_MAC, "[RAPROC] RA-Msg3 received (sdu_lenP %d)\n", sdu_lenP); LOG_D(NR_MAC, "[RAPROC] Received Msg3:\n"); - for (int k = 0; k < sdu_lenP; k++) { + for (uint32_t k = 0; k < sdu_lenP; k++) { LOG_D(NR_MAC, "(%i): 0x%x\n", k, sduP[k]); } From 22ba6b751d5c42a7c1713d3fe94c6123f81c3dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Soares=20Silva?= Date: Thu, 11 Jul 2024 15:00:04 +0100 Subject: [PATCH 19/26] Update sdu_lenP to uint32_t in lcid_crnti_lookahead and nr_process_mac_pdu --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 3e748f4217..f726ef136b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -40,7 +40,7 @@ //#define SRS_IND_DEBUG -static rnti_t lcid_crnti_lookahead(uint8_t *pdu, int pdu_len) +static rnti_t lcid_crnti_lookahead(uint8_t *pdu, uint32_t pdu_len) { uint16_t mac_len = 0; uint16_t mac_subheader_len = 0; @@ -200,7 +200,7 @@ static int nr_process_mac_pdu(instance_t module_idP, frame_t frameP, sub_frame_t slot, uint8_t *pduP, - int pdu_len, + uint32_t pdu_len, const int8_t harq_pid) { From f58765d0237c3200c8ee1d6b55bea566a80f5810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Soares=20Silva?= Date: Thu, 11 Jul 2024 16:44:20 +0100 Subject: [PATCH 20/26] Update sdu_lenP to uint32_t in get_mac_len Add check in lcid_crnti_lookahead to ensure subtraction to pdu_len doesn't cause an underflow --- openair2/LAYER2/NR_MAC_COMMON/nr_mac.h | 13 +++++++------ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 8 +++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h index 6973f06103..fbd36461e1 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h @@ -107,12 +107,13 @@ typedef struct { uint8_t R: 2; // octet 1 [7:6] } __attribute__ ((__packed__)) NR_MAC_SUBHEADER_FIXED; -static inline int get_mac_len(uint8_t* pdu, int pdu_len, uint16_t *mac_ce_len, uint16_t *mac_subheader_len) { - if ( pdu_len < (int)sizeof(NR_MAC_SUBHEADER_SHORT)) +static inline int get_mac_len(uint8_t *pdu, uint32_t pdu_len, uint16_t *mac_ce_len, uint16_t *mac_subheader_len) +{ + if (pdu_len < sizeof(NR_MAC_SUBHEADER_SHORT)) return false; - NR_MAC_SUBHEADER_SHORT *s = (NR_MAC_SUBHEADER_SHORT*) pdu; - NR_MAC_SUBHEADER_LONG *l = (NR_MAC_SUBHEADER_LONG*) pdu; - if (s->F && pdu_len < (int)sizeof(NR_MAC_SUBHEADER_LONG)) + NR_MAC_SUBHEADER_SHORT *s = (NR_MAC_SUBHEADER_SHORT *)pdu; + NR_MAC_SUBHEADER_LONG *l = (NR_MAC_SUBHEADER_LONG *)pdu; + if (s->F && pdu_len < sizeof(NR_MAC_SUBHEADER_LONG)) return false; if (s->F) { *mac_subheader_len = sizeof(*l); @@ -123,7 +124,7 @@ static inline int get_mac_len(uint8_t* pdu, int pdu_len, uint16_t *mac_ce_len, u } return true; } - + // BSR MAC CEs // TS 38.321 ch. 6.1.3.1 // Short BSR for a specific logical channel group ID diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index f726ef136b..1048be2528 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -55,7 +55,13 @@ static rnti_t lcid_crnti_lookahead(uint8_t *pdu, uint32_t pdu_len) break; } pdu += mac_len + mac_subheader_len; - pdu_len -= mac_len + mac_subheader_len; + // if pdu_len can have the value subtracted without underflow, we can subtract + if (pdu_len >= mac_len + mac_subheader_len) { + pdu_len -= mac_len + mac_subheader_len; + } else { + // if not, set to 0 to prevent underflow + pdu_len = 0; + } } return 0; } From ccddddc64e5256247ec599b4616ee06aa7f1a2cc Mon Sep 17 00:00:00 2001 From: arora Date: Thu, 11 Jul 2024 14:26:47 +0200 Subject: [PATCH 21/26] Add 7.2 container dockerfiles and update existing images - Add dedicated target docker files for 7.2, both RHEL and Ubuntu - in existing dockerfiles, update to also build E2 SMs - for UBI images, only the entitlements are necessary, the rest can be omitted (it is superfluous) Signed-off-by: arora Co-authored-by: Raphael Defosseux --- docker/Dockerfile.base.rhel9 | 24 +++-- docker/Dockerfile.base.ubuntu22 | 5 + docker/Dockerfile.build.fhi72.rhel9 | 79 ++++++++++++++++ docker/Dockerfile.build.fhi72.ubuntu22 | 91 ++++++++++++++++++ docker/Dockerfile.gNB.fhi72.rhel9 | 125 +++++++++++++++++++++++++ docker/Dockerfile.gNB.fhi72.ubuntu22 | 113 ++++++++++++++++++++++ 6 files changed, 429 insertions(+), 8 deletions(-) create mode 100644 docker/Dockerfile.build.fhi72.rhel9 create mode 100644 docker/Dockerfile.build.fhi72.ubuntu22 create mode 100644 docker/Dockerfile.gNB.fhi72.rhel9 create mode 100644 docker/Dockerfile.gNB.fhi72.ubuntu22 diff --git a/docker/Dockerfile.base.rhel9 b/docker/Dockerfile.base.rhel9 index 1fc3fa973f..03b4ccac91 100644 --- a/docker/Dockerfile.base.rhel9 +++ b/docker/Dockerfile.base.rhel9 @@ -21,12 +21,13 @@ #--------------------------------------------------------------------- # # Dockerfile for the Open-Air-Interface BUILD service -# Valid for RHEL9 +# Valid for UBI9 # #--------------------------------------------------------------------- FROM registry.access.redhat.com/ubi9/ubi:latest AS ran-base +LABEL MAINTAINER OpenAirInterface ARG NEEDED_GIT_PROXY ENV TZ=Europe/Paris ENV BUILD_UHD_FROM_SOURCE=True @@ -35,10 +36,6 @@ ENV UHD_VERSION=4.4.0.0 # Copy the entitlements COPY ./etc-pki-entitlement /etc/pki/entitlement -# Copy the subscription manager configurations -COPY ./rhsm-conf /etc/rhsm -COPY ./rhsm-ca /etc/rhsm/ca - ENV SMDEV_CONTAINER_OFF=1 #install developers pkg/repo RUN rm -f /etc/rhsm-host && \ @@ -47,11 +44,21 @@ RUN rm -f /etc/rhsm-host && \ cat /etc/os-release && \ dnf install -y \ #gcc needed for build_oai - gcc gcc-c++ \ diffutils \ file \ psmisc \ git \ + meson \ + wget \ + xz \ + pkg-config \ + pcre2-devel \ + m4 \ + python3-devel \ + numactl-libs \ + numactl-devel \ + gcc-toolset-12-gcc \ + gcc-toolset-12-gcc-c++ \ # python3-pip and pyyaml are used for conf template generation python3-pip && \ pip3 install --ignore-installed pyyaml && \ @@ -80,8 +87,9 @@ WORKDIR /oai-ran COPY oaienv . #run build_oai -I to get the builder image -RUN /bin/sh oaienv && \ +RUN /bin/sh oaienv && \ cd cmake_targets && \ mkdir -p log && \ - ./build_oai -I --install-optional-packages -w USRP && \ + scl enable gcc-toolset-12 "\ + ./build_oai -I --install-optional-packages -w USRP" && \ rm /etc/pki/entitlement/*pem diff --git a/docker/Dockerfile.base.ubuntu22 b/docker/Dockerfile.base.ubuntu22 index 9ed0cd6beb..d1b90e2dcf 100644 --- a/docker/Dockerfile.base.ubuntu22 +++ b/docker/Dockerfile.base.ubuntu22 @@ -45,6 +45,11 @@ RUN apt-get update && \ gcc-12 \ g++-12 \ xxd \ + libpcre2-dev \ + python3-dev \ + bison \ + flex \ + m4 \ # python3-pip for conf template generation python3-pip && \ update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 --slave /usr/bin/g++ g++ /usr/bin/g++-12 && \ diff --git a/docker/Dockerfile.build.fhi72.rhel9 b/docker/Dockerfile.build.fhi72.rhel9 new file mode 100644 index 0000000000..fc5570a85c --- /dev/null +++ b/docker/Dockerfile.build.fhi72.rhel9 @@ -0,0 +1,79 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# +# Dockerfile for the Open-Air-Interface RAN Build for FHI 7.2 +# Valid for UBI9 +# +#--------------------------------------------------------------------- + +FROM ran-base:latest as ran-base +ENV TZ=Europe + +RUN rm -Rf /oai-ran +WORKDIR /oai-ran +COPY . . + +## Download and build DPDK +RUN wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz && \ + tar -xvf dpdk-20.11.9.tar.xz && \ + cd dpdk-stable-20.11.9 && \ + meson build && \ + ninja -C build && \ + ninja install -C build + +## Build Fronthaul library +RUN git clone https://gerrit.o-ran-sc.org/r/o-du/phy.git /opt/phy && \ + cd /opt/phy && \ + git checkout oran_e_maintenance_release_v1.0 &&\ + git apply /oai-ran/cmake_targets/tools/oran_fhi_integration_patches/E/oaioran_E.patch && \ + cd /opt/phy/fhi_lib/lib && \ + RTE_SDK=/oai-ran/dpdk-stable-20.11.9/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1 + +FROM ran-base as ran-build-fhi72 +ARG E2AP_VERSION=E2AP_V3 +ARG KPM_VERSION=KPM_V3_00 + +## Build and install OAI +RUN /bin/sh oaienv && \ + cd cmake_targets && \ + mkdir -p log && \ + scl enable gcc-toolset-12 "\ + ./build_oai \ + --gNB \ + --build-lib 'telnetsrv enbscope uescope nrscope' \ + -t oran_fhlib_5g --cmake-opt -Dxran_LOCATION=/opt/phy/fhi_lib/lib \ + --build-e2 --cmake-opt -DXAPP_MULTILANGUAGE=OFF --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION" &&\ + # Mainly to see if the sanitize option was perfectly executed + ldd ran_build/build/nr-softmodem && \ + ldd ran_build/build/liboran_fhlib_5g.so && \ + ldd /opt/phy/fhi_lib/lib/build/libxran.so + +## Build FlexRIC for SM Models +RUN mkdir -p openair2/E2AP/flexric/build && \ + cd openair2/E2AP/flexric/build && \ + scl enable gcc-toolset-12 "\ + cmake -GNinja -DCMAKE_BUILD_TYPE=Release \ + -DXAPP_MULTILANGUAGE=OFF \ + -DKPM_VERSION=$KPM_VERSION \ + -DE2AP_VERSION=$E2AP_VERSION .. &&\ + ninja && \ + ninja install" diff --git a/docker/Dockerfile.build.fhi72.ubuntu22 b/docker/Dockerfile.build.fhi72.ubuntu22 new file mode 100644 index 0000000000..774f488ed7 --- /dev/null +++ b/docker/Dockerfile.build.fhi72.ubuntu22 @@ -0,0 +1,91 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# +# Dockerfile for the Open-Air-Interface RAN Build for FHI 7.2 +# Valid for UBUNTU 22.04 +# +#--------------------------------------------------------------------- + +FROM ran-base:latest as ran-base + +ARG E2AP_VERSION=E2AP_V3 +ARG KPM_VERSION=KPM_V3_00 +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Europe +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \ + DEBIAN_FRONTEND=noninteractive apt-get install --yes \ + meson \ + wget \ + xz-utils \ + pkg-config \ + libnuma-dev && \ + rm -rf /var/lib/apt/lists/* + +RUN rm -Rf /oai-ran +WORKDIR /oai-ran +COPY . . + +## Download and build DPDK +RUN wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz && \ + tar -xvf dpdk-20.11.9.tar.xz && \ + cd dpdk-stable-20.11.9 && \ + meson build && \ + ninja -C build && \ + ninja install -C build + +## Build Fronthaul library +RUN git clone https://gerrit.o-ran-sc.org/r/o-du/phy.git /opt/phy && \ + cd /opt/phy && \ + git checkout oran_e_maintenance_release_v1.0 &&\ + git apply /oai-ran/cmake_targets/tools/oran_fhi_integration_patches/E/oaioran_E.patch && \ + cd /opt/phy/fhi_lib/lib && \ + RTE_SDK=/oai-ran/dpdk-stable-20.11.9/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1 + +FROM ran-base as ran-build-fhi72 +## Build and install OAI +#run build_oai to build the target image +RUN /bin/sh oaienv && \ + cd cmake_targets && \ + mkdir -p log && \ + ./build_oai \ + --gNB \ + --build-lib "telnetsrv enbscope uescope nrscope" \ + -t oran_fhlib_5g --cmake-opt -Dxran_LOCATION=/opt/phy/fhi_lib/lib \ + --build-e2 --cmake-opt -DXAPP_MULTILANGUAGE=OFF --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION && \ + $BUILD_OPTION && \ + # Mainly to see if the sanitize option was perfectly executed + ldd ran_build/build/nr-softmodem && \ + ldd ran_build/build/liboran_fhlib_5g.so && \ + ldd /opt/phy/fhi_lib/lib/build/libxran.so + +## Build FlexRIC for SM Models +RUN mkdir -p openair2/E2AP/flexric/build && \ + cd openair2/E2AP/flexric/build && \ + cmake -GNinja -DCMAKE_BUILD_TYPE=Release \ + -DXAPP_MULTILANGUAGE=OFF \ + -DKPM_VERSION=$KPM_VERSION \ + -DE2AP_VERSION=$E2AP_VERSION \ + -DCMAKE_C_COMPILER=gcc-12 \ + -DCMAKE_CXX_COMPILER=g++-12 .. && \ + ninja && \ + ninja install diff --git a/docker/Dockerfile.gNB.fhi72.rhel9 b/docker/Dockerfile.gNB.fhi72.rhel9 new file mode 100644 index 0000000000..6bac3c71d9 --- /dev/null +++ b/docker/Dockerfile.gNB.fhi72.rhel9 @@ -0,0 +1,125 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# +# Dockerfile for the Open-Air-Interface 7.2 Fronthaul +# Valid for UBI9 +# +#--------------------------------------------------------------------- + +FROM ran-base:latest as gnb-base + +FROM ran-build-fhi72:latest as gnb-build + +#start from scratch for target executable +FROM registry.access.redhat.com/ubi9/ubi:latest as oai-gnb-fhi72 +ENV TZ=Europe/Paris + +# Copy the entitlements +COPY ./etc-pki-entitlement /etc/pki/entitlement + +RUN rm -f /etc/rhsm-host && \ + subscription-manager repos --enable codeready-builder-for-rhel-9-x86_64-rpms && \ + dnf update -y && \ + cat /etc/os-release && \ + dnf install -y \ + procps-ng \ + libXpm \ + libX11 \ + lksctp-tools \ + xz \ + numactl-libs \ + numactl-devel \ + pkg-config \ + pciutils \ + tzdata \ + gdb \ + python3 \ + python3-pip \ + net-tools \ + iputils && \ + pip3 install six && \ + pip3 install requests && \ + echo "/usr/local/lib" > /etc/ld.so.conf.d/local-lib.conf && \ + echo "/usr/local/lib64" >> /etc/ld.so.conf.d/local-lib.conf && \ + subscription-manager repos --disable codeready-builder-for-rhel-9-x86_64-rpms && \ + rm -rf /etc/pki/entitlement + +WORKDIR /opt/oai-gnb/bin +COPY --from=gnb-build \ + /oai-ran/cmake_targets/ran_build/build/nr-softmodem ./ +COPY ./docker/scripts/gnb_entrypoint.sh ./entrypoint.sh + +COPY --from=gnb-build \ + /oai-ran/cmake_targets/ran_build/build/librfsimulator.so \ + /oai-ran/cmake_targets/ran_build/build/liboran_fhlib_5g.so \ + /oai-ran/cmake_targets/ran_build/build/liboai_transpro.so \ + /oai-ran/cmake_targets/ran_build/build/libcoding.so \ + /oai-ran/cmake_targets/ran_build/build/libparams_libconfig.so \ + /oai-ran/cmake_targets/ran_build/build/libdfts.so \ + /oai-ran/cmake_targets/ran_build/build/libldpc*.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ci.so \ + /usr/local/lib/ + +## Libxran library +COPY --from=gnb-build \ + /opt/phy/fhi_lib/lib/build/libxran.so \ + /opt/phy/fhi_lib/lib/build/ + +## Copy all DPDK libraries as liboran_fhlib_5g.so require most of them +COPY --from=gnb-build /usr/local/lib64/ /usr/local/lib64/ + +## Copy E2 SM models +COPY --from=gnb-build /usr/local/lib64/flexric /usr/local/lib64/flexric + +# Copying file for configuration module +COPY --from=gnb-base \ + /lib64/libconfig.so.11 \ + /lib64/libforms.so.2 \ + /lib64/libblas.so.3 \ + /lib64/libcblas.so.3 \ + /lib64/liblapack.so.3 \ + /lib64/liblapacke.so.3 \ + /lib64/libicudata.so.67 \ + /lib64/libicui18n.so.67 \ + /lib64/libicuuc.so.67 \ + /lib64/ + +RUN ldconfig && \ + echo "---- ldd on executable nr-softmodem ----" && \ + ldd /opt/oai-gnb/bin/nr-softmodem && \ + echo "---- ldd on shared libraries ----" && \ + ldd /usr/local/lib/librfsimulator.so \ + /usr/local/lib/liboran_fhlib_5g.so \ + /usr/local/lib/liboai_transpro.so \ + /usr/local/lib/libcoding.so \ + /usr/local/lib/libparams_libconfig.so \ + /usr/local/lib/libdfts.so \ + /usr/local/lib/libldpc*.so \ + /usr/local/lib/libtelnetsrv.so \ + /usr/local/lib/libtelnetsrv_ci.so \ + /opt/phy/fhi_lib/lib/build/libxran.so + +WORKDIR /opt/oai-gnb +COPY --from=gnb-base /tini /tini +ENTRYPOINT ["/tini", "-v", "--", "/opt/oai-gnb/bin/entrypoint.sh"] +CMD ["/opt/oai-gnb/bin/nr-softmodem", "-O", "/opt/oai-gnb/etc/gnb.conf"] diff --git a/docker/Dockerfile.gNB.fhi72.ubuntu22 b/docker/Dockerfile.gNB.fhi72.ubuntu22 new file mode 100644 index 0000000000..5bb0ad5234 --- /dev/null +++ b/docker/Dockerfile.gNB.fhi72.ubuntu22 @@ -0,0 +1,113 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# +# Dockerfile for the Open-Air-Interface 7.2 Fronthaul +# Valid for Ubuntu22.04 +# +#--------------------------------------------------------------------- + +FROM ran-base:latest as gnb-base + +FROM ran-build-fhi72:latest as gnb-build + + +#start from scratch for target executable +FROM ubuntu:jammy as oai-gnb +ARG BUILD_OPTION +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Europe + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \ + DEBIAN_FRONTEND=noninteractive apt-get install --yes \ + software-properties-common \ + procps \ + pkg-config \ + libsctp1 \ + tzdata \ + libblas3 \ + libconfig9 \ + openssl \ + net-tools \ + iperf \ + iproute2 \ + iputils-ping \ + gdb \ + pciutils \ + libpcap-dev \ + xz-utils \ + libnuma-dev \ + python3 \ + python3-six \ + python3-requests &&\ +# if the --sanitize option was used to build, additional packages are required + /bin/bash -c 'if [[ "$BUILD_OPTION" = "--sanitize" ]]; then DEBIAN_FRONTEND=noninteractive apt-get install --yes \ + libasan5 \ + libubsan1 \ + liblapacke; fi' && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/oai-gnb/bin +COPY --from=gnb-build \ + /oai-ran/cmake_targets/ran_build/build/nr-softmodem ./ +COPY ./docker/scripts/gnb_entrypoint.sh ./entrypoint.sh + +COPY --from=gnb-build \ + /oai-ran/cmake_targets/ran_build/build/librfsimulator.so \ + /oai-ran/cmake_targets/ran_build/build/liboai_transpro.so \ + /oai-ran/cmake_targets/ran_build/build/libcoding.so \ + /oai-ran/cmake_targets/ran_build/build/libparams_libconfig.so \ + /oai-ran/cmake_targets/ran_build/build/libdfts.so \ + /oai-ran/cmake_targets/ran_build/build/libldpc*.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ci.so \ + /usr/local/lib/ + +## Libxran library +COPY --from=gnb-build \ + /opt/phy/fhi_lib/lib/build/libxran.so \ + /opt/phy/fhi_lib/lib/build/ + +## Copy all DPDK libraries as liboran_fhlib_5g.so require most of them +COPY --from=gnb-build /usr/local/lib/x86_64-linux-gnu /usr/local/lib/x86_64-linux-gnu/ + +## Copy E2 SM models +COPY --from=gnb-build /usr/local/lib/flexric /usr/local/lib/flexric + +RUN ldconfig && \ + echo "---- ldd on nr-softmodem ----" && \ + ldd /opt/oai-gnb/bin/nr-softmodem && \ + echo "---- ldd on shared libraries ----" && \ + ldd /usr/local/lib/librfsimulator.so \ + /usr/local/lib/liboai_transpro.so \ + /usr/local/lib/libcoding.so \ + /usr/local/lib/libparams_libconfig.so \ + /usr/local/lib/libdfts.so \ + /usr/local/lib/libldpc*.so \ + /usr/local/lib/libtelnetsrv.so \ + /usr/local/lib/libtelnetsrv_ci.so \ + /opt/phy/fhi_lib/lib/build/libxran.so + +WORKDIR /opt/oai-gnb +COPY --from=gnb-base /tini /tini +ENTRYPOINT ["/tini", "-v", "--", "/opt/oai-gnb/bin/entrypoint.sh"] +CMD ["/opt/oai-gnb/bin/nr-softmodem", "-O", "/opt/oai-gnb/etc/gnb.conf"] From 29542595eb91bb559a3f45b61519d4f5553ac9ff Mon Sep 17 00:00:00 2001 From: arora Date: Thu, 11 Jul 2024 14:53:45 +0200 Subject: [PATCH 22/26] Add manifests for fhi72 build on openshift Co-authored-by: Raphael Defosseux --- openshift/oai-gnb-fhi72-bc.yaml | 48 +++++++++++++++++++++++++++++++ openshift/oai-gnb-fhi72-is.yaml | 30 +++++++++++++++++++ openshift/ran-build-fhi72-bc.yaml | 39 +++++++++++++++++++++++++ openshift/ran-build-fhi72-is.yaml | 30 +++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 openshift/oai-gnb-fhi72-bc.yaml create mode 100644 openshift/oai-gnb-fhi72-is.yaml create mode 100644 openshift/ran-build-fhi72-bc.yaml create mode 100644 openshift/ran-build-fhi72-is.yaml diff --git a/openshift/oai-gnb-fhi72-bc.yaml b/openshift/oai-gnb-fhi72-bc.yaml new file mode 100644 index 0000000000..2d8861e2d6 --- /dev/null +++ b/openshift/oai-gnb-fhi72-bc.yaml @@ -0,0 +1,48 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# +kind: BuildConfig +apiVersion: build.openshift.io/v1 +metadata: + name: "oai-gnb-fhi72" +spec: + runPolicy: "Serial" + source: + type: "Binary" + secrets: + - secret: + name: etc-pki-entitlement + destinationDir: etc-pki-entitlement + configMaps: + - configMap: + name: rhsm-conf + destinationDir: rhsm-conf + - configMap: + name: rhsm-ca + destinationDir: rhsm-ca + strategy: + dockerStrategy: + dockerfilePath: "docker/Dockerfile.gNB.fhi72.rhel9" + output: + to: + kind: "ImageStreamTag" + name: "oai-gnb-fhi72:latest" diff --git a/openshift/oai-gnb-fhi72-is.yaml b/openshift/oai-gnb-fhi72-is.yaml new file mode 100644 index 0000000000..a012434295 --- /dev/null +++ b/openshift/oai-gnb-fhi72-is.yaml @@ -0,0 +1,30 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: oai-gnb-fhi72 + namespace: oaicicd-ran +spec: + lookupPolicy: + local: true diff --git a/openshift/ran-build-fhi72-bc.yaml b/openshift/ran-build-fhi72-bc.yaml new file mode 100644 index 0000000000..35a7bd55b0 --- /dev/null +++ b/openshift/ran-build-fhi72-bc.yaml @@ -0,0 +1,39 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# +kind: BuildConfig +apiVersion: build.openshift.io/v1 +metadata: + name: "ran-build-fhi72" +spec: + runPolicy: "Serial" + nodeSelector: + kubernetes.io/hostname: demophon + source: + type: "Binary" + strategy: + dockerStrategy: + dockerfilePath: "docker/Dockerfile.build.fhi72.rhel9" + output: + to: + kind: "ImageStreamTag" + name: "ran-build-fhi72:latest" diff --git a/openshift/ran-build-fhi72-is.yaml b/openshift/ran-build-fhi72-is.yaml new file mode 100644 index 0000000000..eb5f9bebf6 --- /dev/null +++ b/openshift/ran-build-fhi72-is.yaml @@ -0,0 +1,30 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: ran-build-fhi72 + namespace: oaicicd-ran +spec: + lookupPolicy: + local: true From e9b5d84eb451ebd2f4780df35a8da495cf7e9c37 Mon Sep 17 00:00:00 2001 From: arora Date: Thu, 11 Jul 2024 14:33:00 +0200 Subject: [PATCH 23/26] Add docker compose files for deploying 7.2 - Add docker-compose.yml - Write additional documentation --- .../yaml_files/sa_fhi_7.2_gnb/README.md | 87 +++++++ .../sa_fhi_7.2_gnb/docker-compose.yml | 34 +++ doc/images/docker-deploy-oai-7-2.drawio.xml | 214 ++++++++++++++++++ doc/images/docker-deploy-oai-7-2.png | Bin 0 -> 95930 bytes 4 files changed, 335 insertions(+) create mode 100644 ci-scripts/yaml_files/sa_fhi_7.2_gnb/README.md create mode 100644 ci-scripts/yaml_files/sa_fhi_7.2_gnb/docker-compose.yml create mode 100644 doc/images/docker-deploy-oai-7-2.drawio.xml create mode 100644 doc/images/docker-deploy-oai-7-2.png diff --git a/ci-scripts/yaml_files/sa_fhi_7.2_gnb/README.md b/ci-scripts/yaml_files/sa_fhi_7.2_gnb/README.md new file mode 100644 index 0000000000..1acbe7fb86 --- /dev/null +++ b/ci-scripts/yaml_files/sa_fhi_7.2_gnb/README.md @@ -0,0 +1,87 @@ + + + + + +
+ + + + + + OAI O-RAN 7.2 Front-haul Docker Compose +
+ +![Docker deploy 7.2](../../../doc/images/docker-deploy-oai-7-2.png) + +This docker-compose is designed to use `OAI-gNB` with a 7.2 compatible Radio Unit. Before using this docker compose you have to configure +the host machine as per the [ORAN_FHI7.2_Tutorial](../../../doc/ORAN_FHI7.2_Tutorial.md). The container image used by the docker compose file is tested only on `Ubuntu 22.04` docker host. + +## Build Image (Optional) + +Refer to [OAI Docker/Podman Build and Usage Procedures](../../../docker/README.md) + +## Configure Networking + +### SR-IOV Virtual Functions (VFs) + +In docker-compose environment there is no automated method +to configure the VFs on the fly. The user will have to manually configure +C/U plane VFs before starting the container `OAI-gNB`. + +You can follow the step +[configure-network-interfaces-and-dpdk-vfs](../../../doc/ORAN_FHI7.2_Tutorial.md#configure-network-interfaces-and-dpdk-vfs). + +### Interface towards AMF (N2) + +For `N2` interface we are using `macvlan` driver of docker. + +You can use the `bridge` driver, in situation + +- When the core network is running on the same machine +- or different machine but you have configured +needed `ip route` and forwarding to access the core network from RAN host. + +To configure docker `macvlan` network +you need to choose `ipam.config` and `driver_opts.parent` are per your environment + +``` + oai-net: + driver: macvlan + name: oai-net + ipam: + config: + - subnet: "172.21.16.0/22" + ip_range: "172.21.18.20/32" + gateway: "172.21.19.254" + driver_opts: + com.docker.network.bridge.name: "oai-net" + parent: enp193s0f0 +``` + +To configure `bridge` network you need to choose `ipam.config.subnet` as per your environment. + +``` + oai-net: + driver: bridge + name: oai-net + ipam: + config: + - subnet: 192.168.72.128/26 + driver_opts: + com.docker.network.bridge.name: "oai-net" +``` + +## Deploy OAI-gNB Container + +The [configuration file](../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel650.conf) used by docker compose is configured for Benetel 650 RU (RAN650-1v1.0.4-dda1bf5). + +```bash +docker-compose up -d +``` + +To check the logs + +```bash +docker logs oai-gnb -f +``` diff --git a/ci-scripts/yaml_files/sa_fhi_7.2_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_fhi_7.2_gnb/docker-compose.yml new file mode 100644 index 0000000000..e3e93b3b74 --- /dev/null +++ b/ci-scripts/yaml_files/sa_fhi_7.2_gnb/docker-compose.yml @@ -0,0 +1,34 @@ +services: + oai-gnb: + image: oai-gnb-fhi72:latest + privileged: true + container_name: oai-gnb + environment: + TZ: Europe/Paris + USE_ADDITIONAL_OPTIONS: --sa --thread-pool 13,14,15,16,17,18 + devices: + - /dev/vfio:/dev/vfio/ + volumes: + - ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel650.conf:/opt/oai-gnb/etc/gnb.conf + - /dev/hugepages:/dev/hugepages + cpuset: "0,1,2,8,9,10,13,14,15,16,17,18" + networks: + oai-net: + ipv4_address: 172.21.18.20 + healthcheck: + test: /bin/bash -c "pgrep nr-softmodem" + interval: 10s + timeout: 5s + retries: 5 +networks: + oai-net: + driver: macvlan + name: oai-net + ipam: + config: + - subnet: "172.21.16.0/22" + ip_range: "172.21.18.20/32" + gateway: "172.21.19.254" + driver_opts: + com.docker.network.bridge.name: "oai-net" + parent: enp193s0f0 diff --git a/doc/images/docker-deploy-oai-7-2.drawio.xml b/doc/images/docker-deploy-oai-7-2.drawio.xml new file mode 100644 index 0000000000..eadd0a5bf5 --- /dev/null +++ b/doc/images/docker-deploy-oai-7-2.drawio.xml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/images/docker-deploy-oai-7-2.png b/doc/images/docker-deploy-oai-7-2.png new file mode 100644 index 0000000000000000000000000000000000000000..58c323b9de689ba3373613dfbeed3b85b9746ed9 GIT binary patch literal 95930 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 literal 0 HcmV?d00001 From 5524b2c7067428b944a7a2964f26fb88026549ed Mon Sep 17 00:00:00 2001 From: arora Date: Thu, 11 Jul 2024 14:27:13 +0200 Subject: [PATCH 24/26] Update Openshift README to describe Openshift build --- openshift/README.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/openshift/README.md b/openshift/README.md index 9f0923bd60..5560621e68 100644 --- a/openshift/README.md +++ b/openshift/README.md @@ -27,36 +27,37 @@ the ran-build image. If you reinstalled dependencies, you have to restart from ran-base. You first need to login. Do: + ```bash oc login -u -p --server ``` -## RHEL8 Entitlements +Create a project/namespace where you will create your images -To build the RAN images, we use the `codeready-builder-for-rhel-8-x86_64-rpms` repository with all the proper development libraries. -This repository is not directly accessible from the UBI RHEL8 image (`registry.access.redhat.com/ubi8/ubi:latest`). -Therefore, we need to copy certificates and subscription manager configuration files from a registered RHEL8 machine. +```bash +oc new-project oaicicd-ran +``` -on a `RHEL8` physical machine (or a virtual machine) connected to the OpenShift Cluster, recover the entitlement and the RH subscription manager configs: +**NOTE**: If you change the project name then you have to change it in all the `yaml` files. + +## RHEL9 Entitlements + +To build the RAN images, we use the `codeready-builder-for-rhel-9-x86_64-rpms` +repository with all the proper development libraries. To access the library you +will need `etc-pki-entitlement` inside the container image. + +To import `etc-pki-entitlement` in your project follow this +[guide](https://docs.openshift.com/container-platform/4.14/cicd/builds/running-entitled-builds.html#builds-source-secrets-entitlements_running-entitled-builds) + +In case you have difficulties in following it then you can copy the +certificates from an RHEL9 host ```bash oc create configmap rhsm-conf --from-file /etc/rhsm/rhsm.conf oc create configmap rhsm-ca --from-file /etc/rhsm/ca/redhat-uep.pem - oc create secret generic etc-pki-entitlement --from-file /etc/pki/entitlement/{NUMBER_ON_YOUR_COMPUTER}.pem --from-file /etc/pki/entitlement/{NUMBER_ON_YOUR_COMPUTER}-key.pem ``` -These configmaps and secret will be shared by all the build configs in your OC -project. No need to do it each time. However, these files expire every month -or so. If you have done a build on your OC project and try again a few weeks -later, you may need to re-copy them. (The CI recopies them every time.) - -```bash -oc delete secret etc-pki-entitlement -oc delete cm rhsm-conf -oc delete cm rhsm-ca -``` - # 2. Build of `base` shared image Create an image stream and build config that specify image properties and build parameters, then start the build: From fe866405e75dace6b06fedf03823d181fdd8c4a5 Mon Sep 17 00:00:00 2001 From: arora Date: Wed, 10 Jul 2024 17:39:21 +0200 Subject: [PATCH 25/26] Update the build strategy documentation - adding information related to 7.2 images - correcting typos and syntax --- docker/README.md | 114 ++++++++++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 47 deletions(-) diff --git a/docker/README.md b/docker/README.md index 483a049f9d..7ec63aacae 100644 --- a/docker/README.md +++ b/docker/README.md @@ -33,29 +33,30 @@ For all platforms, the strategy for building docker/podman images is the same: into which all targets are compiled. * Then from the `ran-build` shared image, we can build target images for: - eNB - - gNB (with UHD) - - gNB (with AW2S), only on RHEL9 + - gNB/DU (with UHD) + - gNB/DU (with AW2S), only on RHEL9 - lte-UE - nr-UE - + - nr-cuup These target images will only contain: - the generated executable (for example `lte-softmodem`) - the generated shared libraries (for example `liboai_usrpdevif.so`) - the needed libraries and packages to run these generated binaries - Some configuration file templates - Some tools (such as `ping`, `ifconfig`) +* From the `ran-build-fhi72` image, we can build target image for: + - gNB/DU (with FHI 7.2) Note that on every push to develop (i.e., typically after integrating merge requests), we build all images and push them to [Docker Hub](https://hub.docker.com/u/oaisoftwarealliance). To pull them, do -``` +```bash docker pull oaisoftwarealliance/oai-gnb:develop docker pull oaisoftwarealliance/oai-nr-ue:develop docker pull oaisoftwarealliance/oai-enb:develop docker pull oaisoftwarealliance/oai-lte-ue:develop ``` -Have a look at [this README](../ci-scripts/yaml_files/5g_rfsimulator/README.md) to get some -information on how to use the images. +Have a look at [this README](../ci-scripts/yaml_files/5g_rfsimulator/README.md) to get some information on how to use the images. # 2. File organization # @@ -65,18 +66,20 @@ Targets can be: - `base` for an image named `ran-base` (shared image) - `build` for an image named `ran-build` (shared image) +- `build.fhi72` for an image named `ran-build-fhi72` - `eNB` for an image named `oai-enb` - `gNB` for an image named `oai-gnb` - `nr-cuup` for an image named `oai-nr-cuup` - `gNB.aw2s` for an image named `oai-gnb-aw2s` +- `gNB.fhi` for an image named `oai-gnb-fhi72` - `lteUE` for an image named `oai-lte-ue` - `nrUE` for an image named `oai-nr-ue` The currently-supported OS are: -- `rhel9` for Red Hat Entreprise Linux (including images for an OpenShift cluster) +- `rhel9` for Red Hat Enterprise Linux and Openshift Universal Base Image - `ubuntu22` for Ubuntu 22.04 LTS -- `rocky` for Rocky-Linux 8.7 +- `rocky` for Rocky-Linux 9 For more details regarding the build on an Openshift Cluster, see [OpenShift README](../openshift/README.md). @@ -95,25 +98,28 @@ There are two shared images: one that has all dependencies, and a second that co ```bash git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git cd openairinterface5g -git checkout develop +# default branch is develop, to change use git checkout docker build --target ran-base --tag ran-base:latest --file docker/Dockerfile.base.ubuntu22 . +# if you want use USRP, AW2S and RFSimulator radios docker build --target ran-build --tag ran-build:latest --file docker/Dockerfile.build.ubuntu22 . +# if you want to use front-haul 7.2 and RFSimulator radios +docker build --tag ran-build-fhi72:latest --file docker/Dockerfile.build.fhi72.ubuntu22 . ``` -After building both: +After building: ```bash docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE ran-build latest f2633a7f5102 1 minute ago 6.81GB ran-base latest 5c9c02a5b4a8 1 minute ago 2.4GB - +ran-build-fhi72 latest 5190c06dbb4d 1 minute ago 6.86GB ... ``` Note that the steps are identical for `rocky-linux`. -### 3.2.1. Additional build otions +### 3.2.1. Additional build options This is only available for the Ubuntu version of Dockerfiles. @@ -149,13 +155,18 @@ For example, the eNB: docker build --target oai-enb --tag oai-enb:latest --file docker/Dockerfile.eNB.ubuntu22 . ``` +To build gNB/DU with 7.2 fronthaul support: + +```bash +docker build --target oai-gnb-fhi72 --tag oai-gnb-fhi72:latest --file docker/Dockerfile.gNB.fhi72.ubuntu22 . +``` + After a while: ``` docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE oai-enb latest 25ddbd8b7187 1 minute ago 516MB - 875ea3b05b60 8 minutes ago 8.18GB ran-build latest f2633a7f5102 1 hour ago 6.81GB ran-base latest 5c9c02a5b4a8 1 hour ago 2.4GB ``` @@ -176,16 +187,33 @@ docker build --target oai-gnb --tag oai-gnb:latest --file docker/Dockerfile.gNB. Normally the target image will be around 200 Mbytes bigger. -# 4. Building using `podman` under Red Hat Entreprise Linux 8.2 # +# 4. Building using `podman` under Red Hat Entreprise Linux 9.X # Analogous to the above steps: -``` -sudo podman build --target ran-base --tag ran-base:latest --file docker/Dockerfile.base.rhel9 . -sudo podman build --target ran-build --tag ran-build:latest --file docker/Dockerfile.build.rhel9 . -sudo podman build --target oai-enb --tag oai-enb:latest --file docker/Dockerfile.eNB.rhel9 . + +```bash +podman build --target ran-base --tag ran-base:latest --file docker/Dockerfile.base.rhel9 . +# if you want use USRP, AW2S and RFSimulator radios +podman build --target ran-build --tag ran-build:latest --file docker/Dockerfile.build.rhel9 . +# if you want to use front-haul 7.2 and RFSimulator radios +podman build --tag ran-build-fhi72:latest --file docker/Dockerfile.build.fhi72.rhel9 . ``` -# 5. Running modems using `docker` under Ubuntu 18.04 # +## 4.1 Building target images + +For example, the eNB: + +```bash +podman build --target oai-enb --tag oai-gnb:latest --file docker/Dockerfile.eNB.rhel9 . +``` + +To build gNB/DU with 7.2 fronthaul support: + +```bash +podman build --target oai-gnb-fhi72 --tag oai-gnb-fhi72:latest --file docker/Dockerfile.gNB.fhi72.rhel9 . +``` + +# 5. Running modems using `docker` under Ubuntu # The easiest is to run them from a `docker-compose` file, which is used by the CI to test OAI. Some folders under `ci-scripts/yaml_files` have a README that @@ -200,55 +228,47 @@ file](../ci-scripts/yaml_files/sa_b200_gnb/docker-compose.yml). It is also possible to mount your own configuration file. The following docker-compose file can be used to start a gNB using a B210 and your own config, located at `/tmp/gnb.conf`: -``` -version: '3.8' +```yaml services: gnb_mono_tdd: image: oai-gnb:latest - privileged: true container_name: sa-b200-gnb + cap_drop: + - ALL + cap_add: + - SYS_NICE + - IPC_LOCK + ulimits: + core: -1 # for core dumps environment: USE_B2XX: 'yes' - USE_ADDITIONAL_OPTIONS: --sa -E --continuous-tx + USE_ADDITIONAL_OPTIONS: --sa --RUs.[0].sdr_addrs serial=30C51D4 --telnetsrv --telnetsrv.shrmod ci --continuous-tx --log_config.global_log_options level,nocolor,time,line_num,function + devices: + - /dev/bus/usb/:/dev/bus/usb/ volumes: - - /dev:/dev - - /tmp/gnb.conf:/opt/oai-gnb/etc/gnb.conf - networks: - public_net: - ipv4_address: 192.168.68.194 + - ../../conf_files/gnb.sa.band78.51prb.usrpb200.conf:/opt/oai-gnb/etc/gnb.conf + # for performance reasons, we use host mode: in bridge mode, we have + # unacceptable DL TCP performance. However, the whole point of + # containerization is to not be in host mode, so update this to macvlan + # later. + network_mode: "host" + #entrypoint: /bin/bash -c "sleep infinity" healthcheck: # pgrep does NOT work test: /bin/bash -c "ps aux | grep -v grep | grep -c softmodem" interval: 10s timeout: 5s retries: 5 - -networks: - public_net: - name: sa-b200-gnb-net - ipam: - config: - - subnet: 192.168.68.192/26 - driver_opts: - com.docker.network.bridge.name: "sa-gnb-net" ``` You should also change the `image` to the right image name and tag of the gNB you are using. Start like this: -``` +```bash docker-compose up # gNB in foreground docker-compose up -d # gNB in background ``` Stop it like this (in both cases): -``` +```bash docker-compose down ``` - -Note that in the above case, ALL devices are passed into the container (by -mounting `/dev`), which allows the container to access all devices connected to -the host! - -# 6. Running modems using `podman` under Red Hat Entreprise Linux 8.2 # - -TODO. From 9beb1323e44eee3d598e9b91c988e58346c06c57 Mon Sep 17 00:00:00 2001 From: Raphael Defosseux Date: Thu, 11 Jul 2024 14:23:40 +0200 Subject: [PATCH 26/26] chore(ci): adding fhi Ubuntu/UBI build and docker-hub push to CI pipeline Signed-off-by: Raphael Defosseux --- ci-scripts/Jenkinsfile-push-registry | 2 +- ci-scripts/cls_cluster.py | 28 ++++++++++++++++++++++++++++ ci-scripts/cls_containerize.py | 8 ++++++-- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/ci-scripts/Jenkinsfile-push-registry b/ci-scripts/Jenkinsfile-push-registry index 541999e8ff..d3bd5e6e64 100644 --- a/ci-scripts/Jenkinsfile-push-registry +++ b/ci-scripts/Jenkinsfile-push-registry @@ -76,7 +76,7 @@ pipeline { withCredentials([ [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.DH_Credentials}", usernameVariable: 'DH_Username', passwordVariable: 'DH_Password'] ]) { - def listOfImages = ["oai-enb", "oai-gnb", "oai-lte-ue", "oai-nr-ue", "oai-nr-cuup"] + def listOfImages = ["oai-enb", "oai-gnb", "oai-lte-ue", "oai-nr-ue", "oai-nr-cuup", "oai-gnb-fhi72"] // Logging in on both registries sh "docker login -u ${DH_Username} -p ${DH_Password} > /dev/null 2>&1" sh "docker login -u oaicicd -p oaicicd porcepix.sboai.cs.eurecom.fr > /dev/null 2>&1" diff --git a/ci-scripts/cls_cluster.py b/ci-scripts/cls_cluster.py index d35b73890b..80d6d95daa 100644 --- a/ci-scripts/cls_cluster.py +++ b/ci-scripts/cls_cluster.py @@ -462,6 +462,34 @@ class Cluster: self.cmd.run(f'oc logs {nrue_job} &> cmake_targets/log/oai-nr-ue.log') self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log', '\$', 10) + if status: + self._recreate_is_tag('ran-build-fhi72', imageTag, 'openshift/ran-build-fhi72-is.yaml') + self._recreate_bc('ran-build-fhi72', imageTag, 'openshift/ran-build-fhi72-bc.yaml') + self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.build.fhi72.rhel9') + ranbuildfhi72_job = self._start_build('ran-build-fhi72') + attemptedImages += ['ran-build-fhi72'] + + wait = ranbuildfhi72_job is not None and self._wait_build_end([ranbuildfhi72_job], 1200) + if not wait: logging.error('error during build of ranbuildfhi72_job') + status = status and wait + self.cmd.run(f'oc logs {ranbuildfhi72_job} &> cmake_targets/log/ran-build-fhi72.log') + self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log', '\$', 10) + + if status: + self._recreate_is_tag('oai-gnb-fhi72', imageTag, 'openshift/oai-gnb-fhi72-is.yaml') + self._recreate_bc('oai-gnb-fhi72', imageTag, 'openshift/oai-gnb-fhi72-bc.yaml') + self._retag_image_statement('ran-base', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-base', baseTag, 'docker/Dockerfile.gNB.fhi72.rhel9') + self._retag_image_statement('ran-build-fhi72', 'image-registry.openshift-image-registry.svc:5000/oaicicd-ran/ran-build-fhi72', imageTag, 'docker/Dockerfile.gNB.fhi72.rhel9') + gnb_fhi72_job = self._start_build('oai-gnb-fhi72') + attemptedImages += ['oai-gnb-fhi72'] + + wait = gnb_fhi72_job is not None and self._wait_build_end([gnb_fhi72_job], 600) + if not wait: logging.error('error during build of gNB-fhi72') + status = status and wait + # recover logs + self.cmd.run(f'oc logs {gnb_fhi72_job} &> cmake_targets/log/oai-gnb-fhi72.log') + self.cmd.run(f'oc get pods.metrics.k8s.io &>> cmake_targets/log/build-metrics.log', '\$', 10) + # split and analyze logs imageSize = {} for image in attemptedImages: diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 911a6555b5..94d7526813 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -58,7 +58,7 @@ import cls_oaicitest # Helper functions used here and in other classes # (e.g., cls_cluster.py) #----------------------------------------------------------- -IMAGES = ['oai-enb', 'oai-lte-ru', 'oai-lte-ue', 'oai-gnb', 'oai-nr-cuup', 'oai-gnb-aw2s', 'oai-nr-ue', 'oai-gnb-asan', 'oai-nr-ue-asan', 'oai-nr-cuup-asan', 'oai-gnb-aerial'] +IMAGES = ['oai-enb', 'oai-lte-ru', 'oai-lte-ue', 'oai-gnb', 'oai-nr-cuup', 'oai-gnb-aw2s', 'oai-nr-ue', 'oai-gnb-asan', 'oai-nr-ue-asan', 'oai-nr-cuup-asan', 'oai-gnb-aerial', 'oai-gnb-fhi72'] def CreateWorkspace(sshSession, sourcePath, ranRepository, ranCommitID, ranTargetBranch, ranAllowMerge): if ranCommitID == '': @@ -319,6 +319,8 @@ class Containerize(): imageNames.append(('oai-gnb', 'gNB', 'oai-gnb-asan', '--build-arg "BUILD_OPTION=--sanitize"')) imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue-asan', '--build-arg "BUILD_OPTION=--sanitize"')) imageNames.append(('oai-nr-cuup', 'nr-cuup', 'oai-nr-cuup-asan', '--build-arg "BUILD_OPTION=--sanitize"')) + imageNames.append(('ran-build-fhi72', 'build.fhi72', 'ran-build-fhi72', '')) + imageNames.append(('oai-gnb', 'gNB.fhi72', 'oai-gnb-fhi72', '')) result = re.search('build_cross_arm64', self.imageKind) if result is not None: self.dockerfileprefix = '.ubuntu22.cross-arm64' @@ -409,6 +411,8 @@ class Containerize(): # target images should use the proper ran-build image if image != 'ran-build' and "-asan" in name: cmd.run(f'sed -i -e "s#ran-build:latest#ran-build-asan:{imageTag}#" docker/Dockerfile.{pattern}{self.dockerfileprefix}') + elif "fhi72" in name: + cmd.run(f'sed -i -e "s#ran-build-fhi72:latest#ran-build-fhi72:{imageTag}#" docker/Dockerfile.{pattern}{self.dockerfileprefix}') elif image != 'ran-build': cmd.run(f'sed -i -e "s#ran-build:latest#ran-build:{imageTag}#" docker/Dockerfile.{pattern}{self.dockerfileprefix}') if image == 'oai-gnb-aerial': @@ -445,7 +449,7 @@ class Containerize(): cmd.run(f"{self.cli} image prune --force") # Remove all intermediate build images and clean up - cmd.run(f"{self.cli} image rm ran-build:{imageTag} ran-build-asan:{imageTag}") + cmd.run(f"{self.cli} image rm ran-build:{imageTag} ran-build-asan:{imageTag} ran-build-fhi72:{imageTag} || true") cmd.run(f"{self.cli} volume prune --force") # Remove some cached artifacts to prevent out of diskspace problem