From 4b8359f11c71e827aa745a94a74e9323664bb1a0 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 7 Aug 2025 17:57:10 +0200 Subject: [PATCH 01/49] fix for UECAP F1 handling in dora/phytest mode --- openair2/RRC/NR/rrc_gNB_nsa.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index d0cc63062b..aa97a026e0 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -77,7 +77,7 @@ // and put it into a CG-ConfigInfo field static int cg_config_info_from_ue_cap_file(uint32_t maxlen, uint8_t buf[maxlen]) { - OCTET_STRING_t *os_cap = NULL; + OCTET_STRING_t *list_buf = NULL; if (uecap_file != NULL) { LOG_I(NR_RRC, "creating CG-ConfigInfo from UE capability file %s\n", uecap_file); @@ -98,8 +98,17 @@ static int cg_config_info_from_ue_cap_file(uint32_t maxlen, uint8_t buf[maxlen]) DevAssert(dec_rval.code == RC_OK); //xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, cap); - os_cap = calloc_or_fail(1, sizeof(*os_cap)); - os_cap->size = uper_encode_to_new_buffer(&asn_DEF_NR_UE_NR_Capability, NULL, cap, (void **)&os_cap->buf); + uint8_t *buf = NULL; + int sizebuf = uper_encode_to_new_buffer(&asn_DEF_NR_UE_NR_Capability, NULL, cap, (void **)&buf); + NR_UE_CapabilityRAT_Container_t *cont = calloc_or_fail(1, sizeof(*cont)); + cont->rat_Type = NR_RAT_Type_nr; + cont->ue_CapabilityRAT_Container.buf = buf; + cont->ue_CapabilityRAT_Container.size = sizebuf; + NR_UE_CapabilityRAT_ContainerList_t *clist = calloc_or_fail(1, sizeof(*clist)); + asn1cSeqAdd(&clist->list, cont); + list_buf = calloc_or_fail(1, sizeof(*list_buf)); + list_buf->size = uper_encode_to_new_buffer(&asn_DEF_NR_UE_CapabilityRAT_ContainerList, NULL, clist, (void **)&list_buf->buf); + ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, clist); } NR_CG_ConfigInfo_t *cgci = calloc_or_fail(1, sizeof(*cgci)); @@ -108,7 +117,7 @@ static int cg_config_info_from_ue_cap_file(uint32_t maxlen, uint8_t buf[maxlen]) cgci->criticalExtensions.choice.c1->present = NR_CG_ConfigInfo__criticalExtensions__c1_PR_cg_ConfigInfo; NR_CG_ConfigInfo_IEs_t *cgci_ies = calloc_or_fail(1, sizeof(*cgci_ies)); cgci->criticalExtensions.choice.c1->choice.cg_ConfigInfo = cgci_ies; - cgci_ies->ue_CapabilityInfo = os_cap; + cgci_ies->ue_CapabilityInfo = list_buf; //xer_fprint(stdout, &asn_DEF_NR_CG_ConfigInfo, cgci); asn_enc_rval_t rval = uper_encode_to_buffer(&asn_DEF_NR_CG_ConfigInfo, NULL, cgci, buf, maxlen); From 748684df05750301246bbd65d4b415e7e0601d81 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Tue, 29 Apr 2025 12:46:28 +0000 Subject: [PATCH 02/49] feat(time_meas): Standard deviation & merge `diff_square` was not merged in `merge_meas` so that calculation of the standard deviation was not working with merged timers. --- common/utils/time_meas.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/utils/time_meas.h b/common/utils/time_meas.h index f7c2810b60..de79a0252d 100644 --- a/common/utils/time_meas.h +++ b/common/utils/time_meas.h @@ -193,6 +193,7 @@ static inline void merge_meas(time_stats_t *dst_ts, const time_stats_t *src_ts) return; dst_ts->trials += src_ts->trials; dst_ts->diff += src_ts->diff; + dst_ts->diff_square += src_ts->diff_square; if (src_ts->max > dst_ts->max) dst_ts->max = src_ts->max; } From e79221b05b6d1bbd1d3850264b4ca6b360ba8291 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Tue, 29 Apr 2025 12:51:10 +0000 Subject: [PATCH 03/49] feat(nr_ulsim): detailed ULSCH decoding timers Add timers to provide ULSCH decoding time split between decoding, rate recovery and deinterleaving. --- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 4 ++++ openair1/PHY/defs_gNB.h | 3 +++ openair1/SIMULATION/NR_PHY/dlsim.c | 2 +- openair1/SIMULATION/NR_PHY/ulsim.c | 6 ++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index 667c7146b0..239b6f2f29 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -305,6 +305,10 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, LOG_D(PHY, "ULSCH %d in error\n", ULSCH_id); } offset += ((harq_process->K >> 3) - (harq_process->F >> 3) - ((harq_process->C > 1) ? 3 : 0)); + + merge_meas(&phy_vars_gNB->ts_deinterleave, &nrLDPC_segment_decoding_parameters.ts_deinterleave); + merge_meas(&phy_vars_gNB->ts_rate_unmatch, &nrLDPC_segment_decoding_parameters.ts_rate_unmatch); + merge_meas(&phy_vars_gNB->ts_ldpc_decode, &nrLDPC_segment_decoding_parameters.ts_ldpc_decode); } } diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index ad17c243ae..987f77d953 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -521,6 +521,9 @@ typedef struct PHY_VARS_gNB_s { time_stats_t slot_indication_stats; time_stats_t schedule_response_stats; time_stats_t ulsch_decoding_stats; + time_stats_t ts_deinterleave; + time_stats_t ts_rate_unmatch; + time_stats_t ts_ldpc_decode; time_stats_t ulsch_deinterleaving_stats; time_stats_t ulsch_channel_estimation_stats; time_stats_t pusch_channel_estimation_antenna_processing_stats; diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index e355ca51c2..0f21fdb145 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -1341,7 +1341,7 @@ printf("%d\n", slot); printStatIndent3(&gNB->tprep,"DLSCH LDPC input preparation time"); printStatIndent3(&gNB->tparity,"DLSCH LDPC parity generation time"); printStatIndent3(&gNB->toutput,"DLSCH LDPC output generation time"); - printStatIndent3(&gNB->dlsch_rate_matching_stats,"DLSCH Rate Mataching time"); + printStatIndent3(&gNB->dlsch_rate_matching_stats,"DLSCH Rate Matching time"); printStatIndent3(&gNB->dlsch_interleaving_stats, "DLSCH Interleaving time"); printStatIndent2(&gNB->dlsch_modulation_stats,"DLSCH modulation time"); printStatIndent2(&gNB->dlsch_scrambling_stats, "DLSCH scrambling time"); diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index b2b2dc38bb..82500d1e32 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1026,6 +1026,9 @@ int main(int argc, char *argv[]) reset_meas(&gNB->rx_pusch_init_stats); reset_meas(&gNB->rx_pusch_symbol_processing_stats); reset_meas(&gNB->ulsch_decoding_stats); + reset_meas(&gNB->ts_deinterleave); + reset_meas(&gNB->ts_rate_unmatch); + reset_meas(&gNB->ts_ldpc_decode); reset_meas(&gNB->ulsch_channel_estimation_stats); reset_meas(&gNB->pusch_channel_estimation_antenna_processing_stats); reset_meas(&gNB->rx_srs_stats); @@ -1587,6 +1590,9 @@ int main(int argc, char *argv[]) printStatIndent2(&gNB->rx_pusch_init_stats, "RX PUSCH Initialization time"); printStatIndent2(&gNB->rx_pusch_symbol_processing_stats, "RX PUSCH Symbol Processing time"); printStatIndent(&gNB->ulsch_decoding_stats,"ULSCH total decoding time"); + printStatIndent2(&gNB->ts_deinterleave, "ULSCH segment deinterleaving time"); + printStatIndent2(&gNB->ts_rate_unmatch, "ULSCH segment rate recovery time"); + printStatIndent2(&gNB->ts_ldpc_decode, "ULSCH segments decoding time"); printf("\nUE TX\n"); for (int i = PHY_PROC_TX; i <= OFDM_MOD_STATS; i++) { From ad8b76a8ef5cb189d295d13c296cf5a9c6f57ff5 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Thu, 7 Aug 2025 13:55:56 +0200 Subject: [PATCH 04/49] feat(nr-gnb): Log decoding timers in the gNB Log the LDPC reversed interleaving, reversed rate matching and decoding time stats in nrL1_stats.log for the gNB. --- executables/nr-gnb.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 345b58df7e..24870544b8 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -231,6 +231,9 @@ static size_t dump_L1_meas_stats(PHY_VARS_gNB *gNB, RU_t *ru, char *output, size output += print_meas_log(&gNB->dlsch_resource_mapping_stats, "DLSCH resource mapping", NULL, NULL, output,end-output); output += print_meas_log(&gNB->dlsch_precoding_stats, "DLSCH precoding", NULL, NULL, output,end-output); output += print_meas_log(&gNB->phy_proc_rx, "L1 Rx processing", NULL, NULL, output, end - output); + output += print_meas_log(&gNB->ts_deinterleave, "UL segment deinterleaving", NULL, NULL, output, end - output); + output += print_meas_log(&gNB->ts_rate_unmatch, "UL segment rate recovery", NULL, NULL, output, end - output); + output += print_meas_log(&gNB->ts_ldpc_decode, "UL segments decoding", NULL, NULL, output, end - output); output += print_meas_log(&gNB->ul_indication_stats, "UL Indication", NULL, NULL, output, end - output); output += print_meas_log(&gNB->slot_indication_stats, "Slot Indication", NULL, NULL, output, end - output); output += print_meas_log(&gNB->rx_pusch_stats, "PUSCH inner-receiver", NULL, NULL, output, end - output); @@ -288,6 +291,9 @@ void *nrL1_stats_thread(void *param) { reset_meas(&gNB->phy_proc_tx); reset_meas(&gNB->dlsch_encoding_stats); reset_meas(&gNB->phy_proc_rx); + reset_meas(&gNB->ts_deinterleave); + reset_meas(&gNB->ts_rate_unmatch); + reset_meas(&gNB->ts_ldpc_decode); reset_meas(&gNB->ul_indication_stats); reset_meas(&gNB->slot_indication_stats); reset_meas(&gNB->rx_pusch_stats); From e2c89f36eab4ae13e0f1e34c14d1e714449b0445 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Thu, 7 Aug 2025 13:19:54 +0000 Subject: [PATCH 05/49] feat(ldpc_aal): add decoding timer --- .../nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c index 62a20ef822..815ec9ce26 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c @@ -815,6 +815,10 @@ static int pmd_lcore_ldpc_dec(void *arg) AssertFatal(ret == 0, "Allocation failed for %d ops", num_segments); set_ldpc_dec_op(ops_enq, bufs->inputs, bufs->hard_outputs, bufs->harq_outputs, nrLDPC_slot_decoding_parameters); + // Start timer + // We report timing only once in (0,0) since the timers are merged at the end + start_meas(&nrLDPC_slot_decoding_parameters->TBs[0].segments[0].ts_ldpc_decode); + uint16_t enq = 0, deq = 0; while (enq < num_segments) { uint16_t num_to_enq = num_segments - enq; @@ -828,6 +832,10 @@ static int pmd_lcore_ldpc_dec(void *arg) DevAssert(time_out <= TIME_OUT_POLL); } + // Stop timer + // We report timing only once in (0,0) since the timers are merged at the end + stop_meas(&nrLDPC_slot_decoding_parameters->TBs[0].segments[0].ts_ldpc_decode); + if (deq == enq) { ret = retrieve_ldpc_dec_op(ops_deq, nrLDPC_slot_decoding_parameters); AssertFatal(ret == 0, "LDPC offload decoder failed!"); From c94db4adec69028b42a6f69e29f1e0a84b8bd082 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 17 Dec 2024 13:31:24 +0100 Subject: [PATCH 06/49] separate computation of size and value for DCI precoding information --- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c | 292 +++--------------- openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h | 61 +++- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 284 ++++++++++++++++- 3 files changed, 359 insertions(+), 278 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index cd51f6a48f..1a5431bdd2 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -1972,49 +1972,6 @@ static const int32_t table_6_4_1_1_3_4_pusch_dmrs_positions_l[12][8] = { {0, 3072, -1, -1, 3, 1539, -1, -1}, // 14 // (DMRS l' position) }; -// TS 38.212 -static const uint16_t table_7_3_1_1_2_2_1layer[28] = {0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; -static const uint16_t table_7_3_1_1_2_2_2layers[22] = {4, 5, 6, 7, 8, 9, 20, 21, 22, 23, 24, - 25, 26, 27, 48, 49, 50, 51, 52, 53, 54, 55}; -static const uint16_t table_7_3_1_1_2_2_3layers[7] = {10, 28, 29, 56, 57, 58, 59}; -static const uint16_t table_7_3_1_1_2_2_4layers[5] = {11, 30, 31, 60, 61}; -static const uint16_t table_7_3_1_1_2_2B_1layer[16] = {0, 1, 2, 3, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12, 24, 25}; -static const uint16_t table_7_3_1_1_2_2B_2layers[14] = {4, 5, 6, 7, 8, 9, 13, 26, 27, 28, 29, 30, 31, 32}; -static const uint16_t table_7_3_1_1_2_2B_3layers[3] = {10, 14, 33}; -static const uint16_t table_7_3_1_1_2_2B_4layers[3] = {11, 34, 35}; -static const uint16_t table_7_3_1_1_2_2A_1layer[16] = {0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19, 20, 10, 21, 22}; -static const uint16_t table_7_3_1_1_2_2A_2layers[14] = {4, 5, 6, 7, 8, 9, 11, 23, 24, 25, 26, 27, 28, 29}; -static const uint16_t table_7_3_1_1_2_3A[16] = {0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 4, 14, 15}; -static const uint16_t table_7_3_1_1_2_4_1layer_fullyAndPartialAndNonCoherent[6] = {0, 1, 3, 4, 5, 6}; -static const uint16_t table_7_3_1_1_2_4_2layers_fullyAndPartialAndNonCoherent[3] = {2, 7, 8}; -static const uint16_t table_7_3_1_1_2_4A_1layer[3] = {0, 1, 3}; -static const uint16_t table_7_3_1_1_2_28[3][15] = { - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -}; -static const uint16_t table_7_3_1_1_2_29[3][15] = { - {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0}, -}; -static const uint16_t table_7_3_1_1_2_30[3][15] = { - {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 6, 0, 0, 0, 0}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0}, -}; -static const uint16_t table_7_3_1_1_2_31[3][15] = { - {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 6, 0, 0, 0, 0}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, -}; -static const uint16_t table_7_3_1_1_2_32[3][15] = { - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -}; - // the following tables contain 10 times the value reported in 214 (in line with SCF specification and to avoid fractional values) //Table 5.1.3.1-1 of 38.214 static const uint16_t Table_51311[32][2] = {{2, 1200}, {2, 1570}, {2, 1930}, {2, 2510}, {2, 3080}, {2, 3790}, {2, 4490}, {2, 5260}, @@ -2503,61 +2460,61 @@ static int binomial(int n, int k) return c; } +int srs_codebook_nb_res(NR_SRS_Config_t *srs_config) +{ + int count = 0; + for (int i = 0; i < srs_config->srs_ResourceSetToAddModList->list.count; i++) { + if (srs_config->srs_ResourceSetToAddModList->list.array[i]->usage == NR_SRS_ResourceSet__usage_codebook) + count++; + } + return count; +} + +int srs_non_codebook_nb_res(NR_SRS_Config_t *srs_config) +{ + int count = 0; + for (int i = 0; i < srs_config->srs_ResourceSetToAddModList->list.count; i++) { + if (srs_config->srs_ResourceSetToAddModList->list.array[i]->usage == NR_SRS_ResourceSet__usage_nonCodebook) + count++; + } + return count; +} + +int srs_binomial_sum(int count, int Lmax) +{ + int lmin = 0; + int lsum = 0; + lmin = count < Lmax ? count : Lmax; + for (int k = 1; k <= lmin; k++) + lsum += binomial(count, k); + return lsum; +} + // #define DEBUG_SRS_RESOURCE_IND -uint8_t compute_srs_resource_indicator(long *maxMIMO_Layers, - NR_PUSCH_Config_t *pusch_Config, - NR_SRS_Config_t *srs_config, - nr_srs_feedback_t *srs_feedback, - uint32_t *val) + +static uint8_t compute_srs_resource_indicator_size(long *maxMIMO_Layers, NR_PUSCH_Config_t *pusch_Config, NR_SRS_Config_t *srs_config) { uint8_t nbits = 0; - - // SRI occupies a number of bits which is dependent upon the uplink transmission scheme, and it is used to determine - // the antenna ports and uplink transmission beam to use for PUSCH transmission. In the case of codebook based - // transmission, the SRI is used to select between SRS Resources belonging to different antenna panels - // (kind of directional antenna). There can be up to 2 SRS Resources (2 antenna panels). In the case of non-codebook - // based transmission, the SRI is used to select one or more SRS Resources from a set of N_SRS resources. The number - // of SRS Resources selected corresponds to the number of layers (rank) to be transmitted. - if (val) { - *val = 0; - } - if (srs_config && pusch_Config && pusch_Config->txConfig != NULL) { - if (*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook) { - #ifdef DEBUG_SRS_RESOURCE_IND LOG_I(NR_MAC, "*pusch_Config->txConfig = NR_PUSCH_Config__txConfig_codebook\n"); #endif - // TS 38.212 - Section 7.3.1.1.2: SRS resource indicator has ceil(log2(N_SRS)) bits according to // Tables 7.3.1.1.2-32, 7.3.1.1.2-32A and 7.3.1.1.2-32B if the higher layer parameter txConfig = codebook, // where N_SRS is the number of configured SRS resources in the SRS resource set configured by higher layer // parameter srs-ResourceSetToAddModList, and associated with the higher layer parameter usage of value codeBook. - int count = 0; - for (int i=0; isrs_ResourceSetToAddModList->list.count; i++) { - if (srs_config->srs_ResourceSetToAddModList->list.array[i]->usage == NR_SRS_ResourceSet__usage_codebook) { - count++; - } - } - if (count>0) { + int count = srs_codebook_nb_res(srs_config); + if (count > 0) nbits = ceil(log2(count)); - if (val && srs_feedback && count > 1) { - *val = table_7_3_1_1_2_32[count-2][srs_feedback->sri]; - } - } - #ifdef DEBUG_SRS_RESOURCE_IND LOG_I(NR_MAC, "srs_config->srs_ResourceSetToAddModList->list.count = %i\n", srs_config->srs_ResourceSetToAddModList->list.count); LOG_I(NR_MAC, "count = %i\n", count); #endif - } else { - #ifdef DEBUG_SRS_RESOURCE_IND LOG_I(NR_MAC, "*pusch_Config->txConfig = NR_PUSCH_Config__txConfig_nonCodebook\n"); #endif - // TS 38.212 - Section 7.3.1.1.2: SRS resource indicator has ceil(log2(sum(k = 1 until min(Lmax,N_SRS) of binomial(N_SRS,k)))) // bits according to Tables 7.3.1.1.2-28/29/30/31 if the higher layer parameter txConfig = nonCodebook, where // N_SRS is the number of configured SRS resources in the SRS resource set configured by higher layer parameter @@ -2572,68 +2529,29 @@ uint8_t compute_srs_resource_indicator(long *maxMIMO_Layers, if (maxMIMO_Layers != NULL) Lmax = *maxMIMO_Layers; else - AssertFatal(1 == 0, "MIMO on PUSCH not supported, maxMIMO_Layers needs to be set to 1\n"); - int lmin = 0; - int lsum = 0; - int count = 0; - for (int i = 0; i < srs_config->srs_ResourceSetToAddModList->list.count; i++) { - if (srs_config->srs_ResourceSetToAddModList->list.array[i]->usage == NR_SRS_ResourceSet__usage_nonCodebook) { - count++; - } - } - lmin = count < Lmax ? count : Lmax; - for (int k=1;k<=lmin;k++) { - lsum += binomial(count,k); - } - if (lsum>0) { + AssertFatal(false, "MIMO on PUSCH not supported, maxMIMO_Layers needs to be set to 1\n"); + int count = srs_non_codebook_nb_res(srs_config); + int lsum = srs_binomial_sum(count, Lmax); + if (lsum > 0) nbits = ceil(log2(lsum)); - if (val && srs_feedback && nbits > 0) { - switch(Lmax) { - case 1: - *val = table_7_3_1_1_2_28[count-2][srs_feedback->sri]; - break; - case 2: - *val = table_7_3_1_1_2_29[count-2][srs_feedback->sri]; - break; - case 3: - *val = table_7_3_1_1_2_30[count-2][srs_feedback->sri]; - break; - case 4: - *val = table_7_3_1_1_2_31[count-2][srs_feedback->sri]; - break; - default: - LOG_E(NR_MAC, "%s (%d) - Invalid Lmax %d\n", __FUNCTION__, __LINE__, Lmax); - } - } - } - #ifdef DEBUG_SRS_RESOURCE_IND LOG_I(NR_MAC, "srs_config->srs_ResourceSetToAddModList->list.count = %i\n", srs_config->srs_ResourceSetToAddModList->list.count); LOG_I(NR_MAC, "count = %i\n", count); LOG_I(NR_MAC, "Lmax = %i\n", Lmax); LOG_I(NR_MAC, "lsum = %i\n", lsum); #endif - } } - return nbits; } -uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, - NR_SRS_Config_t *srs_config, - dci_field_t srs_resource_indicator, - nr_srs_feedback_t *srs_feedback, - const uint8_t *nrOfLayers, - int *tpmi, - uint32_t *val) +static uint8_t compute_precoding_info_size(NR_PUSCH_Config_t *pusch_Config, + NR_SRS_Config_t *srs_config, + dci_field_t srs_resource_indicator) { // It is only applicable to codebook based transmission. This field occupies 0 bits for non-codebook based // transmission. It also occupies 0 bits for codebook based transmission using a single antenna port. uint8_t nbits = 0; - if (val) { - *val = 0; - } uint8_t pusch_antenna_ports = get_pusch_nb_antenna_ports(pusch_Config, srs_config, srs_resource_indicator); if (!pusch_Config @@ -2645,7 +2563,6 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, long max_rank = *pusch_Config->maxRank; long *ul_FullPowerTransmission = pusch_Config->ext1 ? pusch_Config->ext1->ul_FullPowerTransmission_r16 : NULL; long *codebookSubset = pusch_Config->codebookSubset; - int ul_tpmi = tpmi ? *tpmi : srs_feedback ? srs_feedback->tpmi : -1; if (pusch_antenna_ports == 2) { @@ -2658,23 +2575,11 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, // of higher layer parameter codebookSubset; if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { nbits = 2; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 2,"TPMI %d is invalid!\n", ul_tpmi); - *val = ul_tpmi; - } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 1; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 1,"TPMI %d is invalid!\n", ul_tpmi); - *val = ul_tpmi; - } } else { nbits = 3; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 5,"TPMI %d is invalid!\n", ul_tpmi); - *val = ul_tpmi; - } } } } else { @@ -2685,27 +2590,11 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, // fullpowerMode1, transform precoder is disabled, maxRank=2, and codebookSubset=nonCoherent; if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { nbits = 2; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 2) || (*nrOfLayers==2 && ul_tpmi == 0), - "TPMI %d is invalid!\n", ul_tpmi); - *val = *nrOfLayers==1 ? table_7_3_1_1_2_4A_1layer[ul_tpmi] : 2; - } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 2; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 1) || (*nrOfLayers==2 && ul_tpmi == 0), - "TPMI %d is invalid!\n", ul_tpmi); - *val = *nrOfLayers==1 ? ul_tpmi : 2; - } } else { nbits = 4; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 5) || (*nrOfLayers==2 && ul_tpmi <= 2), - "TPMI %d is invalid!\n", ul_tpmi); - *val = *nrOfLayers==1 ? table_7_3_1_1_2_4_1layer_fullyAndPartialAndNonCoherent[ul_tpmi] : - table_7_3_1_1_2_4_2layers_fullyAndPartialAndNonCoherent[ul_tpmi]; - } } } } @@ -2722,37 +2611,16 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 3; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 3 || ul_tpmi == 13, "TPMI %d is invalid!\n", ul_tpmi); - } } else { nbits = 4; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 15, "TPMI %d is invalid!\n", ul_tpmi); - } - } - if (val && srs_feedback) { - *val = table_7_3_1_1_2_3A[ul_tpmi]; } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 2; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 3, "TPMI %d is invalid!\n", ul_tpmi); - } } else if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent) { nbits = 4; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 11, "TPMI %d is invalid!\n", ul_tpmi); - } } else { nbits = 5; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 27, "TPMI %d is invalid!\n", ul_tpmi); - } - } - if (val && srs_feedback) { - *val = ul_tpmi; } } } else { @@ -2769,99 +2637,26 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, if (max_rank == 2) { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 4; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && (ul_tpmi <= 3 || ul_tpmi==13)) || (*nrOfLayers==2 && ul_tpmi <= 6), - "TPMI %d is invalid!\n", ul_tpmi); - } } else { nbits = 5; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 15) || (*nrOfLayers==2 && ul_tpmi <= 13), - "TPMI %d is invalid!\n", ul_tpmi); - } - } - if (val && srs_feedback) { - *val = *nrOfLayers==1 ? table_7_3_1_1_2_2A_1layer[ul_tpmi] : table_7_3_1_1_2_2A_2layers[ul_tpmi]; } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 4; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && (ul_tpmi <= 3 || ul_tpmi == 13)) || (*nrOfLayers==2 && ul_tpmi <= 6) || - (*nrOfLayers==3 && ul_tpmi <= 1) || (*nrOfLayers==4 && ul_tpmi == 0), - "TPMI %d is invalid!\n", ul_tpmi); - } } else { nbits = 6; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 15) || (*nrOfLayers==2 && ul_tpmi <= 13) || - (*nrOfLayers==3 && ul_tpmi <= 2) || (*nrOfLayers==4 && ul_tpmi <= 2), - "TPMI %d is invalid!\n", ul_tpmi); - } - } - if (val && srs_feedback) { - switch (*nrOfLayers) { - case 1: - *val = table_7_3_1_1_2_2B_1layer[ul_tpmi]; - break; - case 2: - *val = table_7_3_1_1_2_2B_2layers[ul_tpmi]; - break; - case 3: - *val = table_7_3_1_1_2_2B_3layers[ul_tpmi]; - break; - case 4: - *val = table_7_3_1_1_2_2B_4layers[ul_tpmi]; - break; - default: - LOG_E(NR_MAC,"Number of layers %d is invalid!\n", *nrOfLayers); - } } } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 4; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 3) || (*nrOfLayers==2 && ul_tpmi <= 5) || - (*nrOfLayers==3 && ul_tpmi == 0) || (*nrOfLayers==4 && ul_tpmi == 0), - "TPMI %d is invalid!\n", ul_tpmi); - } } else if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent) { nbits = 5; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 11) || (*nrOfLayers==2 && ul_tpmi <= 13) || - (*nrOfLayers==3 && ul_tpmi <= 2) || (*nrOfLayers==4 && ul_tpmi <= 2), - "TPMI %d is invalid!\n", ul_tpmi); - } } else { nbits = 6; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 28) || (*nrOfLayers==2 && ul_tpmi <= 22) || - (*nrOfLayers==3 && ul_tpmi <= 7) || (*nrOfLayers==4 && ul_tpmi <= 5), - "TPMI %d is invalid!\n", ul_tpmi); - } - } - if (val && srs_feedback) { - switch (*nrOfLayers) { - case 1: - *val = table_7_3_1_1_2_2_1layer[ul_tpmi]; - break; - case 2: - *val = table_7_3_1_1_2_2_2layers[ul_tpmi]; - break; - case 3: - *val = table_7_3_1_1_2_2_3layers[ul_tpmi]; - break; - case 4: - *val = table_7_3_1_1_2_2_4layers[ul_tpmi]; - break; - default: - LOG_E(NR_MAC,"Number of layers %d is invalid!\n", *nrOfLayers); - } } } } - } return nbits; } @@ -3100,12 +2895,11 @@ uint16_t nr_dci_size(const NR_UE_DL_BWP_t *DL_BWP, } // SRS resource indicator dci_pdu->srs_resource_indicator.nbits = - compute_srs_resource_indicator(sc_info->maxMIMO_Layers_PUSCH, pusch_Config, srs_config, NULL, NULL); + compute_srs_resource_indicator_size(sc_info->maxMIMO_Layers_PUSCH, pusch_Config, srs_config); size += dci_pdu->srs_resource_indicator.nbits; LOG_D(NR_MAC, "dci_pdu->srs_resource_indicator.nbits %d\n", dci_pdu->srs_resource_indicator.nbits); // Precoding info and number of layers - dci_pdu->precoding_information.nbits = - compute_precoding_information(pusch_Config, srs_config, dci_pdu->srs_resource_indicator, NULL, NULL, NULL, NULL); + dci_pdu->precoding_information.nbits = compute_precoding_info_size(pusch_Config, srs_config, dci_pdu->srs_resource_indicator); size += dci_pdu->precoding_information.nbits; LOG_D(NR_MAC, "dci_pdu->precoding_informaiton.nbits=%d\n", dci_pdu->precoding_information.nbits); // Antenna ports diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index 3770d2c6ec..915fadb03b 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -41,6 +41,49 @@ #define NB_SRS_PERIOD (18) static const uint16_t srs_period[NB_SRS_PERIOD] = { 0, 1, 2, 4, 5, 8, 10, 16, 20, 32, 40, 64, 80, 160, 320, 640, 1280, 2560}; +// TS 38.212 +static const uint16_t table_7_3_1_1_2_2_1layer[28] = {0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; +static const uint16_t table_7_3_1_1_2_2_2layers[22] = {4, 5, 6, 7, 8, 9, 20, 21, 22, 23, 24, + 25, 26, 27, 48, 49, 50, 51, 52, 53, 54, 55}; +static const uint16_t table_7_3_1_1_2_2_3layers[7] = {10, 28, 29, 56, 57, 58, 59}; +static const uint16_t table_7_3_1_1_2_2_4layers[5] = {11, 30, 31, 60, 61}; +static const uint16_t table_7_3_1_1_2_2B_1layer[16] = {0, 1, 2, 3, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12, 24, 25}; +static const uint16_t table_7_3_1_1_2_2B_2layers[14] = {4, 5, 6, 7, 8, 9, 13, 26, 27, 28, 29, 30, 31, 32}; +static const uint16_t table_7_3_1_1_2_2B_3layers[3] = {10, 14, 33}; +static const uint16_t table_7_3_1_1_2_2B_4layers[3] = {11, 34, 35}; +static const uint16_t table_7_3_1_1_2_2A_1layer[16] = {0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19, 20, 10, 21, 22}; +static const uint16_t table_7_3_1_1_2_2A_2layers[14] = {4, 5, 6, 7, 8, 9, 11, 23, 24, 25, 26, 27, 28, 29}; +static const uint16_t table_7_3_1_1_2_3A[16] = {0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 4, 14, 15}; +static const uint16_t table_7_3_1_1_2_4_1layer_fullyAndPartialAndNonCoherent[6] = {0, 1, 3, 4, 5, 6}; +static const uint16_t table_7_3_1_1_2_4_2layers_fullyAndPartialAndNonCoherent[3] = {2, 7, 8}; +static const uint16_t table_7_3_1_1_2_4A_1layer[3] = {0, 1, 3}; +static const uint16_t table_7_3_1_1_2_28[3][15] = { + {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; +static const uint16_t table_7_3_1_1_2_29[3][15] = { + {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0}, +}; +static const uint16_t table_7_3_1_1_2_30[3][15] = { + {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 6, 0, 0, 0, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0}, +}; +static const uint16_t table_7_3_1_1_2_31[3][15] = { + {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 6, 0, 0, 0, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, +}; +static const uint16_t table_7_3_1_1_2_32[3][15] = { + {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + typedef enum { pusch_dmrs_pos0 = 0, pusch_dmrs_pos1 = 1, @@ -81,20 +124,6 @@ void config_frame_structure(int mu, uint8_t frame_type, frame_structure_t *fs); -uint8_t compute_srs_resource_indicator(long *maxMIMO_Layers, - NR_PUSCH_Config_t *pusch_Config, - NR_SRS_Config_t *srs_config, - nr_srs_feedback_t *srs_feedback, - uint32_t *val); - -uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, - NR_SRS_Config_t *srs_config, - dci_field_t srs_resource_indicator, - nr_srs_feedback_t *srs_feedback, - const uint8_t *nrOfLayers, - int *tpmi, - uint32_t *val); - NR_PDSCH_TimeDomainResourceAllocationList_t *get_dl_tdalist(const NR_UE_DL_BWP_t *DL_BWP, int controlResourceSetId, int ss_type, @@ -197,7 +226,9 @@ uint32_t nr_get_code_rate_dl(uint8_t Imcs, uint8_t table_idx); /** \brief Computes Q based on I_MCS PDSCH and table_idx for uplink. Implements MCS Tables from 38.214. */ uint8_t nr_get_Qm_ul(uint8_t Imcs, uint8_t table_idx); uint32_t nr_get_code_rate_ul(uint8_t Imcs, uint8_t table_idx); - +int srs_binomial_sum(int count, int Lmax); +int srs_codebook_nb_res(NR_SRS_Config_t *srs_config); +int srs_non_codebook_nb_res(NR_SRS_Config_t *srs_config); uint16_t get_nr_srs_offset(NR_SRS_PeriodicityAndOffset_t periodicityAndOffset); void get_monitoring_period_offset(const NR_SearchSpace_t *ss, int *period, int *offset); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index f8c842706c..2366708f3c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -887,6 +887,265 @@ dci_pdu_rel15_t prepare_dci_dl_payload(const gNB_MAC_INST *gNB_mac, return dci_payload; } +static uint32_t compute_srs_resource_indicator(long *maxMIMO_Layers, + NR_PUSCH_Config_t *pusch_Config, + NR_SRS_Config_t *srs_config, + nr_srs_feedback_t *srs_feedback) +{ + uint32_t val = 0; + if (srs_config && pusch_Config && pusch_Config->txConfig != NULL) { + if (*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook) { + + // TS 38.212 - Section 7.3.1.1.2: SRS resource indicator has ceil(log2(N_SRS)) bits according to + // Tables 7.3.1.1.2-32, 7.3.1.1.2-32A and 7.3.1.1.2-32B if the higher layer parameter txConfig = codebook, + // where N_SRS is the number of configured SRS resources in the SRS resource set configured by higher layer + // parameter srs-ResourceSetToAddModList, and associated with the higher layer parameter usage of value codeBook. + int count = srs_codebook_nb_res(srs_config); + if (count > 1) + val = table_7_3_1_1_2_32[count - 2][srs_feedback->sri]; + } else { + // TS 38.212 - Section 7.3.1.1.2: SRS resource indicator has ceil(log2(sum(k = 1 until min(Lmax,N_SRS) of binomial(N_SRS,k)))) + // bits according to Tables 7.3.1.1.2-28/29/30/31 if the higher layer parameter txConfig = nonCodebook, where + // N_SRS is the number of configured SRS resources in the SRS resource set configured by higher layer parameter + // srs-ResourceSetToAddModList, and associated with the higher layer parameter usage of value nonCodeBook and: + // + // - if UE supports operation with maxMIMO-Layers and the higher layer parameter maxMIMO-Layers of + // PUSCH-ServingCellConfig of the serving cell is configured, Lmax is given by that parameter; + // + // - otherwise, Lmax is given by the maximum number of layers for PUSCH supported by the UE for the serving cell + // for non-codebook based operation. + int Lmax = 0; + if (maxMIMO_Layers != NULL) + Lmax = *maxMIMO_Layers; + else + AssertFatal(false, "MIMO on PUSCH not supported, maxMIMO_Layers needs to be set to 1\n"); + int count = srs_non_codebook_nb_res(srs_config); + int lsum = srs_binomial_sum(count, Lmax); + if (lsum > 0 && ceil(log2(lsum)) > 0) { + switch(Lmax) { + case 1: + val = table_7_3_1_1_2_28[count-2][srs_feedback->sri]; + break; + case 2: + val = table_7_3_1_1_2_29[count-2][srs_feedback->sri]; + break; + case 3: + val = table_7_3_1_1_2_30[count-2][srs_feedback->sri]; + break; + case 4: + val = table_7_3_1_1_2_31[count-2][srs_feedback->sri]; + break; + default: + LOG_E(NR_MAC, "%s (%d) - Invalid Lmax %d\n", __FUNCTION__, __LINE__, Lmax); + } + } + } + } + return val; +} + +static uint32_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, + NR_SRS_Config_t *srs_config, + dci_field_t srs_resource_indicator, + nr_srs_feedback_t *srs_feedback, + const uint8_t *nrOfLayers, + int *tpmi) +{ + uint32_t val = 0; + + uint8_t pusch_antenna_ports = get_pusch_nb_antenna_ports(pusch_Config, srs_config, srs_resource_indicator); + if (!pusch_Config + || (pusch_Config->txConfig != NULL && *pusch_Config->txConfig == NR_PUSCH_Config__txConfig_nonCodebook) + || pusch_antenna_ports == 1) { + return val; + } + + long max_rank = *pusch_Config->maxRank; + long *ul_FullPowerTransmission = pusch_Config->ext1 ? pusch_Config->ext1->ul_FullPowerTransmission_r16 : NULL; + long *codebookSubset = pusch_Config->codebookSubset; + int ul_tpmi = tpmi ? *tpmi : srs_feedback ? srs_feedback->tpmi : -1; + + if (pusch_antenna_ports == 2) { + + if (max_rank == 1) { + // - 1 or 3 bits according to Table 7.3.1.1.2-5 for 2 antenna ports, if txConfig = codebook, ul-FullPowerTransmission + // is not configured or configured to fullpowerMode2 or configured to fullpower, and according to whether transform + // precoder is enabled or disabled, and the values of higher layer parameters maxRank and codebookSubset; + // - 2 bits according to Table 7.3.1.1.2-5A for 2 antenna ports, if txConfig = codebook, ul-FullPowerTransmission = + // fullpowerMode1, maxRank=1, and according to whether transform precoder is enabled or disabled, and the values + // of higher layer parameter codebookSubset; + if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { + AssertFatal(ul_tpmi <= 2,"TPMI %d is invalid!\n", ul_tpmi); + val = ul_tpmi; + } else { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal(ul_tpmi <= 1,"TPMI %d is invalid!\n", ul_tpmi); + val = ul_tpmi; + } else { + AssertFatal(ul_tpmi <= 5,"TPMI %d is invalid!\n", ul_tpmi); + val = ul_tpmi; + } + } + } else { + // - 2 or 4 bits according to Table 7.3.1.1.2-4 for 2 antenna ports, if txConfig = codebook, ul-FullPowerTransmission + // is not configured or configured to fullpowerMode2 or configured to fullpower, and according to whether transform + // precoder is enabled or disabled, and the values of higher layer parameters maxRank and codebookSubset; + // - 2 bits according to Table 7.3.1.1.2-4A for 2 antenna ports, if txConfig = codebook, ul-FullPowerTransmission = + // fullpowerMode1, transform precoder is disabled, maxRank=2, and codebookSubset=nonCoherent; + if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 2) + || (*nrOfLayers==2 && ul_tpmi == 0), + "TPMI %d is invalid!\n", + ul_tpmi); + val = *nrOfLayers == 1 ? table_7_3_1_1_2_4A_1layer[ul_tpmi] : 2; + } else { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 1) + || (*nrOfLayers==2 && ul_tpmi == 0), + "TPMI %d is invalid!\n", + ul_tpmi); + val = *nrOfLayers == 1 ? ul_tpmi : 2; + } else { + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 5) + || (*nrOfLayers==2 && ul_tpmi <= 2), + "TPMI %d is invalid!\n", + ul_tpmi); + val = *nrOfLayers == 1 ? table_7_3_1_1_2_4_1layer_fullyAndPartialAndNonCoherent[ul_tpmi] : + table_7_3_1_1_2_4_2layers_fullyAndPartialAndNonCoherent[ul_tpmi]; + } + } + } + + } else if (pusch_antenna_ports == 4) { + + if (max_rank == 1) { + // - 2, 4, or 5 bits according to Table 7.3.1.1.2-3 for 4 antenna ports, if txConfig = codebook, ul-FullPowerTransmission + // is not configured or configured to fullpowerMode2 or configured to fullpower, and according to whether transform + // precoder is enabled or disabled, and the values of higher layer parameters maxRank, and codebookSubset; + // - 3 or 4 bits according to Table 7.3.1.1.2-3A for 4 antenna ports, if txConfig = codebook, ul-FullPowerTransmission = + // fullpowerMode1, maxRank=1, and according to whether transform precoder is enabled or disabled, and the values + // of higher layer parameter codebookSubset; + if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal(ul_tpmi <= 3 || ul_tpmi == 13, "TPMI %d is invalid!\n", ul_tpmi); + } else { + AssertFatal(ul_tpmi <= 15, "TPMI %d is invalid!\n", ul_tpmi); + } + val = table_7_3_1_1_2_3A[ul_tpmi]; + } else { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal(ul_tpmi <= 3, "TPMI %d is invalid!\n", ul_tpmi); + } else if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent) { + AssertFatal(ul_tpmi <= 11, "TPMI %d is invalid!\n", ul_tpmi); + } else { + AssertFatal(ul_tpmi <= 27, "TPMI %d is invalid!\n", ul_tpmi); + } + val = ul_tpmi; + } + } else { + // - 4, 5, or 6 bits according to Table 7.3.1.1.2-2 for 4 antenna ports, if txConfig = codebook, ul-FullPowerTransmission + // is not configured or configured to fullpowerMode2 or configured to fullpower, and according to whether transform + // precoder is enabled or disabled, and the values of higher layer parameters maxRank, and codebookSubset; + // - 4 or 5 bits according to Table 7.3.1.1.2-2A for 4 antenna ports, if txConfig = codebook, ul-FullPowerTransmission = + // fullpowerMode1, maxRank=2, transform precoder is disabled, and according to the values of higher layer parameter + // codebookSubset; + // - 4 or 6 bits according to Table 7.3.1.1.2-2B for 4 antenna ports, if txConfig = codebook, ul-FullPowerTransmission = + // fullpowerMode1, maxRank=3 or 4, transform precoder is disabled, and according to the values of higher layer + // parameter codebookSubset; + if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { + if (max_rank == 2) { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal((*nrOfLayers==1 + && (ul_tpmi <= 3 || ul_tpmi == 13)) + || (*nrOfLayers == 2 && ul_tpmi <= 6), + "TPMI %d is invalid!\n", + ul_tpmi); + } else { + AssertFatal((*nrOfLayers == 1 && ul_tpmi <= 15) + || (*nrOfLayers == 2 && ul_tpmi <= 13), + "TPMI %d is invalid!\n", + ul_tpmi); + } + val = *nrOfLayers == 1 ? table_7_3_1_1_2_2A_1layer[ul_tpmi] : table_7_3_1_1_2_2A_2layers[ul_tpmi]; + } else { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal((*nrOfLayers == 1 + && (ul_tpmi <= 3 || ul_tpmi == 13)) + || (*nrOfLayers == 2 && ul_tpmi <= 6) + || (*nrOfLayers == 3 && ul_tpmi <= 1) + || (*nrOfLayers == 4 && ul_tpmi == 0), + "TPMI %d is invalid!\n", + ul_tpmi); + } else { + AssertFatal((*nrOfLayers == 1 && ul_tpmi <= 15) + || (*nrOfLayers == 2 && ul_tpmi <= 13) + || (*nrOfLayers == 3 && ul_tpmi <= 2) + || (*nrOfLayers == 4 && ul_tpmi <= 2), + "TPMI %d is invalid!\n", + ul_tpmi); + } + switch (*nrOfLayers) { + case 1: + val = table_7_3_1_1_2_2B_1layer[ul_tpmi]; + break; + case 2: + val = table_7_3_1_1_2_2B_2layers[ul_tpmi]; + break; + case 3: + val = table_7_3_1_1_2_2B_3layers[ul_tpmi]; + break; + case 4: + val = table_7_3_1_1_2_2B_4layers[ul_tpmi]; + break; + default: + LOG_E(NR_MAC,"Number of layers %d is invalid!\n", *nrOfLayers); + } + } + } else { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal((*nrOfLayers == 1 && ul_tpmi <= 3) + || (*nrOfLayers == 2 && ul_tpmi <= 5) + || (*nrOfLayers == 3 && ul_tpmi == 0) + || (*nrOfLayers == 4 && ul_tpmi == 0), + "TPMI %d is invalid!\n", + ul_tpmi); + } else if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent) { + AssertFatal((*nrOfLayers == 1 && ul_tpmi <= 11) + || (*nrOfLayers == 2 && ul_tpmi <= 13) + || (*nrOfLayers == 3 && ul_tpmi <= 2) + || (*nrOfLayers == 4 && ul_tpmi <= 2), + "TPMI %d is invalid!\n", + ul_tpmi); + } else { + AssertFatal((*nrOfLayers == 1 && ul_tpmi <= 28) + || (*nrOfLayers == 2 && ul_tpmi <= 22) + || (*nrOfLayers == 3 && ul_tpmi <= 7) + || (*nrOfLayers == 4 && ul_tpmi <= 5), + "TPMI %d is invalid!\n", + ul_tpmi); + } + switch (*nrOfLayers) { + case 1: + val = table_7_3_1_1_2_2_1layer[ul_tpmi]; + break; + case 2: + val = table_7_3_1_1_2_2_2layers[ul_tpmi]; + break; + case 3: + val = table_7_3_1_1_2_2_3layers[ul_tpmi]; + break; + case 4: + val = table_7_3_1_1_2_2_4layers[ul_tpmi]; + break; + default: + LOG_E(NR_MAC,"Number of layers %d is invalid!\n", *nrOfLayers); + } + } + } + } + return val; +} + void config_uldci(const NR_UE_ServingCell_Info_t *sc_info, const nfapi_nr_pusch_pdu_t *pusch_pdu, dci_pdu_rel15_t *dci_pdu_rel15, @@ -932,23 +1191,20 @@ void config_uldci(const NR_UE_ServingCell_Info_t *sc_info, // bwp indicator as per table 7.3.1.1.2-1 in 38.212 dci_pdu_rel15->bwp_indicator.val = sc_info->n_ul_bwp < 4 ? bwp_id : bwp_id - 1; // SRS resource indicator - if (pusch_Config && - pusch_Config->txConfig != NULL) { + if (pusch_Config && pusch_Config->txConfig != NULL) { AssertFatal(*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook, "Non Codebook configuration non supported\n"); - compute_srs_resource_indicator(sc_info->maxMIMO_Layers_PUSCH, - pusch_Config, - ul_bwp->srs_Config, - srs_feedback, - &dci_pdu_rel15->srs_resource_indicator.val); + dci_pdu_rel15->srs_resource_indicator.val = compute_srs_resource_indicator(sc_info->maxMIMO_Layers_PUSCH, + pusch_Config, + ul_bwp->srs_Config, + srs_feedback); } - compute_precoding_information(pusch_Config, - ul_bwp->srs_Config, - dci_pdu_rel15->srs_resource_indicator, - srs_feedback, - &pusch_pdu->nrOfLayers, - tpmi, - &dci_pdu_rel15->precoding_information.val); + dci_pdu_rel15->precoding_information.val = compute_precoding_information(pusch_Config, + ul_bwp->srs_Config, + dci_pdu_rel15->srs_resource_indicator, + srs_feedback, + &pusch_pdu->nrOfLayers, + tpmi); // antenna_ports.val = 0 for transform precoder is disabled, dmrs-Type=1, maxLength=1, Rank=1/2/3/4 // Antenna Ports From 5212bd7a851e4a211a161b43a93cf6467701f71d Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Wed, 30 Jul 2025 09:59:55 +0000 Subject: [PATCH 07/49] feat(rf_emulator): Add a dummy RF library Add a simple RF emulator library with no real RF. It only synchronizes the gNB with the real time clock. It is to be used for stress test or benchmarking in phy-test mode. --- cmake_targets/build_oai | 2 +- radio/CMakeLists.txt | 5 + radio/emulator/CMakeLists.txt | 3 + radio/emulator/rf_emulator.c | 267 ++++++++++++++++++++++++++++++++++ 4 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 radio/emulator/CMakeLists.txt create mode 100644 radio/emulator/rf_emulator.c diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index 292e8d0575..c2268a64b5 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -487,7 +487,7 @@ function main() { # add some default libraries that should always be built # for eNB, gNB, UEs, simulators if [[ $gNB == 1 || $eNB == 1 || $UE == 1 || $nrUE == 1 || $SIMUS_PHY == 1 || $RU == 1 ]]; then - TARGET_LIST="$TARGET_LIST params_libconfig coding rfsimulator dfts params_yaml vrtsim" + TARGET_LIST="$TARGET_LIST params_libconfig coding rfsimulator dfts params_yaml vrtsim rf_emulator" fi mkdir -p $DIR/$BUILD_DIR/build diff --git a/radio/CMakeLists.txt b/radio/CMakeLists.txt index 654fb037f0..a47a358903 100644 --- a/radio/CMakeLists.txt +++ b/radio/CMakeLists.txt @@ -42,3 +42,8 @@ add_boolean_option(VRTSIM_RADIO ON "Activate OAI's shared memory radio driver" O if(VRTSIM_RADIO) add_subdirectory(vrtsim) endif() + +add_boolean_option(OAI_RF_EMULATOR ON "Activate OAI's RF emulator" OFF) +if(OAI_RF_EMULATOR) + add_subdirectory(emulator) +endif() diff --git a/radio/emulator/CMakeLists.txt b/radio/emulator/CMakeLists.txt new file mode 100644 index 0000000000..af25cf7019 --- /dev/null +++ b/radio/emulator/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(rf_emulator MODULE rf_emulator.c) +set_target_properties(rf_emulator PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +add_dependencies(rf_emulator generate_T) diff --git a/radio/emulator/rf_emulator.c b/radio/emulator/rf_emulator.c new file mode 100644 index 0000000000..307d9ea80d --- /dev/null +++ b/radio/emulator/rf_emulator.c @@ -0,0 +1,267 @@ +/* + * 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 + */ + +/*! \file radio/emulator/rf_emulator.c + * \brief RF library that does nothing to be used in benchmarks with phy-test without RF + */ + +#include +#include + +#include +#include +#include "common/utils/threadPool/pthread_utils.h" +#include "common_lib.h" + +// structures and timing thread job for timing +typedef struct { + uint64_t timestamp; + pthread_mutex_t mutex; + pthread_cond_t cond; +} emulator_timestamp_t; + +typedef struct { + uint64_t last_received_sample; + double timescale; + double sample_rate; + emulator_timestamp_t emulator_timestamp; + pthread_t timing_thread; + bool run_timing_thread; +} emulator_state_t; + +static void *emulator_timing_job(void *arg) +{ + emulator_state_t *emulator_state = (emulator_state_t *)arg; + struct timespec timestamp; + if (clock_gettime(CLOCK_REALTIME, ×tamp)) { + LOG_E(UTIL, "clock_gettime failed\n"); + exit(1); + } + double leftover_samples = 0; + while (emulator_state->run_timing_thread) { + struct timespec current_time; + if (clock_gettime(CLOCK_REALTIME, ¤t_time)) { + LOG_E(UTIL, "clock_gettime failed\n"); + exit(1); + } + uint64_t diff = (current_time.tv_sec - timestamp.tv_sec) * 1000000000 + (current_time.tv_nsec - timestamp.tv_nsec); + timestamp = current_time; + double samples_to_produce = emulator_state->sample_rate * emulator_state->timescale * diff / 1e9; + + // Attempt to correct compounding rounding error + leftover_samples += samples_to_produce - (uint64_t)samples_to_produce; + if (leftover_samples > 1.0f) { + samples_to_produce += 1; + leftover_samples -= 1; + } + + emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; + mutexlock(emulator_timestamp->mutex); + emulator_timestamp->timestamp += samples_to_produce; + condbroadcast(emulator_timestamp->cond); + mutexunlock(emulator_timestamp->mutex); + + usleep(1); + } + return 0; +} + +/*! \brief Called to start the RF transceiver. Return 0 if OK, < 0 if error + * \param device pointer to the device structure specific to the RF hardware target + */ +static int emulator_start(openair0_device *device) +{ + // Start the timing thread + emulator_state_t *emulator_state = (emulator_state_t *)device->priv; + if (!emulator_state->run_timing_thread) { + emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; + memset(emulator_timestamp, 0, sizeof(emulator_timestamp_t)); + + pthread_mutexattr_t mutex_attr; + pthread_condattr_t cond_attr; + int ret = pthread_mutexattr_init(&mutex_attr); + AssertFatal(ret == 0, "pthread_mutexattr_init() failed: errno %d, %s\n", errno, strerror(errno)); + + ret = pthread_condattr_init(&cond_attr); + AssertFatal(ret == 0, "pthread_condattr_init() failed: errno %d, %s\n", errno, strerror(errno)); + + ret = pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); + AssertFatal(ret == 0, "pthread_mutexattr_setpshared() failed: errno %d, %s\n", errno, strerror(errno)); + + ret = pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); + AssertFatal(ret == 0, "pthread_condattr_setpshared() failed: errno %d, %s\n", errno, strerror(errno)); + + ret = pthread_mutex_init(&emulator_timestamp->mutex, &mutex_attr); + AssertFatal(ret == 0, "pthread_mutex_init() failed: errno %d, %s\n", errno, strerror(errno)); + + ret = pthread_cond_init(&emulator_timestamp->cond, &cond_attr); + AssertFatal(ret == 0, "pthread_cond_init() failed: errno %d, %s\n", errno, strerror(errno)); + + emulator_state->run_timing_thread = true; + ret = pthread_create(&emulator_state->timing_thread, NULL, emulator_timing_job, emulator_state); + AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno)); + } + + return 0; +} + +/*! \brief print the RF statistics + * \param device pointer to the device structure specific to the RF hardware target + * \returns 0 on success + */ +int emulator_get_stats(openair0_device *device) +{ + return (0); +} + +/*! \brief Reset the RF statistics + * \param device pointer to the device structure specific to the RF hardware target + * \returns 0 on success + */ +int emulator_reset_stats(openair0_device *device) +{ + return (0); +} + +/*! \brief Terminate operation of the RF transceiver -- free all associated resources (if any) + * \param pointer to the device structure specific to the RF hardware target + */ +static void emulator_end(openair0_device *device) +{ + // Stop the timing thread + emulator_state_t *emulator_state = (emulator_state_t *)device->priv; + if (emulator_state->run_timing_thread) { + emulator_state->run_timing_thread = false; + int ret = pthread_join(emulator_state->timing_thread, NULL); + AssertFatal(ret == 0, "pthread_join() failed: errno: %d, %s\n", errno, strerror(errno)); + } +} + +/*! \brief Stop RF + * \param device pointer to the device structure specific to the RF hardware target + */ +int emulator_stop(openair0_device *device) +{ + return (0); +} + +/*! \brief Set Gains (TX/RX) + * \param device pointer to the device structure specific to the RF hardware target + * \param openair0_cfg RF frontend parameters set by application + * \returns 0 in success + */ +int emulator_set_gains(openair0_device *device, openair0_config_t *openair0_cfg) +{ + return (0); +} + +/*! \brief Set frequencies (TX/RX). + * \param device pointer to the device structure specific to the RF hardware target + * \param openair0_cfg RF frontend parameters set by application + * \returns 0 in success + */ +int emulator_set_freq(openair0_device *device, openair0_config_t *openair0_cfg) +{ + return (0); +} + +int emulator_write_init(openair0_device *device) +{ + return (0); +} + +/*! \brief Called to send samples to the RF target + * \param device pointer to the device structure specific to the RF hardware target + * \param timestamp The timestamp at which the first sample MUST be sent + * \param buff Buffer which holds the samples + * \param nsamps number of samples to be sent + * \param nbAnt number of antennas + * \param flags flags must be set to true if timestamp parameter needs to be applied + */ +static int emulator_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int nbAnt, int flags) +{ + return 0; +} + +/*! \brief Receive samples from hardware. + * Read \ref nsamps samples from each channel to buffers. buff[0] is the array for + * the first channel. + * \param[out] ptimestamp time at which the first sample was received. + * \param device pointer to the device structure specific to the RF hardware target + * \param[out] ptimestamp the time at which the first sample was received. + * \param[out] buff An array of pointers to buffers for received samples. The buffers must be large enough to hold the number of + * samples \ref nsamps. \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte. + * \param nbAnt number of antennas + * \returns the number of samples read + */ +static int emulator_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int nbAnt) +{ + emulator_state_t *emulator_state = (emulator_state_t *)device->priv; + + emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; + uint64_t timestamp = emulator_state->last_received_sample + nsamps; + uint64_t current_timestamp = emulator_timestamp->timestamp; + if (current_timestamp < timestamp) { + mutexlock(emulator_timestamp->mutex); + while (current_timestamp < timestamp) { + condwait(emulator_timestamp->cond, emulator_timestamp->mutex); + current_timestamp = emulator_timestamp->timestamp; + } + mutexunlock(emulator_timestamp->mutex); + } else { + LOG_W(HW, "RF read is late!\n"); + } + *ptimestamp = emulator_state->last_received_sample; + emulator_state->last_received_sample += nsamps; + + for (int i = 0; i < nbAnt; i++) { + memset(buff[i], 0, nsamps * sizeof(uint32_t)); // TODO Play random data + } + + return nsamps; +} + +int device_init(openair0_device *device, openair0_config_t *openair0_cfg) +{ + LOG_I(HW, "This is emulator RF that does nothing\n"); + + emulator_state_t *emulator_state = calloc_or_fail(1, sizeof(emulator_state_t)); + emulator_state->last_received_sample = 0; + emulator_state->timescale = 1.0; + emulator_state->sample_rate = openair0_cfg->sample_rate; + emulator_state->run_timing_thread = false; + + device->priv = emulator_state; + device->openair0_cfg = openair0_cfg; + device->trx_start_func = emulator_start; + device->trx_get_stats_func = emulator_get_stats; + device->trx_reset_stats_func = emulator_reset_stats; + device->trx_end_func = emulator_end; + device->trx_stop_func = emulator_stop; + device->trx_set_freq_func = emulator_set_freq; + device->trx_set_gains_func = emulator_set_gains; + device->trx_write_init = emulator_write_init; + device->type = RFSIMULATOR; + device->trx_write_func = emulator_write; + device->trx_read_func = emulator_read; + + return 0; +} From bf83b787586445652c83de907a09dde694a36758 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Thu, 31 Jul 2025 13:46:48 +0000 Subject: [PATCH 08/49] feat(rf_emulator): Count late reads Count late reads and print every second instead of printing each. --- radio/emulator/rf_emulator.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/radio/emulator/rf_emulator.c b/radio/emulator/rf_emulator.c index 307d9ea80d..0ba9ea0636 100644 --- a/radio/emulator/rf_emulator.c +++ b/radio/emulator/rf_emulator.c @@ -36,6 +36,7 @@ typedef struct { uint64_t timestamp; pthread_mutex_t mutex; pthread_cond_t cond; + uint64_t late_reads; } emulator_timestamp_t; typedef struct { @@ -62,6 +63,15 @@ static void *emulator_timing_job(void *arg) LOG_E(UTIL, "clock_gettime failed\n"); exit(1); } + + emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; + if (current_time.tv_sec != timestamp.tv_sec) { + if (emulator_timestamp->late_reads != 0) { + LOG_W(HW, "%ld RF reads were late\n", emulator_timestamp->late_reads); + } + emulator_timestamp->late_reads = 0; + } + uint64_t diff = (current_time.tv_sec - timestamp.tv_sec) * 1000000000 + (current_time.tv_nsec - timestamp.tv_nsec); timestamp = current_time; double samples_to_produce = emulator_state->sample_rate * emulator_state->timescale * diff / 1e9; @@ -73,7 +83,6 @@ static void *emulator_timing_job(void *arg) leftover_samples -= 1; } - emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; mutexlock(emulator_timestamp->mutex); emulator_timestamp->timestamp += samples_to_produce; condbroadcast(emulator_timestamp->cond); @@ -227,7 +236,9 @@ static int emulator_read(openair0_device *device, openair0_timestamp *ptimestamp } mutexunlock(emulator_timestamp->mutex); } else { - LOG_W(HW, "RF read is late!\n"); + mutexlock(emulator_timestamp->mutex); + emulator_timestamp->late_reads++; + mutexunlock(emulator_timestamp->mutex); } *ptimestamp = emulator_state->last_received_sample; emulator_state->last_received_sample += nsamps; From 33cbbafddb1ba8334075d97ae08e198cabcac47d Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 24 Jul 2025 16:59:12 +0200 Subject: [PATCH 09/49] ds: add a new type "byte array producer" Introduced to write bytes to a byte array with size checking. --- common/utils/ds/CMakeLists.txt | 1 + common/utils/ds/byte_array_producer.c | 64 +++++++++++++++++++++++++++ common/utils/ds/byte_array_producer.h | 40 +++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 common/utils/ds/byte_array_producer.c create mode 100644 common/utils/ds/byte_array_producer.h diff --git a/common/utils/ds/CMakeLists.txt b/common/utils/ds/CMakeLists.txt index a5ae86c52f..b5f5efb47a 100644 --- a/common/utils/ds/CMakeLists.txt +++ b/common/utils/ds/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(ds OBJECT byte_array.c + byte_array_producer.c seq_arr.c ) diff --git a/common/utils/ds/byte_array_producer.c b/common/utils/ds/byte_array_producer.c new file mode 100644 index 0000000000..07f46c363c --- /dev/null +++ b/common/utils/ds/byte_array_producer.c @@ -0,0 +1,64 @@ +/* + * 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 + */ + +#include "byte_array_producer.h" + +/* create a byte array producer from an existing buffer */ +byte_array_producer_t byte_array_producer_from_buffer(uint8_t *buffer, int len) +{ + byte_array_producer_t result = { .b.len = len, .b.buf = buffer, .pos = 0 }; + return result; +} + +/* put a byte in a byte array if there is space + * returns 1 on success, 0 on error + */ +int byte_array_producer_put_byte(byte_array_producer_t *b, uint8_t byte) +{ + if (b->pos == b->b.len) + return 0; + + b->b.buf[b->pos] = byte; + b->pos++; + + return 1; +} + +/* put an u32 number as big endian in a byte array if there is space + * returns 1 on success, 0 on error + */ +int byte_array_producer_put_u32_be(byte_array_producer_t *b, uint32_t v) +{ + return byte_array_producer_put_byte(b, (v >> 24) & 0xff) + && byte_array_producer_put_byte(b, (v >> 16) & 0xff) + && byte_array_producer_put_byte(b, (v >> 8) & 0xff) + && byte_array_producer_put_byte(b, v & 0xff); +} + +/* put an u24 number as big endian in a byte array if there is space + * returns 1 on success, 0 on error + */ +int byte_array_producer_put_u24_be(byte_array_producer_t *b, uint32_t v) +{ + return byte_array_producer_put_byte(b, (v >> 16) & 0xff) + && byte_array_producer_put_byte(b, (v >> 8) & 0xff) + && byte_array_producer_put_byte(b, v & 0xff); +} diff --git a/common/utils/ds/byte_array_producer.h b/common/utils/ds/byte_array_producer.h new file mode 100644 index 0000000000..2087c77af1 --- /dev/null +++ b/common/utils/ds/byte_array_producer.h @@ -0,0 +1,40 @@ +/* + * 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 + */ + +#ifndef BYTE_ARRAY_PRODUCER_H +#define BYTE_ARRAY_PRODUCER_H + +#include + +#include "byte_array.h" + +typedef struct { + byte_array_t b; + size_t pos; +} byte_array_producer_t; + +byte_array_producer_t byte_array_producer_from_buffer(uint8_t *buffer, int len); + +int byte_array_producer_put_byte(byte_array_producer_t *b, uint8_t v); +int byte_array_producer_put_u32_be(byte_array_producer_t *b, uint32_t v); +int byte_array_producer_put_u24_be(byte_array_producer_t *b, uint32_t v); + +#endif From 5e0374883572a65e23ddc15ee1310e1b5f140459 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Fri, 1 Aug 2025 16:15:03 +0000 Subject: [PATCH 10/49] feat(rf_emulator): Add noise generation --- radio/emulator/CMakeLists.txt | 1 + radio/emulator/rf_emulator.c | 46 ++++++++++++++++++++++++++++++++--- radio/vrtsim/CMakeLists.txt | 6 +++-- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/radio/emulator/CMakeLists.txt b/radio/emulator/CMakeLists.txt index af25cf7019..42b196ee95 100644 --- a/radio/emulator/CMakeLists.txt +++ b/radio/emulator/CMakeLists.txt @@ -1,3 +1,4 @@ add_library(rf_emulator MODULE rf_emulator.c) set_target_properties(rf_emulator PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +target_link_libraries(rf_emulator PRIVATE noise_device) add_dependencies(rf_emulator generate_T) diff --git a/radio/emulator/rf_emulator.c b/radio/emulator/rf_emulator.c index 0ba9ea0636..c53ac7adf3 100644 --- a/radio/emulator/rf_emulator.c +++ b/radio/emulator/rf_emulator.c @@ -30,6 +30,8 @@ #include #include "common/utils/threadPool/pthread_utils.h" #include "common_lib.h" +#include "radio/vrtsim/noise_device.h" +#include "simde/x86/avx512.h" // structures and timing thread job for timing typedef struct { @@ -98,9 +100,11 @@ static void *emulator_timing_job(void *arg) */ static int emulator_start(openair0_device *device) { - // Start the timing thread + // Start the timing thread and the noise device emulator_state_t *emulator_state = (emulator_state_t *)device->priv; if (!emulator_state->run_timing_thread) { + init_noise_device(0); // TODO configure noise level + emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; memset(emulator_timestamp, 0, sizeof(emulator_timestamp_t)); @@ -155,12 +159,13 @@ int emulator_reset_stats(openair0_device *device) */ static void emulator_end(openair0_device *device) { - // Stop the timing thread + // Stop the timing thread and the noise device emulator_state_t *emulator_state = (emulator_state_t *)device->priv; if (emulator_state->run_timing_thread) { emulator_state->run_timing_thread = false; int ret = pthread_join(emulator_state->timing_thread, NULL); AssertFatal(ret == 0, "pthread_join() failed: errno: %d, %s\n", errno, strerror(errno)); + free_noise_device(); } } @@ -210,6 +215,41 @@ static int emulator_write(openair0_device *device, openair0_timestamp timestamp, return 0; } +/*! \brief Store noise in the buffer buff + * \param[out] buff A buffer for received samples. + * The buffers must be large enough to hold the number of samples \ref nsamps. + * \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte. + */ +static void read_noise(void *buff, int nsamps) +{ + int aligned_nsamps = ceil_mod(nsamps, (512 / 8) / sizeof(cf_t)); + cf_t samples[aligned_nsamps] __attribute__((aligned(64))); + // Apply noise from global settings + get_noise_vector((float *)samples, nsamps * 2); + + // Convert to c16_t + c16_t samples_out[aligned_nsamps] __attribute__((aligned(64))); +#if defined(__AVX512F__) + for (int i = 0; i < aligned_nsamps / 8; i++) { + simde__m512 *in = (simde__m512 *)&samples[i * 8]; + simde__m256i *out = (simde__m256i *)&samples_out[i * 8]; + *out = simde_mm512_cvtsepi32_epi16(simde_mm512_cvtps_epi32(*in)); + } +#elif defined(__AVX2__) + for (int i = 0; i < aligned_nsamps / 4; i++) { + simde__m256 *in = (simde__m256 *)&samples[i * 4]; + simde__m128i *out = (simde__m128i *)&samples_out[i * 4]; + *out = simde_mm256_cvtsepi32_epi16(simde_mm256_cvtps_epi32(*in)); + } +#else + for (int i = 0; i < nsamps; i++) { + samples_out[i].r = lroundf(samples[i].r); + samples_out[i].i = lroundf(samples[i].i); + } +#endif + memcpy(buff, samples_out, nsamps * sizeof(c16_t)); +} + /*! \brief Receive samples from hardware. * Read \ref nsamps samples from each channel to buffers. buff[0] is the array for * the first channel. @@ -244,7 +284,7 @@ static int emulator_read(openair0_device *device, openair0_timestamp *ptimestamp emulator_state->last_received_sample += nsamps; for (int i = 0; i < nbAnt; i++) { - memset(buff[i], 0, nsamps * sizeof(uint32_t)); // TODO Play random data + read_noise(buff[i], nsamps); } return nsamps; diff --git a/radio/vrtsim/CMakeLists.txt b/radio/vrtsim/CMakeLists.txt index 79eb4d88da..8586183099 100644 --- a/radio/vrtsim/CMakeLists.txt +++ b/radio/vrtsim/CMakeLists.txt @@ -1,4 +1,6 @@ -add_library(vrtsim MODULE vrtsim.c noise_device.c) +add_library(noise_device STATIC noise_device.c) + +add_library(vrtsim MODULE vrtsim.c) set_target_properties(vrtsim PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -target_link_libraries(vrtsim PRIVATE SIMU shm_td_iq_channel actor) +target_link_libraries(vrtsim PRIVATE SIMU shm_td_iq_channel actor noise_device) add_dependencies(vrtsim generate_T) From dfa77647b1f9a1f877b5c8e3fa6832259a296a59 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Mon, 4 Aug 2025 15:29:25 +0000 Subject: [PATCH 11/49] feat(rf_emulator): Add late writes counter --- radio/emulator/rf_emulator.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/radio/emulator/rf_emulator.c b/radio/emulator/rf_emulator.c index c53ac7adf3..a747afc053 100644 --- a/radio/emulator/rf_emulator.c +++ b/radio/emulator/rf_emulator.c @@ -39,6 +39,7 @@ typedef struct { pthread_mutex_t mutex; pthread_cond_t cond; uint64_t late_reads; + uint64_t late_writes; } emulator_timestamp_t; typedef struct { @@ -72,6 +73,10 @@ static void *emulator_timing_job(void *arg) LOG_W(HW, "%ld RF reads were late\n", emulator_timestamp->late_reads); } emulator_timestamp->late_reads = 0; + if (emulator_timestamp->late_writes != 0) { + LOG_W(HW, "%ld RF writes were late\n", emulator_timestamp->late_writes); + } + emulator_timestamp->late_writes = 0; } uint64_t diff = (current_time.tv_sec - timestamp.tv_sec) * 1000000000 + (current_time.tv_nsec - timestamp.tv_nsec); @@ -212,6 +217,17 @@ int emulator_write_init(openair0_device *device) */ static int emulator_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int nbAnt, int flags) { + emulator_state_t *emulator_state = (emulator_state_t *)device->priv; + + // timestamp in the past + emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; + uint64_t current_timestamp = emulator_timestamp->timestamp; + if (timestamp < current_timestamp) { + mutexlock(emulator_timestamp->mutex); + emulator_timestamp->late_writes++; + mutexunlock(emulator_timestamp->mutex); + } + return 0; } From 29108a0974d458ba423837bcfe1afd7f442c6350 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Fri, 1 Aug 2025 15:22:50 +0200 Subject: [PATCH 12/49] cleaning syntax of nr-ru.c --- executables/nr-ru.c | 979 +++++++++++++++++++++----------------------- 1 file changed, 459 insertions(+), 520 deletions(-) diff --git a/executables/nr-ru.c b/executables/nr-ru.c index c0da88e634..72bc44bffb 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -63,7 +63,6 @@ static int DEFRUTPCORES[] = {-1,-1,-1,-1}; #include - #include "T.h" #include "nfapi_interface.h" #include @@ -74,63 +73,69 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg); /*************************************************************/ /* Functions to attach and configure RRU */ -int attach_rru(RU_t *ru) { - ssize_t msg_len,len; +int attach_rru(RU_t *ru) +{ RRU_CONFIG_msg_t rru_config_msg; int received_capabilities=0; wait_gNBs(); // Wait for capabilities while (received_capabilities==0) { - memset((void *)&rru_config_msg,0,sizeof(rru_config_msg)); - rru_config_msg.type = RAU_tick; - rru_config_msg.len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE; + rru_config_msg = (RRU_CONFIG_msg_t){.type = RAU_tick, .len = sizeof(rru_config_msg.msg)}; LOG_D(PHY, "Sending RAU tick to RRU %d\n", ru->idx); AssertFatal((ru->ifdevice.trx_ctlsend_func(&ru->ifdevice,&rru_config_msg,rru_config_msg.len)!=-1), "RU %d cannot access remote radio\n",ru->idx); - msg_len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_capabilities_t); - + ssize_t msg_len = rru_config_msg.len + sizeof(RRU_capabilities_t); // wait for answer with timeout - if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, - &rru_config_msg, - msg_len))<0) { + ssize_t len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, &rru_config_msg, msg_len); + if (len < 0) { LOG_D(PHY, "Waiting for RRU %d\n", ru->idx); } else if (rru_config_msg.type == RRU_capabilities) { - AssertFatal(rru_config_msg.len==msg_len,"Received capabilities with incorrect length (%d!=%d)\n",(int)rru_config_msg.len,(int)msg_len); - LOG_I(PHY,"Received capabilities from RRU %d (len %d/%d, num_bands %d,max_pdschReferenceSignalPower %d, max_rxgain %d, nb_tx %d, nb_rx %d)\n",ru->idx, - (int)rru_config_msg.len,(int)msg_len, - ((RRU_capabilities_t *)&rru_config_msg.msg[0])->num_bands, - ((RRU_capabilities_t *)&rru_config_msg.msg[0])->max_pdschReferenceSignalPower[0], - ((RRU_capabilities_t *)&rru_config_msg.msg[0])->max_rxgain[0], - ((RRU_capabilities_t *)&rru_config_msg.msg[0])->nb_tx[0], - ((RRU_capabilities_t *)&rru_config_msg.msg[0])->nb_rx[0]); + AssertFatal(rru_config_msg.len == msg_len, + "Received capabilities with incorrect length (%ld!=%ld)\n", + rru_config_msg.len, + msg_len); + RRU_capabilities_t *cap = (RRU_capabilities_t *)rru_config_msg.msg; + LOG_I(PHY, + "Received capabilities from RRU %d (len %ld/%ld, num_bands %d,max_pdschReferenceSignalPower %d, max_rxgain %d, nb_tx " + "%d, nb_rx %d)\n", + ru->idx, + rru_config_msg.len, + msg_len, + cap->num_bands, + cap->max_pdschReferenceSignalPower[0], + cap->max_rxgain[0], + cap->nb_tx[0], + cap->nb_rx[0]); received_capabilities=1; } else { LOG_E(PHY,"Received incorrect message %d from RRU %d\n",rru_config_msg.type,ru->idx); } } - configure_ru(ru, (RRU_capabilities_t *)&rru_config_msg.msg[0]); + configure_ru(ru, (RRU_capabilities_t *)rru_config_msg.msg); rru_config_msg.type = RRU_config; rru_config_msg.len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_config_t); - LOG_I(PHY,"Sending Configuration to RRU %d (num_bands %d,band0 %d,txfreq %u,rxfreq %u,att_tx %d,att_rx %d,N_RB_DL %d,N_RB_UL %d,3/4FS %d, prach_FO %d, prach_CI %d)\n",ru->idx, - ((RRU_config_t *)&rru_config_msg.msg[0])->num_bands, - ((RRU_config_t *)&rru_config_msg.msg[0])->band_list[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->tx_freq[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->rx_freq[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->att_tx[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->att_rx[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_DL[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_UL[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->threequarter_fs[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->prach_FreqOffset[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->prach_ConfigIndex[0]); + RRU_config_t *conf = (RRU_config_t *)rru_config_msg.msg; + LOG_I(PHY, + "Sending Configuration to RRU %d (num_bands %d,band0 %d,txfreq %u,rxfreq %u,att_tx %d,att_rx %d,N_RB_DL %d,N_RB_UL " + "%d,3/4FS %d, prach_FO %d, prach_CI %d)\n", + ru->idx, + conf->num_bands, + conf->band_list[0], + conf->tx_freq[0], + conf->rx_freq[0], + conf->att_tx[0], + conf->att_rx[0], + conf->N_RB_DL[0], + conf->N_RB_UL[0], + conf->threequarter_fs[0], + conf->prach_FreqOffset[0], + conf->prach_ConfigIndex[0]); AssertFatal((ru->ifdevice.trx_ctlsend_func(&ru->ifdevice,&rru_config_msg,rru_config_msg.len)!=-1), "RU %d failed send configuration to remote radio\n",ru->idx); - - if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, - &rru_config_msg, - msg_len))<0) { + int len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, &rru_config_msg, sizeof(rru_config_msg.msg)); + if (len < 0) { LOG_I(PHY,"Waiting for RRU %d\n",ru->idx); } else if (rru_config_msg.type == RRU_config_ok) { LOG_I(PHY, "RRU_config_ok received\n"); @@ -142,58 +147,57 @@ int attach_rru(RU_t *ru) { } int connect_rau(RU_t *ru) { - RRU_CONFIG_msg_t rru_config_msg; - ssize_t msg_len; - int tick_received = 0; - int configuration_received = 0; - RRU_capabilities_t *cap; - int i; - int len; + RRU_CONFIG_msg_t rru_config_msg; // wait for RAU_tick + int tick_received = 0; while (tick_received == 0) { - msg_len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE; - - if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, - &rru_config_msg, - msg_len))<0) { + ssize_t msg_len = sizeof(rru_config_msg.msg); + int len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, &rru_config_msg, msg_len); + if (len < 0) { LOG_I(PHY,"Waiting for RAU\n"); } else { if (rru_config_msg.type == RAU_tick) { LOG_I(PHY,"Tick received from RAU\n"); tick_received = 1; - } else LOG_E(PHY,"Received erroneous message (%d)from RAU, expected RAU_tick\n",rru_config_msg.type); + } else + LOG_E(PHY, "Received erroneous message (%d)from RAU, expected RAU_tick\n", rru_config_msg.type); } } // send capabilities rru_config_msg.type = RRU_capabilities; rru_config_msg.len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_capabilities_t); - cap = (RRU_capabilities_t *)&rru_config_msg.msg[0]; - LOG_I(PHY,"Sending Capabilities (len %d, num_bands %d,max_pdschReferenceSignalPower %d, max_rxgain %d, nb_tx %d, nb_rx %d)\n", - (int)rru_config_msg.len,ru->num_bands,ru->max_pdschReferenceSignalPower,ru->max_rxgain,ru->nb_tx,ru->nb_rx); + RRU_capabilities_t *cap = (RRU_capabilities_t *)rru_config_msg.msg; + LOG_I(PHY, + "Sending Capabilities (len %ld, num_bands %d,max_pdschReferenceSignalPower %d, max_rxgain %d, nb_tx %d, nb_rx %d)\n", + rru_config_msg.len, + ru->num_bands, + ru->max_pdschReferenceSignalPower, + ru->max_rxgain, + ru->nb_tx, + ru->nb_rx); switch (ru->function) { case NGFI_RRU_IF4p5: - cap->FH_fmt = OAI_IF4p5_only; + cap->FH_fmt = OAI_IF4p5_only; break; case NGFI_RRU_IF5: - cap->FH_fmt = OAI_IF5_only; + cap->FH_fmt = OAI_IF5_only; break; case MBP_RRU_IF5: - cap->FH_fmt = MBP_IF5; + cap->FH_fmt = MBP_IF5; break; default: - AssertFatal(1==0,"RU_function is unknown %d\n",RC.ru[0]->function); + AssertFatal(false, "RU_function is unknown %d\n", RC.ru[0]->function); break; } - cap->num_bands = ru->num_bands; - - for (i=0; inum_bands; i++) { + cap->num_bands = ru->num_bands; + for (int i = 0; i < ru->num_bands; i++) { LOG_I(PHY,"Band %d: nb_rx %d nb_tx %d pdschReferenceSignalPower %d rxgain %d\n", ru->band[i],ru->nb_rx,ru->nb_tx,ru->max_pdschReferenceSignalPower,ru->max_rxgain); cap->band_list[i] = ru->band[i]; @@ -208,25 +212,28 @@ int connect_rau(RU_t *ru) { // wait for configuration rru_config_msg.len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_config_t); + int configuration_received = 0; while (configuration_received == 0) { - if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, - &rru_config_msg, - rru_config_msg.len))<0) { + int len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, &rru_config_msg, rru_config_msg.len); + if (len < 0) { LOG_I(PHY,"Waiting for configuration from RAU\n"); } else { - LOG_I(PHY,"Configuration received from RAU (num_bands %d,band0 %d,txfreq %u,rxfreq %u,att_tx %d,att_rx %d,N_RB_DL %d,N_RB_UL %d,3/4FS %d, prach_FO %d, prach_CI %d)\n", - ((RRU_config_t *)&rru_config_msg.msg[0])->num_bands, - ((RRU_config_t *)&rru_config_msg.msg[0])->band_list[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->tx_freq[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->rx_freq[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->att_tx[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->att_rx[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_DL[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_UL[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->threequarter_fs[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->prach_FreqOffset[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->prach_ConfigIndex[0]); - configure_rru(ru, (void *)&rru_config_msg.msg[0]); + RRU_config_t *conf = (RRU_config_t *)rru_config_msg.msg; + LOG_I(PHY, + "Configuration received from RAU (num_bands %d,band0 %d,txfreq %u,rxfreq %u,att_tx %d,att_rx %d,N_RB_DL %d,N_RB_UL " + "%d,3/4FS %d, prach_FO %d, prach_CI %d)\n", + conf->num_bands, + conf->band_list[0], + conf->tx_freq[0], + conf->rx_freq[0], + conf->att_tx[0], + conf->att_rx[0], + conf->N_RB_DL[0], + conf->N_RB_UL[0], + conf->threequarter_fs[0], + conf->prach_FreqOffset[0], + conf->prach_ConfigIndex[0]); + configure_rru(ru, (void *)rru_config_msg.msg); configuration_received = 1; } } @@ -238,15 +245,23 @@ int connect_rau(RU_t *ru) { // southbound IF5 fronthaul for 16-bit OAI format void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { - if (ru == RC.ru[0]) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx&0xffffffff ); + if (ru == RC.ru[0]) + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx & 0xffffffff); int offset = ru->nr_frame_parms->get_samples_slot_timestamp(slot,ru->nr_frame_parms,0); void *buffs[ru->nb_tx]; for (int aid = 0; aid < ru->nb_tx; aid++) buffs[aid] = (void*)&ru->common.txdata[aid][offset]; struct timespec txmeas; clock_gettime(CLOCK_MONOTONIC, &txmeas); - LOG_D(NR_PHY,"IF5 TX %d.%d, TS %llu, buffs[0] %p, buffs[1] %p ener0 %f dB, tx start %d\n",frame,slot,(unsigned long long)timestamp,buffs[0],buffs[1], - 10*log10((double)signal_energy(buffs[0],ru->nr_frame_parms->get_samples_per_slot(slot,ru->nr_frame_parms))),(int)txmeas.tv_nsec); + LOG_D(NR_PHY, + "IF5 TX %d.%d, TS %lu, buffs[0] %p, buffs[1] %p ener0 %f dB, tx start %d\n", + frame, + slot, + timestamp, + buffs[0], + buffs[1], + 10 * log10((double)signal_energy(buffs[0], ru->nr_frame_parms->get_samples_per_slot(slot, ru->nr_frame_parms))), + (int)txmeas.tv_nsec); ru->ifdevice.trx_write_func2(&ru->ifdevice, timestamp, buffs, @@ -258,13 +273,12 @@ void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { // southbound IF4p5 fronthaul void fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { - nfapi_nr_config_request_scf_t *cfg = &ru->config; - - if (ru == RC.ru[0]) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx&0xffffffff ); + if (ru == RC.ru[0]) + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx & 0xffffffff); LOG_D(PHY,"Sending IF4p5 for frame %d subframe %d\n",ru->proc.frame_tx,ru->proc.tti_tx); - if ((nr_slot_select(cfg,ru->proc.frame_tx,ru->proc.tti_tx)&NR_DOWNLINK_SLOT) > 0) + if ((nr_slot_select(&ru->config, ru->proc.frame_tx, ru->proc.tti_tx) & NR_DOWNLINK_SLOT) > 0) send_IF4p5(ru,frame, slot, IF4p5_PDLFFT); } @@ -281,11 +295,11 @@ void fh_if5_south_in(RU_t *ru, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_RECV_IF5, 1 ); start_meas(&ru->rx_fhaul); - ru->ifdevice.trx_read_func2(&ru->ifdevice,&proc->timestamp_rx,NULL,fp->get_samples_per_slot(*tti,fp)); - if (proc->first_rx == 1) ru->ts_offset = proc->timestamp_rx; - proc->frame_rx = ((proc->timestamp_rx-ru->ts_offset) / (fp->samples_per_subframe*10))&1023; - proc->tti_rx = fp->get_slot_from_timestamp(proc->timestamp_rx-ru->ts_offset,fp); - + ru->ifdevice.trx_read_func2(&ru->ifdevice, &proc->timestamp_rx, NULL, fp->get_samples_per_slot(*tti, fp)); + if (proc->first_rx == 1) + ru->ts_offset = proc->timestamp_rx; + proc->frame_rx = ((proc->timestamp_rx - ru->ts_offset) / (fp->samples_per_subframe * 10)) & 1023; + proc->tti_rx = fp->get_slot_from_timestamp(proc->timestamp_rx - ru->ts_offset, fp); if (proc->first_rx == 0) { if (proc->tti_rx != *tti) { @@ -311,8 +325,26 @@ void fh_if5_south_in(RU_t *ru, struct timespec rxmeas; clock_gettime(CLOCK_MONOTONIC, &rxmeas); double fhtime = ru->rx_fhaul.p_time/(cpu_freq_GHz*1000.0); - if (fhtime > 800) LOG_W(PHY,"IF5 %d.%d => RX %d.%d first_rx %d: time %f, rxstart %d\n",*frame,*tti,proc->frame_rx,proc->tti_rx,proc->first_rx,ru->rx_fhaul.p_time/(cpu_freq_GHz*1000.0),(int)rxmeas.tv_nsec); - else LOG_D(PHY,"IF5 %d.%d => RX %d.%d first_rx %d: time %f, rxstart %d\n",*frame,*tti,proc->frame_rx,proc->tti_rx,proc->first_rx,ru->rx_fhaul.p_time/(cpu_freq_GHz*1000.0),(int)rxmeas.tv_nsec); + if (fhtime > 800) + LOG_W(PHY, + "IF5 %d.%d => RX %d.%d first_rx %d: time %f, rxstart %ld\n", + *frame, + *tti, + proc->frame_rx, + proc->tti_rx, + proc->first_rx, + ru->rx_fhaul.p_time / (cpu_freq_GHz * 1000.0), + rxmeas.tv_nsec); + else + LOG_D(PHY, + "IF5 %d.%d => RX %d.%d first_rx %d: time %f, rxstart %ld\n", + *frame, + *tti, + proc->frame_rx, + proc->tti_rx, + proc->first_rx, + ru->rx_fhaul.p_time / (cpu_freq_GHz * 1000.0), + rxmeas.tv_nsec); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, proc->timestamp_rx&0xffffffff ); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_RECV_IF5, 0 ); @@ -329,21 +361,16 @@ void fh_if4p5_south_in(RU_t *ru, uint32_t symbol_number=0; uint32_t symbol_mask_full=0; - /* - if ((fp->frame_type == TDD) && (subframe_select(fp,*slot)==SF_S)) - symbol_mask_full = (1<ul_symbols_in_S_subframe)-1; - else - symbol_mask_full = (1<symbols_per_slot)-1; - - AssertFatal(proc->symbol_mask[*slot]==0,"rx_fh_if4p5: proc->symbol_mask[%d] = %x\n",*slot,proc->symbol_mask[*slot]);*/ do { // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!! recv_IF4p5(ru, &f, &sl, &packet_type, &symbol_number); if (packet_type == IF4p5_PULFFT) proc->symbol_mask[sl] = proc->symbol_mask[sl] | (1<first_rx==0) && (f!=*frame)) LOG_E(PHY,"rx_fh_if4p5: PULTICK received frame %d != expected %d\n",f,*frame); + if ((proc->first_rx == 0) && (f != *frame)) + LOG_E(PHY, "rx_fh_if4p5: PULTICK received frame %d != expected %d\n", f, *frame); - if ((proc->first_rx==0) && (sl!=*slot)) LOG_E(PHY,"rx_fh_if4p5: PULTICK received subframe %d != expected %d (first_rx %d)\n",sl,*slot,proc->first_rx); + if ((proc->first_rx == 0) && (sl != *slot)) + LOG_E(PHY, "rx_fh_if4p5: PULTICK received subframe %d != expected %d (first_rx %d)\n", sl, *slot, proc->first_rx); break; } else if (packet_type == IF4p5_PRACH) { @@ -359,7 +386,7 @@ void fh_if4p5_south_in(RU_t *ru, proc->timestamp_rx = (proc->frame_rx * fp->samples_per_subframe * 10) + fp->get_samples_slot_timestamp(proc->tti_rx, fp, 0); // proc->timestamp_tx = proc->timestamp_rx + (4*fp->samples_per_subframe); proc->tti_tx = (sl+ru->sl_ahead)%fp->slots_per_frame; - proc->frame_tx = (sl>(fp->slots_per_frame-1-(ru->sl_ahead))) ? (f+1)&1023 : f; + proc->frame_tx = (sl > (fp->slots_per_frame - 1 - (ru->sl_ahead))) ? (f + 1) & 1023 : f; if (proc->first_rx == 0) { if (proc->tti_rx != *slot) { @@ -394,20 +421,12 @@ void fh_if4p5_south_asynch_in(RU_t *ru,int *frame,int *slot) { NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; RU_proc_t *proc = &ru->proc; uint16_t packet_type; - uint32_t symbol_number,symbol_mask,prach_rx; - // uint32_t got_prach_info=0; - symbol_number = 0; - symbol_mask = (1<<(fp->symbols_per_slot))-1; - prach_rx = 0; + uint32_t symbol_number = 0; + uint32_t symbol_mask = (1 << fp->symbols_per_slot) - 1; + uint32_t prach_rx = 0; do { // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!! recv_IF4p5(ru, &proc->frame_rx, &proc->tti_rx, &packet_type, &symbol_number); - - // grab first prach information for this new subframe - /*if (got_prach_info==0) { - prach_rx = is_prach_subframe(fp, proc->frame_rx, proc->tti_rx); - got_prach_info = 1; - }*/ if (proc->first_rx != 0) { *frame = proc->frame_rx; *slot = proc->tti_rx; @@ -417,22 +436,19 @@ void fh_if4p5_south_asynch_in(RU_t *ru,int *frame,int *slot) { LOG_E(PHY,"frame_rx %d is not what we expect %d\n",proc->frame_rx,*frame); exit_fun("Exiting"); } - if (proc->tti_rx != *slot) { LOG_E(PHY,"tti_rx %d is not what we expect %d\n",proc->tti_rx,*slot); exit_fun("Exiting"); } } - if (packet_type == IF4p5_PULFFT) symbol_mask &= (~(1< 0) || (prach_rx >0)); // haven't received all PUSCH symbols and PRACH information + if (packet_type == IF4p5_PULFFT) + symbol_mask &= ~(1 << symbol_number); + else if (packet_type == IF4p5_PRACH) + prach_rx &= ~0x1; + } while (symbol_mask > 0 || prach_rx > 0); // haven't received all PUSCH symbols and PRACH information } - - - - /*************************************************************/ /* Input Fronthaul from North RRU */ @@ -459,26 +475,9 @@ void fh_if4p5_north_in(RU_t *ru,int *frame,int *slot) { } } -void fh_if5_north_asynch_in(RU_t *ru,int *frame,int *slot) { - NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; - RU_proc_t *proc = &ru->proc; - int tti_tx,frame_tx; - openair0_timestamp timestamp_tx = 0; +void fh_if5_north_asynch_in(RU_t *ru, int *frame, int *slot) +{ AssertFatal(1 == 0, "Shouldn't get here\n"); - frame_tx = (timestamp_tx / (fp->samples_per_subframe*10))&1023; - uint32_t idx_sf = timestamp_tx / fp->samples_per_subframe; - tti_tx = (idx_sf * fp->slots_per_subframe + (int)round((float)(timestamp_tx % fp->samples_per_subframe) / fp->samples_per_slot0))%(fp->slots_per_frame); - - if (proc->first_tx != 0) { - *slot = tti_tx; - *frame = frame_tx; - proc->first_tx = 0; - } else { - AssertFatal(tti_tx == *slot, - "tti_tx %d is not what we expect %d\n",tti_tx,*slot); - AssertFatal(frame_tx == *frame, - "frame_tx %d is not what we expect %d\n",frame_tx,*frame); - } } void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { @@ -486,17 +485,18 @@ void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { nfapi_nr_config_request_scf_t *cfg = &ru->config; RU_proc_t *proc = &ru->proc; uint16_t packet_type; - uint32_t symbol_number,symbol_mask,symbol_mask_full=0; + uint32_t symbol_mask_full = 0; int slot_tx,frame_tx; LOG_D(PHY, "%s(ru:%p frame, subframe)\n", __FUNCTION__, ru); - symbol_number = 0; - symbol_mask = 0; + uint32_t symbol_number = 0; + uint32_t symbol_mask = 0; // symbol_mask_full = ((subframe_select(fp,*slot) == SF_S) ? (1<dl_symbols_in_S_subframe) : (1<symbols_per_slot))-1; do { recv_IF4p5(ru, &frame_tx, &slot_tx, &packet_type, &symbol_number); - if (((nr_slot_select(cfg,frame_tx,slot_tx) & NR_DOWNLINK_SLOT) > 0) && (symbol_number == 0)) start_meas(&ru->rx_fhaul); + if (((nr_slot_select(cfg, frame_tx, slot_tx) & NR_DOWNLINK_SLOT) > 0) && (symbol_number == 0)) + start_meas(&ru->rx_fhaul); LOG_D(PHY,"slot %d (%d): frame %d, slot %d, symbol %d\n", *slot,nr_slot_select(cfg,frame_tx,*slot),frame_tx,slot_tx,symbol_number); @@ -505,7 +505,6 @@ void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { *frame = frame_tx; *slot = slot_tx; proc->first_tx = 0; - //symbol_mask_full = ((subframe_select(fp,*slot) == SF_S) ? (1<dl_symbols_in_S_subframe) : (1<symbols_per_slot))-1; } else { AssertFatal(frame_tx == *frame, "frame_tx %d is not what we expect %d\n",frame_tx,*frame); @@ -515,18 +514,22 @@ void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { if (packet_type == IF4p5_PDLFFT) { symbol_mask = symbol_mask | (1<0) stop_meas(&ru->rx_fhaul); + if ((nr_slot_select(cfg, frame_tx, slot_tx) & NR_DOWNLINK_SLOT) > 0) + stop_meas(&ru->rx_fhaul); proc->tti_tx = slot_tx; proc->frame_tx = frame_tx; - if ((frame_tx == 0)&&(slot_tx == 0)) proc->frame_tx_unwrap += 1024; + if (frame_tx == 0 && slot_tx == 0) + proc->frame_tx_unwrap += 1024; - proc->timestamp_tx = (((uint64_t)frame_tx + (uint64_t)proc->frame_tx_unwrap) * fp->samples_per_subframe * 10) + fp->get_samples_slot_timestamp(slot_tx, fp, 0); - LOG_D(PHY,"RU %d/%d TST %llu, frame %d, subframe %d\n",ru->idx,0,(long long unsigned int)proc->timestamp_tx,frame_tx,slot_tx); + proc->timestamp_tx = + ((uint64_t)frame_tx + proc->frame_tx_unwrap) * fp->samples_per_subframe * 10 + fp->get_samples_slot_timestamp(slot_tx, fp, 0); + LOG_D(PHY, "RU %d/%d TST %lu, frame %d, subframe %d\n", ru->idx, 0, proc->timestamp_tx, frame_tx, slot_tx); // dump VCD output for first RU in list if (ru == RC.ru[0]) { @@ -544,24 +547,15 @@ void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { void fh_if5_north_out(RU_t *ru) { /// **** send_IF5 of rxdata to BBU **** /// VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_SEND_IF5, 1 ); - AssertFatal(1==0,"Shouldn't get here\n"); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_SEND_IF5, 0 ); + AssertFatal(1 == 0, "Shouldn't get here\n"); } // RRU IF4p5 northbound interface (RX) void fh_if4p5_north_out(RU_t *ru) { RU_proc_t *proc=&ru->proc; + if (ru->idx == 0) + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU, proc->tti_rx); - //NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; - //const int subframe = proc->tti_rx; - if (ru->idx==0) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU, proc->tti_rx ); - - /* - if ((fp->frame_type == TDD) && (subframe_select(fp,subframe)!=SF_UL)) { - /// **** in TDD during DL send_IF4 of ULTICK to RCC **** /// - send_IF4p5(ru, proc->frame_rx, proc->tti_rx, IF4p5_PULTICK); - return; - }*/ start_meas(&ru->tx_fhaul); send_IF4p5(ru, proc->frame_rx, proc->tti_rx, IF4p5_PULFFT); stop_meas(&ru->tx_fhaul); @@ -572,7 +566,7 @@ void *emulatedRF_thread(void *param) { int microsec = 500; // length of time to sleep, in miliseconds struct timespec req = {0}; req.tv_sec = 0; - req.tv_nsec = (numerology>0)? ((microsec * 1000L)/numerology):(microsec * 1000L)*2; + req.tv_nsec = numerology > 0 ? (microsec * 1000L) / numerology : microsec * 1000L * 2; wait_sync("emulatedRF_thread"); while(!oai_exit) { @@ -618,8 +612,6 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 ); proc->timestamp_rx = ts-ru->ts_offset; - //AssertFatal(rxs == fp->samples_per_subframe, - //"rx_rf: Asked for %d samples, got %d from USRP\n",fp->samples_per_subframe,rxs); if (rxs != samples_per_slot) LOG_E(PHY, "rx_rf: Asked for %d samples, got %d from USRP\n", samples_per_slot, rxs); @@ -636,23 +628,26 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) } } - //compute system frame number (SFN) according to O-RAN-WG4-CUS.0-v02.00 (using alpha=beta=0) - // this assumes that the USRP has been synchronized to the GPS time - // OAI uses timestamps in sample time stored in int64_t, but it will fit in double precision for many years to come. - double gps_sec = ((double) ts)/cfg->sample_rate; - //proc->frame_rx = ((int64_t) (gps_sec/0.01)) & 1023; + // compute system frame number (SFN) according to O-RAN-WG4-CUS.0-v02.00 (using alpha=beta=0) + // this assumes that the USRP has been synchronized to the GPS time + // OAI uses timestamps in sample time stored in int64_t, but it will fit in double precision for many years to come. + double gps_sec = ((double)ts) / cfg->sample_rate; // in fact the following line is the same as long as the timestamp_rx is synchronized to GPS. proc->frame_rx = (proc->timestamp_rx / (fp->samples_per_subframe*10))&1023; proc->tti_rx = fp->get_slot_from_timestamp(proc->timestamp_rx,fp); // synchronize first reception to frame 0 subframe 0 - LOG_D(PHY,"RU %d/%d TS %ld, GPS %f, SR %f, frame %d, slot %d.%d / %d\n", + LOG_D(PHY, + "RU %d/%d TS %ld, GPS %f, SR %f, frame %d, slot %d.%d / %d\n", ru->idx, 0, - ts, //(unsigned long long int)(proc->timestamp_rx+ru->ts_offset), - gps_sec, - cfg->sample_rate, - proc->frame_rx,proc->tti_rx,proc->tti_tx,fp->slots_per_frame); + ts, + gps_sec, + cfg->sample_rate, + proc->frame_rx, + proc->tti_rx, + proc->tti_tx, + fp->slots_per_frame); // dump VCD output for first RU in list if (ru == RC.ru[0]) { @@ -662,15 +657,19 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) if (proc->first_rx == 0) { if (proc->tti_rx != *slot) { - LOG_E(PHY,"Received Timestamp (%llu) doesn't correspond to the time we think it is (proc->tti_rx %d, slot %d)\n",(long long unsigned int)proc->timestamp_rx,proc->tti_rx,*slot); + LOG_E(PHY, + "Received Timestamp (%lu) doesn't correspond to the time we think it is (proc->tti_rx %d, slot %d)\n", + proc->timestamp_rx, + proc->tti_rx, + *slot); exit_fun("Exiting"); } if (proc->frame_rx != *frame) { LOG_E(PHY, - "Received Timestamp (%llu) doesn't correspond to the time we think it is (proc->frame_rx %d frame %d, proc->tti_rx %d, " + "Received Timestamp (%lu) doesn't correspond to the time we think it is (proc->frame_rx %d frame %d, proc->tti_rx %d, " "slot %d)\n", - (long long unsigned int)proc->timestamp_rx, + proc->timestamp_rx, proc->frame_rx, *frame, proc->tti_rx, @@ -720,6 +719,7 @@ static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot) } } break; + case RU_GPIO_CONTROL_INTERDIGITAL: { // the beam index is written in bits 8-10 of the flags // bit 11 enables the gpio programming @@ -805,9 +805,7 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) flags_gpio = get_gpio_flags(ru, slot); const int flags = flags_burst | (flags_gpio << 4); - - if (proc->first_tx == 1) - proc->first_tx = 0; + proc->first_tx = 0; VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_WRITE_FLAGS, flags); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, frame); @@ -828,10 +826,10 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) nt, flags); LOG_D(PHY, - "[TXPATH] RU %d tx_rf, writing to TS %llu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, " + "[TXPATH] RU %d tx_rf, writing to TS %lu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, " "returned %d, E %f\n", ru->idx, - (long long unsigned int)(timestamp + ru->ts_offset - sf_extension), + timestamp + ru->ts_offset - sf_extension, frame, slot, proc->frame_tx_unwrap, @@ -841,7 +839,6 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) txs, 10 * log10((double)signal_energy(txp[0], siglen + sf_extension))); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0); - // AssertFatal(txs == 0,"trx write function error %d\n", txs); } static void fill_rf_config(RU_t *ru, char *rf_config_file) @@ -880,12 +877,12 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file) for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { if (ru->if_frequency == 0) { - cfg->tx_freq[i] = (double)fp->dl_CarrierFreq; + cfg->tx_freq[i] = fp->dl_CarrierFreq; } else if (ru->if_freq_offset) { - cfg->tx_freq[i] = (double)(ru->if_frequency); + cfg->tx_freq[i] = ru->if_frequency; LOG_I(PHY, "Setting IF TX frequency to %lu Hz with IF TX frequency offset %d Hz\n", ru->if_frequency, ru->if_freq_offset); } else { - cfg->tx_freq[i] = (double)ru->if_frequency; + cfg->tx_freq[i] = ru->if_frequency; } cfg->tx_gain[i] = ru->att_tx; @@ -895,12 +892,12 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file) for (int i = 0; i < ru->nb_rx * ru->num_beams_period; i++) { if (ru->if_frequency == 0) { - cfg->rx_freq[i] = (double)fp->ul_CarrierFreq; + cfg->rx_freq[i] = fp->ul_CarrierFreq; } else if (ru->if_freq_offset) { - cfg->rx_freq[i] = (double)(ru->if_frequency + ru->if_freq_offset); + cfg->rx_freq[i] = ru->if_frequency + ru->if_freq_offset; LOG_I(PHY, "Setting IF RX frequency to %lu Hz with IF RX frequency offset %d Hz\n", ru->if_frequency, ru->if_freq_offset); } else { - cfg->rx_freq[i] = (double)(ru->if_frequency+fp->ul_CarrierFreq-fp->dl_CarrierFreq); + cfg->rx_freq[i] = ru->if_frequency + fp->ul_CarrierFreq - fp->dl_CarrierFreq; } cfg->rx_gain[i] = ru->max_rxgain-ru->att_rx; @@ -941,17 +938,11 @@ static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_ antennas are mapped to successive RF chains on the same card. */ int setup_RU_buffers(RU_t *ru) { - int card,ant; - //uint16_t N_TA_offset = 0; - NR_DL_FRAME_PARMS *fp; + if (!ru) + return (-1); + + NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; nfapi_nr_config_request_scf_t *config = &ru->config; - - if (ru) { - fp = ru->nr_frame_parms; - } else { - return(-1); - } - int mu = config->ssb_config.scs_common.value; int N_RB = config->carrier_config.dl_grid_size[config->ssb_config.scs_common.value].value; @@ -963,8 +954,8 @@ int setup_RU_buffers(RU_t *ru) if (ru->openair0_cfg.mmapped_dma == 1) { // replace RX signal buffers with mmaped HW versions for (int i = 0; i < ru->nb_rx * ru->num_beams_period; i++) { - card = i / 4; - ant = i % 4; + int card = i / 4; + int ant = i % 4; LOG_D(PHY, "Mapping RU id %u, rx_ant %d, on card %d, chain %d\n", ru->idx, i, ru->rf_map.card + card, ru->rf_map.chain + ant); free(ru->common.rxdata[i]); ru->common.rxdata[i] = ru->openair0_cfg.rxbase[ru->rf_map.chain + ant]; @@ -975,8 +966,8 @@ int setup_RU_buffers(RU_t *ru) } for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { - card = i / 4; - ant = i % 4; + int card = i / 4; + int ant = i % 4; LOG_D(PHY, "Mapping RU id %u, tx_ant %d, on card %d, chain %d\n", ru->idx, i, ru->rf_map.card + card, ru->rf_map.chain + ant); free(ru->common.txdata[i]); ru->common.txdata[i] = ru->openair0_cfg.txbase[ru->rf_map.chain + ant]; @@ -985,8 +976,9 @@ int setup_RU_buffers(RU_t *ru) ru->common.txdata[i][j] = 16 - j; } } - } else { // not memory-mapped DMA - //nothing to do, everything already allocated in lte_init + } else { + // not memory-mapped DMA + // nothing to do, everything already allocated in lte_init } return(0); @@ -1007,14 +999,13 @@ void ru_tx_func(void *param) ru->feptx_prec(ru,frame_tx,slot_tx); // do OFDM with/without TX front-end processing if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) + if (ru->fh_north_asynch_in == NULL && ru->feptx_ofdm) ru->feptx_ofdm(ru, frame_tx, slot_tx); if(!emulate_rf) { // do outgoing fronthaul (south) if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) + if (ru->fh_north_asynch_in == NULL && ru->fh_south_out) ru->fh_south_out(ru, frame_tx, slot_tx, info->timestamp_tx); - if (ru->fh_north_out) ru->fh_north_out(ru); } else { @@ -1111,7 +1102,7 @@ void *ru_thread(void *param) // set default return value sprintf(threadname,"ru_thread %u",ru->idx); LOG_I(PHY,"Starting RU %d (%s,%s) on cpu %d\n",ru->idx,NB_functions[ru->function],NB_timing[ru->if_timing],sched_getcpu()); - memcpy((void *)&ru->config,(void *)&RC.gNB[0]->gNB_config,sizeof(ru->config)); + ru->config = RC.gNB[0]->gNB_config; nr_init_frame_parms(&ru->config, fp); nr_dump_frame_parms(fp); @@ -1136,7 +1127,6 @@ void *ru_thread(void *param) if (t != NULL) ru->fh_south_out = t; } else { - malloc_IF4p5_buffer(ru); } @@ -1156,9 +1146,10 @@ void *ru_thread(void *param) AssertFatal(ru->nr_start_if(ru,NULL) == 0, "Could not start the IF device\n"); if (ru->has_ctrl_prt > 0) { - if (ru->if_south == LOCAL_RF) ret = connect_rau(ru); - else ret = attach_rru(ru); - + if (ru->if_south == LOCAL_RF) + ret = connect_rau(ru); + else + ret = attach_rru(ru); AssertFatal(ret==0,"Cannot connect to remote radio\n"); } @@ -1204,8 +1195,8 @@ void *ru_thread(void *param) // This is a forever while loop, it loops over subframes which are scheduled by incoming samples from HW devices struct timespec slot_start; - clock_gettime(CLOCK_MONOTONIC, &slot_start); - + clock_gettime(CLOCK_MONOTONIC, &slot_start); + while (!oai_exit) { if (slot==(fp->slots_per_frame-1)) { slot=0; @@ -1222,8 +1213,8 @@ void *ru_thread(void *param) // synchronization on input FH interface, acquire signals/data and block LOG_D(PHY,"[RU_thread] read data: frame_rx = %d, tti_rx = %d\n", frame, slot); - if (ru->fh_south_in) ru->fh_south_in(ru,&frame,&slot); - else AssertFatal(1==0, "No fronthaul interface at south port"); + AssertFatal(ru->fh_south_in, "No fronthaul interface at south port"); + ru->fh_south_in(ru, &frame, &slot); if (initial_wait == 1 && proc->frame_rx < 300) { if (proc->frame_rx > 0 && ((proc->frame_rx % 100) == 0) && proc->tti_rx == 0) { @@ -1308,14 +1299,14 @@ void *ru_thread(void *param) } // end if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT) { notifiedFIFO_elt_t *resTx = newNotifiedFIFO_elt(sizeof(processingData_L1tx_t), 0, &gNB->L1_tx_out, NULL); - processingData_L1tx_t *syncMsgTx = NotifiedFifoData(resTx); - syncMsgTx->gNB = gNB; - syncMsgTx->frame = proc->frame_tx; - syncMsgTx->slot = proc->tti_tx; - syncMsgTx->frame_rx = proc->frame_rx; - syncMsgTx->slot_rx = proc->tti_rx; - syncMsgTx->timestamp_tx = proc->timestamp_tx; resTx->key = proc->tti_tx; + processingData_L1tx_t *syncMsgTx = NotifiedFifoData(resTx); + *syncMsgTx = (processingData_L1tx_t){.gNB = gNB, + .frame = proc->frame_tx, + .slot = proc->tti_tx, + .frame_rx = proc->frame_rx, + .slot_rx = proc->tti_rx, + .timestamp_tx = proc->timestamp_tx}; pushNotifiedFIFO(&gNB->L1_tx_out, resTx); } @@ -1325,15 +1316,16 @@ void *ru_thread(void *param) int start_streaming(RU_t *ru) { LOG_I(PHY,"Starting streaming on third-party RRU\n"); - return(ru->ifdevice.thirdparty_startstreaming(&ru->ifdevice)); + return ru->ifdevice.thirdparty_startstreaming(&ru->ifdevice); } int nr_start_if(struct RU_t_s *ru, struct PHY_VARS_gNB_s *gNB) { if (ru->if_south <= REMOTE_IF5) - for (int i=0;inb_rx;i++) ru->openair0_cfg.rxbase[i] = ru->common.rxdata[i]; + for (int i = 0; i < ru->nb_rx; i++) + ru->openair0_cfg.rxbase[i] = ru->common.rxdata[i]; ru->openair0_cfg.rxsize = ru->nr_frame_parms->samples_per_subframe*10; reset_meas(&ru->ifdevice.tx_fhaul); - return(ru->ifdevice.trx_start_func(&ru->ifdevice)); + return ru->ifdevice.trx_start_func(&ru->ifdevice); } int start_rf(RU_t *ru) { @@ -1349,29 +1341,16 @@ int stop_rf(RU_t *ru) { } int start_write_thread(RU_t *ru) { - return(ru->rfdevice.trx_write_init(&ru->rfdevice)); + return ru->rfdevice.trx_write_init(&ru->rfdevice); } void init_RU_proc(RU_t *ru) { - int i=0; - RU_proc_t *proc; - proc = &ru->proc; - memset((void *)proc,0,sizeof(RU_proc_t)); - proc->ru = ru; - proc->instance_cnt_emulateRF = -1; - proc->first_rx = 1; - proc->first_tx = 1; - proc->frame_offset = 0; - proc->num_slaves = 0; - proc->frame_tx_unwrap = 0; - - for (i=0; i<10; i++) proc->symbol_mask[i]=0; - - pthread_mutex_init( &proc->mutex_emulateRF,NULL); - pthread_cond_init( &proc->cond_emulateRF, NULL); + ru->proc = (RU_proc_t){.ru = ru, .instance_cnt_emulateRF = -1, .first_rx = 1, .first_tx = 1}; + pthread_mutex_init(&ru->proc.mutex_emulateRF, NULL); + pthread_cond_init(&ru->proc.cond_emulateRF, NULL); if(emulate_rf) - threadCreate( &proc->pthread_emulateRF, emulatedRF_thread, (void *)proc, "emulateRF", -1, OAI_PRIORITY_RT ); + threadCreate(&ru->proc.pthread_emulateRF, emulatedRF_thread, (void *)&ru->proc, "emulateRF", -1, OAI_PRIORITY_RT); LOG_I(PHY, "Initialized RU proc %d (%s,%s),\n", ru->idx, NB_functions[ru->function], NB_timing[ru->if_timing]); } @@ -1395,10 +1374,10 @@ void kill_NR_RU_proc(int inst) { * mutex_fep/cond_fep. Thus, we unlocked above for pthread_FH above and do * the same for FEP thread below again (using broadcast() to ensure both * threads get the signal). This one will also destroy the mutex and cond. */ - pthread_mutex_lock(&proc->mutex_fep[0]); + pthread_mutex_lock(proc->mutex_fep); proc->instance_cnt_fep[0] = 0; - pthread_cond_broadcast(&proc->cond_fep[0]); - pthread_mutex_unlock( &proc->mutex_fep[0] ); + pthread_cond_broadcast(proc->cond_fep); + pthread_mutex_unlock(proc->mutex_fep); pthread_join(proc->pthread_FH, NULL); // everything should be stopped now, we can safely stop the RF device @@ -1417,19 +1396,15 @@ void kill_NR_RU_proc(int inst) { int check_capabilities(RU_t *ru,RRU_capabilities_t *cap) { FH_fmt_options_t fmt = cap->FH_fmt; int i; - int found_band=0; LOG_I(PHY,"RRU %d, num_bands %d, looking for band %d\n",ru->idx,cap->num_bands,ru->nr_frame_parms->nr_band); for (i=0; inum_bands; i++) { LOG_I(PHY,"band %d on RRU %d\n",cap->band_list[i],ru->idx); - - if (ru->nr_frame_parms->nr_band == cap->band_list[i]) { - found_band=1; + if (ru->nr_frame_parms->nr_band == cap->band_list[i]) break; - } } - if (found_band == 0) { + if (i == cap->num_bands) { LOG_I(PHY,"Couldn't find target NR band %d on RRU %d\n",ru->nr_frame_parms->nr_band,ru->idx); return(-1); } @@ -1441,18 +1416,18 @@ int check_capabilities(RU_t *ru,RRU_capabilities_t *cap) { break; case REMOTE_IF5: - if (fmt == OAI_IF5_only || fmt == OAI_IF5_and_IF4p5) return(0); - + if (fmt == OAI_IF5_only || fmt == OAI_IF5_and_IF4p5) + return (0); break; case REMOTE_IF4p5: - if (fmt == OAI_IF4p5_only || fmt == OAI_IF5_and_IF4p5) return(0); - + if (fmt == OAI_IF4p5_only || fmt == OAI_IF5_and_IF4p5) + return (0); break; case REMOTE_MBP_IF5: - if (fmt == MBP_IF5) return(0); - + if (fmt == MBP_IF5) + return (0); break; default: @@ -1464,46 +1439,40 @@ int check_capabilities(RU_t *ru,RRU_capabilities_t *cap) { } const char rru_format_options[4][20] = {"OAI_IF5_only", "OAI_IF4p5_only", "OAI_IF5_and_IF4p5", "MBP_IF5"}; - const char rru_formats[3][20] = {"OAI_IF5", "MBP_IF5", "OAI_IF4p5"}; const char ru_if_formats[4][20] = {"LOCAL_RF", "REMOTE_OAI_IF5", "REMOTE_MBP_IF5", "REMOTE_OAI_IF4p5"}; void configure_ru(void *ruu, void *arg) { RU_t *ru = (RU_t *)ruu; - RRU_config_t *config = (RRU_config_t *)arg; - RRU_capabilities_t *capabilities = (RRU_capabilities_t *)arg; nfapi_nr_config_request_scf_t *cfg = &ru->config; int ret; LOG_I(PHY, "Received capabilities from RRU %d\n", ru->idx); - if (capabilities->FH_fmt < MAX_FH_FMTs) LOG_I(PHY, "RU FH options %s\n",rru_format_options[capabilities->FH_fmt]); + RRU_capabilities_t *capabilities = (RRU_capabilities_t *)arg; + if (capabilities->FH_fmt < MAX_FH_FMTs) + LOG_I(PHY, "RU FH options %s\n", rru_format_options[capabilities->FH_fmt]); ret = check_capabilities(ru,capabilities); AssertFatal(ret == 0, "Cannot configure RRU %d, check_capabilities returned %d\n", ru->idx, ret); // take antenna capabilities of RRU - ru->nb_tx = capabilities->nb_tx[0]; - ru->nb_rx = capabilities->nb_rx[0]; + ru->nb_tx = capabilities->nb_tx[0]; + ru->nb_rx = capabilities->nb_rx[0]; // Pass configuration to RRU LOG_I(PHY, "Using %s fronthaul (%d), band %d \n",ru_if_formats[ru->if_south],ru->if_south,ru->nr_frame_parms->nr_band); + // wait for configuration - config->FH_fmt = ru->if_south; - config->num_bands = 1; - config->band_list[0] = ru->nr_frame_parms->nr_band; - config->tx_freq[0] = ru->nr_frame_parms->dl_CarrierFreq; - config->rx_freq[0] = ru->nr_frame_parms->ul_CarrierFreq; - //config->tdd_config[0] = ru->nr_frame_parms->tdd_config; - //config->tdd_config_S[0] = ru->nr_frame_parms->tdd_config_S; - config->att_tx[0] = ru->att_tx; - config->att_rx[0] = ru->att_rx; - config->N_RB_DL[0] = cfg->carrier_config.dl_grid_size[cfg->ssb_config.scs_common.value].value; - config->N_RB_UL[0] = cfg->carrier_config.dl_grid_size[cfg->ssb_config.scs_common.value].value; - config->threequarter_fs[0] = ru->nr_frame_parms->threequarter_fs; - /* if (ru->if_south==REMOTE_IF4p5) { - config->prach_FreqOffset[0] = ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_FreqOffset; - config->prach_ConfigIndex[0] = ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_ConfigIndex; - LOG_I(PHY,"REMOTE_IF4p5: prach_FrequOffset %d, prach_ConfigIndex %d\n", - config->prach_FreqOffset[0],config->prach_ConfigIndex[0]);*/ + RRU_config_t *config = (RRU_config_t *)arg; + *config = (RRU_config_t){.FH_fmt = ru->if_south, + .num_bands = 1, + .band_list[0] = ru->nr_frame_parms->nr_band, + .tx_freq[0] = ru->nr_frame_parms->dl_CarrierFreq, + .rx_freq[0] = ru->nr_frame_parms->ul_CarrierFreq, + .att_tx[0] = ru->att_tx, + .att_rx[0] = ru->att_rx, + .N_RB_DL[0] = cfg->carrier_config.dl_grid_size[cfg->ssb_config.scs_common.value].value, + .N_RB_UL[0] = cfg->carrier_config.dl_grid_size[cfg->ssb_config.scs_common.value].value, + .threequarter_fs[0] = ru->nr_frame_parms->threequarter_fs}; nr_init_frame_parms(&ru->config, ru->nr_frame_parms); nr_phy_init_RU(ru); } @@ -1513,37 +1482,31 @@ void configure_rru(void *ruu, void *arg) RRU_config_t *config = (RRU_config_t *)arg; RU_t *ru = (RU_t *)ruu; nfapi_nr_config_request_scf_t *cfg = &ru->config; - ru->nr_frame_parms->nr_band = config->band_list[0]; - ru->nr_frame_parms->dl_CarrierFreq = config->tx_freq[0]; - ru->nr_frame_parms->ul_CarrierFreq = config->rx_freq[0]; + NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; - if (ru->nr_frame_parms->dl_CarrierFreq == ru->nr_frame_parms->ul_CarrierFreq) { - cfg->cell_config.frame_duplex_type.value = TDD; - //ru->nr_frame_parms->tdd_config = config->tdd_config[0]; - //ru->nr_frame_parms->tdd_config_S = config->tdd_config_S[0]; + fp->nr_band = config->band_list[0]; + fp->dl_CarrierFreq = config->tx_freq[0]; + fp->ul_CarrierFreq = config->rx_freq[0]; + + if (fp->dl_CarrierFreq == fp->ul_CarrierFreq) { + cfg->cell_config.frame_duplex_type.value = TDD; } else - cfg->cell_config.frame_duplex_type.value = FDD; + cfg->cell_config.frame_duplex_type.value = FDD; - ru->att_tx = config->att_tx[0]; - ru->att_rx = config->att_rx[0]; + ru->att_tx = config->att_tx[0]; + ru->att_rx = config->att_rx[0]; int mu = cfg->ssb_config.scs_common.value; - cfg->carrier_config.dl_grid_size[mu].value = config->N_RB_DL[0]; - cfg->carrier_config.dl_grid_size[mu].value = config->N_RB_UL[0]; - ru->nr_frame_parms->threequarter_fs = config->threequarter_fs[0]; + cfg->carrier_config.dl_grid_size[mu].value = config->N_RB_DL[0]; + cfg->carrier_config.dl_grid_size[mu].value = config->N_RB_UL[0]; + fp->threequarter_fs = config->threequarter_fs[0]; - //ru->nr_frame_parms->pdsch_config_common.referenceSignalPower = ru->max_pdschReferenceSignalPower-config->att_tx[0]; if (ru->function==NGFI_RRU_IF4p5) { - ru->nr_frame_parms->att_rx = ru->att_rx; - ru->nr_frame_parms->att_tx = ru->att_tx; - /* - LOG_I(PHY,"Setting ru->function to NGFI_RRU_IF4p5, prach_FrequOffset %d, prach_ConfigIndex %d, att (%d,%d)\n", - config->prach_FreqOffset[0],config->prach_ConfigIndex[0],ru->att_tx,ru->att_rx); - ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_FreqOffset = config->prach_FreqOffset[0]; - ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_ConfigIndex = config->prach_ConfigIndex[0]; */ + fp->att_rx = ru->att_rx; + fp->att_tx = ru->att_tx; } fill_rf_config(ru,ru->rf_config_file); - nr_init_frame_parms(&ru->config, ru->nr_frame_parms); + nr_init_frame_parms(&ru->config, fp); nr_phy_init_RU(ru); } @@ -1600,17 +1563,6 @@ void set_function_spec_param(RU_t *ru) ru->stop_rf = stop_rf; ru->start_write_thread = start_write_thread; // starting RF TX in different thread } - - /* - fill_rf_config(ru,rf_config_file); - init_frame_parms(&ru->frame_parms,1); - nr_phy_init_RU(ru); - - ret = openair0_device_load(&ru->rfdevice,&ru->openair0_cfg); - if (setup_RU_buffers(ru)!=0) { - exit(-1); - } - */ break; case REMOTE_IF5: // the remote unit is IF5 RRU @@ -1659,12 +1611,6 @@ void set_function_spec_param(RU_t *ru) void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file) { - int ru_id; - RU_t *ru; - PHY_VARS_gNB *gNB_RC; - PHY_VARS_gNB *gNB0= (PHY_VARS_gNB *)NULL; - NR_DL_FRAME_PARMS *fp = (NR_DL_FRAME_PARMS *)NULL; - int i; // create status mask RC.ru_mask = 0; pthread_mutex_init(&RC.ru_mutex,NULL); @@ -1674,9 +1620,9 @@ void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file) LOG_I(PHY,"number of L1 instances %d, number of RU %d, number of CPU cores %d\n",RC.nb_nr_L1_inst,RC.nb_RU,get_nprocs()); LOG_D(PHY,"Process RUs RC.nb_RU:%d\n",RC.nb_RU); - for (ru_id=0; ru_idrf_config_file = rf_config_file; ru->idx = ru_id; ru->ts_offset = 0; @@ -1693,23 +1639,18 @@ void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file) } } - gNB_RC = RC.gNB[0]; - gNB0 = ru->gNB_list[0]; - fp = ru->nr_frame_parms; + PHY_VARS_gNB *gNB_RC = RC.gNB[0]; + PHY_VARS_gNB *gNB0 = ru->gNB_list[0]; LOG_D(PHY, "RU FUnction:%d ru->if_south:%d\n", ru->function, ru->if_south); if (gNB0) { - if ((ru->function != NGFI_RRU_IF5) && (ru->function != NGFI_RRU_IF4p5)) - AssertFatal(gNB0, "gNB0 is null!\n"); - - if (gNB0 && gNB_RC) { + if (gNB_RC) { LOG_D(PHY, "Copying frame parms from gNB in RC to gNB %d in ru %d and frame_parms in ru\n", gNB0->Mod_id, ru->idx); - memcpy((void *)fp, &gNB_RC->frame_parms, sizeof(NR_DL_FRAME_PARMS)); - memcpy((void *)&gNB0->frame_parms, (void *)&gNB_RC->frame_parms, sizeof(NR_DL_FRAME_PARMS)); + *ru->nr_frame_parms = gNB_RC->frame_parms; + gNB0->frame_parms = gNB_RC->frame_parms; // attach all RU to all gNBs in its list/ LOG_D(PHY,"ru->num_gNB:%d gNB0->num_RU:%d\n", ru->num_gNB, gNB0->num_RU); - - for (i=0; inum_gNB; i++) { + for (int i = 0; i < ru->num_gNB; i++) { gNB0 = ru->gNB_list[i]; gNB0->RU_list[gNB0->num_RU++] = ru; } @@ -1723,22 +1664,21 @@ void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file) LOG_E(PHY, "Number of threads for gNB should be more than 1. Allocated only %d\n", threadCnt); char pool[80]; int s_offset = sprintf(pool,"%d",ru->tpcores[0]); - for (int icpu=1; icputpcores[icpu]); + for (int icpu = 1; icpu < threadCnt; icpu++) { + s_offset += sprintf(pool + s_offset, ",%d", ru->tpcores[icpu]); } LOG_I(PHY, "RU thread-pool core string %s (size %d)\n", pool, threadCnt); - ru->threadPool = (tpool_t*)malloc(sizeof(tpool_t)); + ru->threadPool = malloc(sizeof(tpool_t)); initTpool(pool, ru->threadPool, cpumeas(CPUMEAS_GETSTATE)); // FEP RX result FIFO - ru->respfeprx = (notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); + ru->respfeprx = malloc(sizeof(notifiedFIFO_t)); initNotifiedFIFO(ru->respfeprx); // FEP TX result FIFO - ru->respfeptx = (notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); + ru->respfeptx = malloc(sizeof(notifiedFIFO_t)); initNotifiedFIFO(ru->respfeptx); } } // for ru_id - // sleep(1); LOG_D(HW,"[nr-softmodem.c] RU threads created\n"); } @@ -1755,7 +1695,6 @@ void stop_RU(int nb_ru) { } } - /* --------------------------------------------------------*/ /* from here function to use configuration module */ static void NRRCconfig_RU(configmodule_interface_t *cfg) @@ -1764,199 +1703,199 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg) paramlist_def_t RUParamList = {CONFIG_STRING_RU_LIST, NULL, 0}; config_getlist(cfg, &RUParamList, RUParams, sizeofArray(RUParams), NULL); - if (RUParamList.numelt > 0) { - RC.ru = (RU_t **)malloc(RC.nb_RU*sizeof(RU_t *)); - RC.ru_mask = (1 << RC.nb_RU) - 1; + if (RUParamList.numelt <= 0) + return; - for (int j = 0; j < RC.nb_RU; j++) { - RC.ru[j] = calloc(1, sizeof(*RC.ru[j])); - RC.ru[j]->idx = j; - RC.ru[j]->nr_frame_parms = calloc(1, sizeof(*RC.ru[j]->nr_frame_parms)); - RC.ru[j]->frame_parms = calloc(1, sizeof(*RC.ru[j]->frame_parms)); - RC.ru[j]->if_timing = synch_to_ext_device; + RC.ru = (RU_t **)malloc(RC.nb_RU * sizeof(RU_t *)); + RC.ru_mask = (1 << RC.nb_RU) - 1; - if (RC.nb_nr_L1_inst > 0) - RC.ru[j]->num_gNB = RUParamList.paramarray[j][RU_ENB_LIST_IDX].numelt; - else - RC.ru[j]->num_gNB = 0; + for (int j = 0; j < RC.nb_RU; j++) { + RU_t *ru = RC.ru[j] = calloc(1, sizeof(*RC.ru[j])); + ru->idx = j; + ru->nr_frame_parms = calloc(1, sizeof(*ru->nr_frame_parms)); + ru->frame_parms = calloc(1, sizeof(*ru->frame_parms)); + ru->if_timing = synch_to_ext_device; + paramdef_t *param = RUParamList.paramarray[j]; + if (RC.nb_nr_L1_inst > 0) + ru->num_gNB = param[RU_ENB_LIST_IDX].numelt; + else + ru->num_gNB = 0; - for (int i = 0; i < RC.ru[j]->num_gNB; i++) - RC.ru[j]->gNB_list[i] = RC.gNB[RUParamList.paramarray[j][RU_ENB_LIST_IDX].iptr[i]]; + for (int i = 0; i < ru->num_gNB; i++) + ru->gNB_list[i] = RC.gNB[param[RU_ENB_LIST_IDX].iptr[i]]; - if (config_isparamset(RUParamList.paramarray[j], RU_SDR_ADDRS)) { - RC.ru[j]->openair0_cfg.sdr_addrs = strdup(*(RUParamList.paramarray[j][RU_SDR_ADDRS].strptr)); - } + if (config_isparamset(param, RU_SDR_ADDRS)) { + ru->openair0_cfg.sdr_addrs = strdup(*param[RU_SDR_ADDRS].strptr); + } - if (config_isparamset(RUParamList.paramarray[j], RU_GPIO_CONTROL)) { - if (strcmp(*RUParamList.paramarray[j][RU_GPIO_CONTROL].strptr, "generic") == 0) { - RC.ru[j]->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_GENERIC; - LOG_I(PHY, "RU GPIO control set as 'generic'\n"); - } else if (strcmp(*RUParamList.paramarray[j][RU_GPIO_CONTROL].strptr, "interdigital") == 0) { - RC.ru[j]->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_INTERDIGITAL; - LOG_I(PHY, "RU GPIO control set as 'interdigital'\n"); - } else { - AssertFatal(false, - "bad GPIO controller in configuration file: '%s'\n", - *(RUParamList.paramarray[j][RU_GPIO_CONTROL].strptr)); - } - } else - RC.ru[j]->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_NONE; - - if (config_isparamset(RUParamList.paramarray[j], RU_TX_SUBDEV)) { - RC.ru[j]->openair0_cfg.tx_subdev = strdup(*(RUParamList.paramarray[j][RU_TX_SUBDEV].strptr)); - LOG_I(PHY, "RU USRP tx subdev == %s\n", RC.ru[j]->openair0_cfg.tx_subdev); - } - - if (config_isparamset(RUParamList.paramarray[j], RU_RX_SUBDEV)) { - RC.ru[j]->openair0_cfg.rx_subdev = strdup(*(RUParamList.paramarray[j][RU_RX_SUBDEV].strptr)); - LOG_I(PHY, "RU USRP rx subdev == %s\n", RC.ru[j]->openair0_cfg.rx_subdev); - } - - if (config_isparamset(RUParamList.paramarray[j], RU_SDR_CLK_SRC)) { - if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "internal") == 0) { - RC.ru[j]->openair0_cfg.clock_source = internal; - LOG_I(PHY, "RU clock source set as internal\n"); - } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "external") == 0) { - RC.ru[j]->openair0_cfg.clock_source = external; - LOG_I(PHY, "RU clock source set as external\n"); - } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "gpsdo") == 0) { - RC.ru[j]->openair0_cfg.clock_source = gpsdo; - LOG_I(PHY, "RU clock source set as gpsdo\n"); - } else { - LOG_E(PHY, "Erroneous RU clock source in the provided configuration file: '%s'\n", *(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr)); - } + if (config_isparamset(param, RU_GPIO_CONTROL)) { + char *str = *param[RU_GPIO_CONTROL].strptr; + if (strcmp(str, "generic") == 0) { + ru->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_GENERIC; + LOG_I(PHY, "RU GPIO control set as 'generic'\n"); + } else if (strcmp(str, "interdigital") == 0) { + ru->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_INTERDIGITAL; + LOG_I(PHY, "RU GPIO control set as 'interdigital'\n"); } else { - LOG_D(PHY, "Setting clock source to internal\n"); - RC.ru[j]->openair0_cfg.clock_source = internal; + AssertFatal(false, "bad GPIO controller in configuration file: '%s'\n", str); } + } else + ru->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_NONE; - if (config_isparamset(RUParamList.paramarray[j], RU_SDR_TME_SRC)) { - if (strcmp(*(RUParamList.paramarray[j][RU_SDR_TME_SRC].strptr), "internal") == 0) { - RC.ru[j]->openair0_cfg.time_source = internal; - LOG_I(PHY, "RU time source set as internal\n"); - } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_TME_SRC].strptr), "external") == 0) { - RC.ru[j]->openair0_cfg.time_source = external; - LOG_I(PHY, "RU time source set as external\n"); - } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_TME_SRC].strptr), "gpsdo") == 0) { - RC.ru[j]->openair0_cfg.time_source = gpsdo; - LOG_I(PHY, "RU time source set as gpsdo\n"); - } else { - LOG_E(PHY, "Erroneous RU time source in the provided configuration file: '%s'\n", *(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr)); - } + if (config_isparamset(param, RU_TX_SUBDEV)) { + ru->openair0_cfg.tx_subdev = strdup(*param[RU_TX_SUBDEV].strptr); + LOG_I(PHY, "RU USRP tx subdev == %s\n", ru->openair0_cfg.tx_subdev); + } + + if (config_isparamset(param, RU_RX_SUBDEV)) { + ru->openair0_cfg.rx_subdev = strdup(*param[RU_RX_SUBDEV].strptr); + LOG_I(PHY, "RU USRP rx subdev == %s\n", ru->openair0_cfg.rx_subdev); + } + + if (config_isparamset(param, RU_SDR_CLK_SRC)) { + char *str = *param[RU_SDR_CLK_SRC].strptr; + if (strcmp(str, "internal") == 0) { + ru->openair0_cfg.clock_source = internal; + LOG_I(PHY, "RU clock source set as internal\n"); + } else if (strcmp(str, "external") == 0) { + ru->openair0_cfg.clock_source = external; + LOG_I(PHY, "RU clock source set as external\n"); + } else if (strcmp(str, "gpsdo") == 0) { + ru->openair0_cfg.clock_source = gpsdo; + LOG_I(PHY, "RU clock source set as gpsdo\n"); } else { - LOG_D(PHY, "Setting time source to internal\n"); - RC.ru[j]->openair0_cfg.time_source = internal; + LOG_E(PHY, "Erroneous RU clock source in the provided configuration file: '%s'\n", str); + } + } else { + LOG_D(PHY, "Setting clock source to internal\n"); + ru->openair0_cfg.clock_source = internal; + } + + if (config_isparamset(param, RU_SDR_TME_SRC)) { + char *str = *param[RU_SDR_TME_SRC].strptr; + if (strcmp(str, "internal") == 0) { + ru->openair0_cfg.time_source = internal; + LOG_I(PHY, "RU time source set as internal\n"); + } else if (strcmp(str, "external") == 0) { + ru->openair0_cfg.time_source = external; + LOG_I(PHY, "RU time source set as external\n"); + } else if (strcmp(str, "gpsdo") == 0) { + ru->openair0_cfg.time_source = gpsdo; + LOG_I(PHY, "RU time source set as gpsdo\n"); + } else { + LOG_E(PHY, "Erroneous RU time source in the provided configuration file: '%s'\n", str); + } + } else { + LOG_D(PHY, "Setting time source to internal\n"); + ru->openair0_cfg.time_source = internal; + } + + ru->openair0_cfg.tune_offset = get_softmodem_params()->tune_offset; + + if (strcmp(*param[RU_LOCAL_RF_IDX].strptr, "yes") == 0) { + if (!config_isparamset(param, RU_LOCAL_IF_NAME_IDX)) { + ru->if_south = LOCAL_RF; + ru->function = gNodeB_3GPP; + LOG_D(PHY, "Setting function for RU %d to gNodeB_3GPP\n", j); + } else { + ru->eth_params.local_if_name = strdup(*param[RU_LOCAL_IF_NAME_IDX].strptr); + ru->eth_params.my_addr = strdup(*param[RU_LOCAL_ADDRESS_IDX].strptr); + ru->eth_params.remote_addr = strdup(*param[RU_REMOTE_ADDRESS_IDX].strptr); + ru->eth_params.my_portc = *param[RU_LOCAL_PORTC_IDX].uptr; + ru->eth_params.remote_portc = *param[RU_REMOTE_PORTC_IDX].uptr; + ru->eth_params.my_portd = *param[RU_LOCAL_PORTD_IDX].uptr; + ru->eth_params.remote_portd = *param[RU_REMOTE_PORTD_IDX].uptr; + char *str = *param[RU_TRANSPORT_PREFERENCE_IDX].strptr; + if (strcmp(str, "udp") == 0) { + ru->if_south = LOCAL_RF; + ru->function = NGFI_RRU_IF5; + ru->eth_params.transp_preference = ETH_UDP_MODE; + LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (udp)\n", j); + } else if (strcmp(str, "raw") == 0) { + ru->if_south = LOCAL_RF; + ru->function = NGFI_RRU_IF5; + ru->eth_params.transp_preference = ETH_RAW_MODE; + LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (raw)\n", j); + } else if (strcmp(str, "udp_if4p5") == 0) { + ru->if_south = LOCAL_RF; + ru->function = NGFI_RRU_IF4p5; + ru->eth_params.transp_preference = ETH_UDP_IF4p5_MODE; + LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (udp)\n", j); + } else if (strcmp(str, "raw_if4p5") == 0) { + ru->if_south = LOCAL_RF; + ru->function = NGFI_RRU_IF4p5; + ru->eth_params.transp_preference = ETH_RAW_IF4p5_MODE; + LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (raw)\n", j); + } } - RC.ru[j]->openair0_cfg.tune_offset = get_softmodem_params()->tune_offset; + ru->max_pdschReferenceSignalPower = *param[RU_MAX_RS_EPRE_IDX].uptr; + ru->max_rxgain = *param[RU_MAX_RXGAIN_IDX].uptr; + ru->sf_extension = *param[RU_SF_EXTENSION_IDX].uptr; + } // strcmp(local_rf, "yes") == 0 + else { + char *str = *param[RU_TRANSPORT_PREFERENCE_IDX].strptr; + LOG_D(PHY, "RU %d: Transport %s\n", j, str); + ru->eth_params.local_if_name = strdup(*param[RU_LOCAL_IF_NAME_IDX].strptr); + ru->eth_params.my_addr = strdup(*param[RU_LOCAL_ADDRESS_IDX].strptr); + ru->eth_params.remote_addr = strdup(*param[RU_REMOTE_ADDRESS_IDX].strptr); + ru->eth_params.my_portc = *param[RU_LOCAL_PORTC_IDX].uptr; + ru->eth_params.remote_portc = *param[RU_REMOTE_PORTC_IDX].uptr; + ru->eth_params.my_portd = *param[RU_LOCAL_PORTD_IDX].uptr; + ru->eth_params.remote_portd = *param[RU_REMOTE_PORTD_IDX].uptr; - if (strcmp(*(RUParamList.paramarray[j][RU_LOCAL_RF_IDX].strptr), "yes") == 0) { - if (!(config_isparamset(RUParamList.paramarray[j],RU_LOCAL_IF_NAME_IDX))) { - RC.ru[j]->if_south = LOCAL_RF; - RC.ru[j]->function = gNodeB_3GPP; - LOG_D(PHY, "Setting function for RU %d to gNodeB_3GPP\n", j); - } else { - RC.ru[j]->eth_params.local_if_name = strdup(*(RUParamList.paramarray[j][RU_LOCAL_IF_NAME_IDX].strptr)); - RC.ru[j]->eth_params.my_addr = strdup(*(RUParamList.paramarray[j][RU_LOCAL_ADDRESS_IDX].strptr)); - RC.ru[j]->eth_params.remote_addr = strdup(*(RUParamList.paramarray[j][RU_REMOTE_ADDRESS_IDX].strptr)); - RC.ru[j]->eth_params.my_portc = *(RUParamList.paramarray[j][RU_LOCAL_PORTC_IDX].uptr); - RC.ru[j]->eth_params.remote_portc = *(RUParamList.paramarray[j][RU_REMOTE_PORTC_IDX].uptr); - RC.ru[j]->eth_params.my_portd = *(RUParamList.paramarray[j][RU_LOCAL_PORTD_IDX].uptr); - RC.ru[j]->eth_params.remote_portd = *(RUParamList.paramarray[j][RU_REMOTE_PORTD_IDX].uptr); - - if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp") == 0) { - RC.ru[j]->if_south = LOCAL_RF; - RC.ru[j]->function = NGFI_RRU_IF5; - RC.ru[j]->eth_params.transp_preference = ETH_UDP_MODE; - LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (udp)\n", j); - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw") == 0) { - RC.ru[j]->if_south = LOCAL_RF; - RC.ru[j]->function = NGFI_RRU_IF5; - RC.ru[j]->eth_params.transp_preference = ETH_RAW_MODE; - LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (raw)\n", j); - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp_if4p5") == 0) { - RC.ru[j]->if_south = LOCAL_RF; - RC.ru[j]->function = NGFI_RRU_IF4p5; - RC.ru[j]->eth_params.transp_preference = ETH_UDP_IF4p5_MODE; - LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (udp)\n", j); - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw_if4p5") == 0) { - RC.ru[j]->if_south = LOCAL_RF; - RC.ru[j]->function = NGFI_RRU_IF4p5; - RC.ru[j]->eth_params.transp_preference = ETH_RAW_IF4p5_MODE; - LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (raw)\n", j); - } - } - - RC.ru[j]->max_pdschReferenceSignalPower = *(RUParamList.paramarray[j][RU_MAX_RS_EPRE_IDX].uptr);; - RC.ru[j]->max_rxgain = *(RUParamList.paramarray[j][RU_MAX_RXGAIN_IDX].uptr); - RC.ru[j]->sf_extension = *(RUParamList.paramarray[j][RU_SF_EXTENSION_IDX].uptr); - } //strcmp(local_rf, "yes") == 0 - else { - LOG_D(PHY, "RU %d: Transport %s\n", j, *(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr)); - RC.ru[j]->eth_params.local_if_name = strdup(*(RUParamList.paramarray[j][RU_LOCAL_IF_NAME_IDX].strptr)); - RC.ru[j]->eth_params.my_addr = strdup(*(RUParamList.paramarray[j][RU_LOCAL_ADDRESS_IDX].strptr)); - RC.ru[j]->eth_params.remote_addr = strdup(*(RUParamList.paramarray[j][RU_REMOTE_ADDRESS_IDX].strptr)); - RC.ru[j]->eth_params.my_portc = *(RUParamList.paramarray[j][RU_LOCAL_PORTC_IDX].uptr); - RC.ru[j]->eth_params.remote_portc = *(RUParamList.paramarray[j][RU_REMOTE_PORTC_IDX].uptr); - RC.ru[j]->eth_params.my_portd = *(RUParamList.paramarray[j][RU_LOCAL_PORTD_IDX].uptr); - RC.ru[j]->eth_params.remote_portd = *(RUParamList.paramarray[j][RU_REMOTE_PORTD_IDX].uptr); - - if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp") == 0) { - RC.ru[j]->if_south = REMOTE_IF5; - RC.ru[j]->function = NGFI_RAU_IF5; - RC.ru[j]->eth_params.transp_preference = ETH_UDP_MODE; - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp_ecpri_if5") == 0) { - RC.ru[j]->if_south = REMOTE_IF5; - RC.ru[j]->function = NGFI_RAU_IF5; - RC.ru[j]->eth_params.transp_preference = ETH_UDP_IF5_ECPRI_MODE; - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw") == 0) { - RC.ru[j]->if_south = REMOTE_IF5; - RC.ru[j]->function = NGFI_RAU_IF5; - RC.ru[j]->eth_params.transp_preference = ETH_RAW_MODE; - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp_if4p5") == 0) { - RC.ru[j]->if_south = REMOTE_IF4p5; - RC.ru[j]->function = NGFI_RAU_IF4p5; - RC.ru[j]->eth_params.transp_preference = ETH_UDP_IF4p5_MODE; - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw_if4p5") == 0) { - RC.ru[j]->if_south = REMOTE_IF4p5; - RC.ru[j]->function = NGFI_RAU_IF4p5; - RC.ru[j]->eth_params.transp_preference = ETH_RAW_IF4p5_MODE; - } - } /* strcmp(local_rf, "yes") != 0 */ - - RC.ru[j]->nb_tx = *(RUParamList.paramarray[j][RU_NB_TX_IDX].uptr); - RC.ru[j]->nb_rx = *(RUParamList.paramarray[j][RU_NB_RX_IDX].uptr); - RC.ru[j]->att_tx = *(RUParamList.paramarray[j][RU_ATT_TX_IDX].uptr); - RC.ru[j]->att_rx = *(RUParamList.paramarray[j][RU_ATT_RX_IDX].uptr); - RC.ru[j]->if_frequency = *(RUParamList.paramarray[j][RU_IF_FREQUENCY].u64ptr); - RC.ru[j]->if_freq_offset = *(RUParamList.paramarray[j][RU_IF_FREQ_OFFSET].iptr); - RC.ru[j]->sl_ahead = *(RUParamList.paramarray[j][RU_SL_AHEAD].iptr); - RC.ru[j]->num_bands = RUParamList.paramarray[j][RU_BAND_LIST_IDX].numelt; - for (int i = 0; i < RC.ru[j]->num_bands; i++) - RC.ru[j]->band[i] = RUParamList.paramarray[j][RU_BAND_LIST_IDX].iptr[i]; - RC.ru[j]->openair0_cfg.nr_flag = *(RUParamList.paramarray[j][RU_NR_FLAG].iptr); - RC.ru[j]->openair0_cfg.nr_band = RC.ru[j]->band[0]; - RC.ru[j]->openair0_cfg.nr_scs_for_raster = *(RUParamList.paramarray[j][RU_NR_SCS_FOR_RASTER].iptr); - LOG_D(PHY, - "[RU %d] Setting nr_flag %d, nr_band %d, nr_scs_for_raster %d\n", - j, - RC.ru[j]->openair0_cfg.nr_flag, - RC.ru[j]->openair0_cfg.nr_band, - RC.ru[j]->openair0_cfg.nr_scs_for_raster); - RC.ru[j]->openair0_cfg.rxfh_cores[0] = *(RUParamList.paramarray[j][RU_RXFH_CORE_ID].iptr); - RC.ru[j]->openair0_cfg.txfh_cores[0] = *(RUParamList.paramarray[j][RU_TXFH_CORE_ID].iptr); - RC.ru[j]->num_tpcores = *(RUParamList.paramarray[j][RU_NUM_TP_CORES].iptr); - RC.ru[j]->half_slot_parallelization = *(RUParamList.paramarray[j][RU_HALF_SLOT_PARALLELIZATION].iptr); - RC.ru[j]->ru_thread_core = *(RUParamList.paramarray[j][RU_RU_THREAD_CORE].iptr); - LOG_D(PHY, "[RU %d] Setting half-slot parallelization to %d\n", j, RC.ru[j]->half_slot_parallelization); - AssertFatal(RC.ru[j]->num_tpcores <= RUParamList.paramarray[j][RU_TP_CORES].numelt, "Number of TP cores should be <=16\n"); - for (int i = 0; i < RC.ru[j]->num_tpcores; i++) - RC.ru[j]->tpcores[i] = RUParamList.paramarray[j][RU_TP_CORES].iptr[i]; - }// j=0..num_rus - } else { - RC.nb_RU = 0; - } // setting != NULL + if (strcmp(str, "udp") == 0) { + ru->if_south = REMOTE_IF5; + ru->function = NGFI_RAU_IF5; + ru->eth_params.transp_preference = ETH_UDP_MODE; + } else if (strcmp(str, "udp_ecpri_if5") == 0) { + ru->if_south = REMOTE_IF5; + ru->function = NGFI_RAU_IF5; + ru->eth_params.transp_preference = ETH_UDP_IF5_ECPRI_MODE; + } else if (strcmp(str, "raw") == 0) { + ru->if_south = REMOTE_IF5; + ru->function = NGFI_RAU_IF5; + ru->eth_params.transp_preference = ETH_RAW_MODE; + } else if (strcmp(str, "udp_if4p5") == 0) { + ru->if_south = REMOTE_IF4p5; + ru->function = NGFI_RAU_IF4p5; + ru->eth_params.transp_preference = ETH_UDP_IF4p5_MODE; + } else if (strcmp(str, "raw_if4p5") == 0) { + ru->if_south = REMOTE_IF4p5; + ru->function = NGFI_RAU_IF4p5; + ru->eth_params.transp_preference = ETH_RAW_IF4p5_MODE; + } + } /* strcmp(local_rf, "yes") != 0 */ + ru->nb_tx = *param[RU_NB_TX_IDX].uptr; + ru->nb_rx = *param[RU_NB_RX_IDX].uptr; + ru->att_tx = *param[RU_ATT_TX_IDX].uptr; + ru->att_rx = *param[RU_ATT_RX_IDX].uptr; + ru->if_frequency = *param[RU_IF_FREQUENCY].u64ptr; + ru->if_freq_offset = *param[RU_IF_FREQ_OFFSET].iptr; + ru->sl_ahead = *param[RU_SL_AHEAD].iptr; + ru->num_bands = param[RU_BAND_LIST_IDX].numelt; + for (int i = 0; i < ru->num_bands; i++) + ru->band[i] = param[RU_BAND_LIST_IDX].iptr[i]; + ru->openair0_cfg.nr_flag = *param[RU_NR_FLAG].iptr; + ru->openair0_cfg.nr_band = ru->band[0]; + ru->openair0_cfg.nr_scs_for_raster = *param[RU_NR_SCS_FOR_RASTER].iptr; + LOG_D(PHY, + "[RU %d] Setting nr_flag %d, nr_band %d, nr_scs_for_raster %d\n", + j, + ru->openair0_cfg.nr_flag, + ru->openair0_cfg.nr_band, + ru->openair0_cfg.nr_scs_for_raster); + ru->openair0_cfg.rxfh_cores[0] = *param[RU_RXFH_CORE_ID].iptr; + ru->openair0_cfg.txfh_cores[0] = *param[RU_TXFH_CORE_ID].iptr; + ru->num_tpcores = *param[RU_NUM_TP_CORES].iptr; + ru->half_slot_parallelization = *param[RU_HALF_SLOT_PARALLELIZATION].iptr; + ru->ru_thread_core = *param[RU_RU_THREAD_CORE].iptr; + LOG_D(PHY, "[RU %d] Setting half-slot parallelization to %d\n", j, ru->half_slot_parallelization); + AssertFatal(ru->num_tpcores <= param[RU_TP_CORES].numelt, "Number of TP cores should be <=16\n"); + for (int i = 0; i < ru->num_tpcores; i++) + ru->tpcores[i] = param[RU_TP_CORES].iptr[i]; + } // j=0..num_rus return; } From 096ea7bd87f430b8bbe9d7a1e48e5ce076f44ac6 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Tue, 15 Apr 2025 09:41:53 +0200 Subject: [PATCH 13/49] Fixes for vrtsim - Allow client to exit cleanly if server is down - Ensure timing_thread is joined only once avoiding exit_function loop - Free noise_device - Save previously sent samples for channel modelling --- .../utils/shm_iq_channel/shm_td_iq_channel.c | 3 -- radio/vrtsim/noise_device.c | 4 +- radio/vrtsim/vrtsim.c | 53 +++++++++++++++---- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/common/utils/shm_iq_channel/shm_td_iq_channel.c b/common/utils/shm_iq_channel/shm_td_iq_channel.c index 89e09ddb1f..3920fe027f 100644 --- a/common/utils/shm_iq_channel/shm_td_iq_channel.c +++ b/common/utils/shm_iq_channel/shm_td_iq_channel.c @@ -228,9 +228,6 @@ IQChannelErrorType shm_td_iq_channel_rx(ShmTDIQChannel *channel, void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, size_t num_samples) { ShmTDIQChannelData *data = channel->data; - if (channel->type != IQ_CHANNEL_TYPE_SERVER) { - return; - } if (data->is_connected == false) { return; } diff --git a/radio/vrtsim/noise_device.c b/radio/vrtsim/noise_device.c index 2e15944d4a..acb3a2b5da 100644 --- a/radio/vrtsim/noise_device.c +++ b/radio/vrtsim/noise_device.c @@ -18,6 +18,7 @@ * For more information about the OpenAirInterface (OAI) Software Alliance: * contact@openairinterface.org */ +#include "assertions.h" #include #include #include "noise_device.h" @@ -74,7 +75,8 @@ void init_noise_device(float noise_power) void free_noise_device(void) { state.running = false; - pthread_join(state.noise_device_thread, NULL); + int ret = pthread_join(state.noise_device_thread, NULL); + AssertFatal(ret == 0, "pthread_join failed: %d, errno %d (%s)\n", ret, errno, strerror(errno)); } void get_noise_vector(float *noise_vector, int length) diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index f0e0151aa6..0e602b3686 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -19,6 +19,7 @@ * contact@openairinterface.org */ +#include "PHY/TOOLS/tools_defs.h" #include #include #include @@ -50,7 +51,8 @@ // Simulator role typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; -#define NUM_CHANMOD_THREADS 1 +#define MAX_NUM_ANTENNAS_TX 4 +#define MAX_CHANNEL_LENGTH (1 << 20) #define ROLE_CLIENT_STRING "client" #define ROLE_SERVER_STRING "server" @@ -112,6 +114,9 @@ typedef struct { Actor_t *channel_modelling_actors; } vrtsim_state_t; +// Sample history for channel impulse response +static c16_t saved_samples[MAX_NUM_ANTENNAS_TX][MAX_CHANNEL_LENGTH] __attribute__((aligned(32))) = {0}; + static void histogram_add(histogram_t *histogram, double diff) { histogram->num_samples++; @@ -149,6 +154,16 @@ static void load_channel_model(vrtsim_state_t *vrtsim_state) vrtsim_state->tx_bw); char *model_name = vrtsim_state->role == ROLE_CLIENT ? "client_tx_channel_model" : "server_tx_channel_model"; vrtsim_state->channel_desc = find_channel_desc_fromname(model_name); + AssertFatal(vrtsim_state->channel_desc != NULL, + "Could not find model name %s. Make sure it is present in the config file", + model_name); + LOG_I(HW, + "Channel model %s parameters: path_loss_dB=%.2f, nb_tx=%d, nb_rx=%d, channel_length=%d\n", + model_name, + vrtsim_state->channel_desc->path_loss_dB, + vrtsim_state->channel_desc->nb_tx, + vrtsim_state->channel_desc->nb_rx, + vrtsim_state->channel_desc->channel_length); random_channel(vrtsim_state->channel_desc, 0); AssertFatal(vrtsim_state->channel_desc != NULL, "Could not find channel model %s\n", model_name); } @@ -376,7 +391,7 @@ static int vrtsim_write_internal(vrtsim_state_t *vrtsim_state, typedef struct { vrtsim_state_t *vrtsim_state; openair0_timestamp timestamp; - c16_t *samples[4]; + c16_t *samples[MAX_NUM_ANTENNAS_TX]; int nsamps; int nbAnt; int flags; @@ -410,17 +425,16 @@ static void perform_channel_modelling(void *arg) } for (int aatx = 0; aatx < nb_tx_ant; aatx++) { + c16_t *previous_samples = saved_samples[aatx]; for (int i = 0; i < nsamps; i++) { cf_t *impulse_response = channel_impulse_response[aatx]; for (int l = 0; l < channel_desc->channel_length; l++) { int idx = i - l; - // TODO: Use AVX512 for this - // TODO: What are the previously sent samples (for impulse response) - if (idx >= 0) { - c16_t tx_input = input_samples[aatx][idx]; - samples[i].r += tx_input.r * impulse_response[l].r - tx_input.i * impulse_response[l].i; - samples[i].i += tx_input.i * impulse_response[l].r + tx_input.r * impulse_response[l].i; - } + // TODO: Use AVX2 for this + c16_t tx_input = idx >= 0 ? input_samples[aatx][idx] + : previous_samples[(channel_modelling_args->timestamp + i + idx) % MAX_CHANNEL_LENGTH]; + samples[i].r += tx_input.r * impulse_response[l].r - tx_input.i * impulse_response[l].i; + samples[i].i += tx_input.i * impulse_response[l].r + tx_input.r * impulse_response[l].i; } } } @@ -462,6 +476,7 @@ static int vrtsim_write_with_chanmod(vrtsim_state_t *vrtsim_state, int nbAnt, int flags) { + AssertFatal(nbAnt < MAX_NUM_ANTENNAS_TX, "Number of antennas %d exceeds maximum %d\n", nbAnt, MAX_NUM_ANTENNAS_TX); for (int aarx = 0; aarx < vrtsim_state->peer_info.num_rx_antennas; aarx++) { notifiedFIFO_elt_t *task = newNotifiedFIFO_elt(sizeof(channel_modelling_args_t), 0, NULL, perform_channel_modelling); channel_modelling_args_t *args = (channel_modelling_args_t *)NotifiedFifoData(task); @@ -476,6 +491,23 @@ static int vrtsim_write_with_chanmod(vrtsim_state_t *vrtsim_state, } pushNotifiedFIFO(&vrtsim_state->channel_modelling_actors[aarx].fifo, task); } + int start_index = timestamp % MAX_CHANNEL_LENGTH; + int end_index = min(start_index + nsamps, MAX_CHANNEL_LENGTH); + int cp_nsamps = end_index - start_index; + for (int aatx = 0; aatx < nbAnt; aatx++) { + c16_t *samples = (c16_t *)samplesVoid[aatx]; + memcpy(&saved_samples[aatx][start_index], &samples[0], sizeof(c16_t) * cp_nsamps); + } + + if (end_index < start_index + nsamps) { + // wrap around condition, write at beginning of buffer + cp_nsamps = nsamps - cp_nsamps; // remaining samples + start_index = 0; + for (int aatx = 0; aatx < nbAnt; aatx++) { + c16_t *samples = (c16_t *)samplesVoid[aatx]; + memcpy(&saved_samples[aatx][start_index], &samples[0], sizeof(c16_t) * cp_nsamps); + } + } return nsamps; } @@ -506,7 +538,7 @@ static int vrtsim_read(openair0_device *device, openair0_timestamp *ptimestamp, static void vrtsim_end(openair0_device *device) { vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; - if (vrtsim_state->role == ROLE_SERVER) { + if (vrtsim_state->role == ROLE_SERVER && vrtsim_state->run_timing_thread) { vrtsim_state->run_timing_thread = false; int ret = pthread_join(vrtsim_state->timing_thread, NULL); AssertFatal(ret == 0, "pthread_join() failed: errno: %d, %s\n", errno, strerror(errno)); @@ -526,6 +558,7 @@ static void vrtsim_end(openair0_device *device) tx_timing->tx_samples_total += tx_timing[i].tx_samples_total; } tx_timing->average_tx_budget /= vrtsim_state->peer_info.num_rx_antennas; + free_noise_device(); } // produce 1 second of extra samples so threads can finish shm_td_iq_channel_produce_samples(vrtsim_state->channel, vrtsim_state->sample_rate); From 92dc641e98bc12f6e2a2e8326c9c930d27ae4bb6 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 19 Aug 2025 09:17:33 +0200 Subject: [PATCH 14/49] common channel level function --- .../PHY/nr_phy_common/inc/nr_phy_common.h | 7 ++++++ .../PHY/nr_phy_common/src/nr_phy_common.c | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h index d52b6d9c43..febf434e5a 100644 --- a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h +++ b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h @@ -54,6 +54,13 @@ void freq2time(uint16_t ofdm_symbol_size, int16_t *freq_signal, int16_t *time_si void nr_est_delay(int ofdm_symbol_size, const c16_t *ls_est, c16_t *ch_estimates_time, delay_t *delay); unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int total_nb_rb, int nb_rb); void nr_fo_compensation(double fo_Hz, int samples_per_ms, int sample_offset, const c16_t *rxdata_in, c16_t *rxdata_out, int size); +void nr_channel_level(const int symbol, + const int size_est, + const c16_t ch_estimates_ext[][size_est], + const int nb_rx, + const int Nl, + int32_t avg[nb_rx * Nl], + const uint32_t len); void nr_generate_csi_rs(const NR_DL_FRAME_PARMS *frame_parms, const csi_mapping_parms_t *phy_csi_parms, const int16_t amp, diff --git a/openair1/PHY/nr_phy_common/src/nr_phy_common.c b/openair1/PHY/nr_phy_common/src/nr_phy_common.c index e86652bd49..4e8951d124 100644 --- a/openair1/PHY/nr_phy_common/src/nr_phy_common.c +++ b/openair1/PHY/nr_phy_common/src/nr_phy_common.c @@ -420,6 +420,28 @@ unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int total_nb_rb, in return (0); } +// compute average channel_level on each antenna +void nr_channel_level(const int symbol, + const int size_est, + const c16_t ch_estimates_ext[][size_est], + const int nb_rx, + const int Nl, + int32_t avg[nb_rx * Nl], + const uint32_t len) +{ + int16_t x = factor2(len); + int16_t y = len >> x; + for (int aarx = 0; aarx < nb_rx; aarx++) { + for (int l = 0; l < Nl; l++) { + simde__m128i *ch128 = (simde__m128i *)&ch_estimates_ext[l * nb_rx + aarx][symbol * len]; + //compute average level + avg[l * nb_rx + aarx] = simde_mm_average(ch128, len, x, y); + LOG_D(PHY, "Channel level: %d\n", avg[l * nb_rx + aarx]); + } + } +} + + void nr_fo_compensation(double fo_Hz, int samples_per_ms, int sample_offset, const c16_t *rxdata_in, c16_t *rxdata_out, int size) { const double phase_inc = -fo_Hz / (samples_per_ms * 1000); From a95a32c2f63ea70df75fb5212b13db409ff625e6 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 19 Aug 2025 09:32:19 +0200 Subject: [PATCH 15/49] use common channel level for PBCH --- openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c | 38 ++++--------------- openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c | 8 +++- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 3 -- 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c index 298de612ee..8065da24b2 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c @@ -38,6 +38,7 @@ #include #include #include "openair1/PHY/NR_REFSIG/nr_refsig_common.h" +#include "PHY/nr_phy_common/inc/nr_phy_common.h" #include "instrumentation.h" //#define DEBUG_PBCH //#define DEBUG_PBCH_ENCODING @@ -201,31 +202,6 @@ static uint16_t nr_pbch_extract(uint32_t rxdataF_sz, return(0); } -//__m128i avg128; - -//compute average channel_level on each (TX,RX) antenna pair -int nr_pbch_channel_level(struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL], - const NR_DL_FRAME_PARMS *frame_parms, - int nb_re) -{ - int32_t avg2 = 0; - - for (int aarx=0; aarxnb_antennas_rx; aarx++) { - - simde__m128i *dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[aarx]; - - //compute average level - int32_t avg1 = simde_mm_average(dl_ch128, nb_re, 0, nb_re); - - if (avg1>avg2) - avg2 = avg1; - - //LOG_I(PHY,"Channel level : %d, %d\n",avg1, avg2); - } - - return(avg2); -} - void nr_pbch_channel_compensation(struct complex16 rxdataF_ext[][PBCH_MAX_RE_PER_SYMBOL], struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL], int nb_re, @@ -346,7 +322,6 @@ int nr_rx_pbch(PHY_VARS_NR_UE *ue, const struct complex16 rxdataF[][rxdataFSize]) { TracyCZone(ctx, true); - int max_h=0; int symbol; uint8_t Lmax=frame_parms->Lmax; int M = NR_POLAR_PBCH_E; @@ -390,11 +365,14 @@ int nr_rx_pbch(PHY_VARS_NR_UE *ue, LOG_I(PHY,"[PHY] PBCH starting channel_level\n"); #endif + int max_h = 0; if (symbol == 1) { - max_h = nr_pbch_channel_level(dl_ch_estimates_ext, - frame_parms, - nb_re); - log2_maxh = 3+(log2_approx(max_h)/2); + int avg[frame_parms->nb_antennas_rx]; + nr_channel_level(0, PBCH_MAX_RE_PER_SYMBOL, dl_ch_estimates_ext, frame_parms->nb_antennas_rx, 1, avg, nb_re); + max_h = avg[0]; + for (int i = 1; i < frame_parms->nb_antennas_rx; i++) + max_h = cmax(avg[i], max_h); + log2_maxh = 3 + (log2_approx(max_h) / 2); } #ifdef DEBUG_PBCH diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c b/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c index 7406855cc4..1010a6e5e8 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c @@ -25,6 +25,7 @@ #include "common/utils/LOG/log.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h" #include "PHY/TOOLS/phy_scope_interface.h" +#include "PHY/nr_phy_common/inc/nr_phy_common.h" // #define DEBUG_PSBCH @@ -158,8 +159,11 @@ int nr_rx_psbch(PHY_VARS_NR_UE *ue, int max_h = 0; if (symbol == 0) { - max_h = nr_pbch_channel_level(dl_ch_estimates_ext, frame_parms, nb_re); - // log2_maxh = 3+(log2_approx(max_h)/2); + int avg[frame_parms->nb_antennas_rx]; + nr_channel_level(0, PBCH_MAX_RE_PER_SYMBOL, dl_ch_estimates_ext, frame_parms->nb_antennas_rx, 1, avg, nb_re); + max_h = avg[0]; + for (int i = 1; i < frame_parms->nb_antennas_rx; i++) + max_h = cmax(avg[i], max_h); log2_maxh = 5 + (log2_approx(max_h) / 2); // LLR32 crc error. LLR 16 CRC works } #ifdef DEBUG_PSBCH diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index a4098394ac..395081b4a8 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -346,9 +346,6 @@ void nr_tx_psbch(PHY_VARS_NR_UE *UE, uint32_t frame_tx, uint32_t slot_tx, sl_nr_ nr_initial_sync_t sl_nr_slss_search(PHY_VARS_NR_UE *UE, UE_nr_rxtx_proc_t *proc, int num_frames); // Reuse already existing PBCH functions -int nr_pbch_channel_level(struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL], - const NR_DL_FRAME_PARMS *frame_parms, - int nb_re); void nr_pbch_channel_compensation(struct complex16 rxdataF_ext[][PBCH_MAX_RE_PER_SYMBOL], struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL], int nb_re, From 65d0fb152e4a9b89b1101ec449bbbde1d55abad2 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 19 Aug 2025 10:00:00 +0200 Subject: [PATCH 16/49] use common channel level for DCI reception --- openair1/PHY/NR_UE_TRANSPORT/dci_nr.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index b4e2c413cf..43b27e3932 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -36,6 +36,7 @@ #include "PHY/CODING/nrPolar_tools/nr_polar_dci_defs.h" #include "PHY/phy_extern.h" #include "PHY/CODING/coding_extern.h" +#include "PHY/nr_phy_common/inc/nr_phy_common.h" #include "PHY/sse_intrin.h" #include "common/utils/nr/nr_common.h" #include @@ -197,20 +198,6 @@ static void nr_pdcch_llr(uint32_t sz, c16_t *rxF, c16_t *llr) } } -//compute average channel_level on each (TX,RX) antenna pair -static int32_t nr_pdcch_channel_level(int nb_re, int arraySz, c16_t dl_ch_estimates_ext[][arraySz], int nb_antennas_rx) -{ - int32_t avgs = 0; - for (int aarx = 0; aarx < nb_antennas_rx; aarx++) { - simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx]; - // compute average level - // if nb_re is not %4, some samples are missed inside simde_mm_average, so let's discard it - int32_t tmp = simde_mm_average(dl_ch128, nb_re & ~3, 0, nb_re & ~3); - avgs = cmax(avgs, tmp); - } - return avgs; -} - // This function will extract the mapped DM-RS PDCCH REs as per 38.211 Section 7.4.1.3.2 (Mapping to physical resources) static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, uint32_t coreset_nbr_rb, @@ -372,9 +359,13 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, rel15->coreset.frequency_domain_resource, rel15->BWPStart); - LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_level(dl_ch_estimates_ext -> dl_ch_estimates_ext)\n"); + LOG_D(NR_PHY_DCI, "in channel level function (dl_ch_estimates_ext -> dl_ch_estimates_ext)\n"); // compute channel level based on ofdm symbol 0 - int32_t avgs = nr_pdcch_channel_level(n_rb, arraySz, pdcch_dl_ch_estimates_ext, antRx); + int avg[antRx]; + nr_channel_level(0, arraySz, pdcch_dl_ch_estimates_ext, antRx, 1, avg, n_rb); + int avgs = avg[0]; + for (int i = 1; i < antRx; i++) + avgs = cmax(avgs, avg[i]); uint8_t log2_maxh = (log2_approx(avgs) / 2) + 5; //+antRx; #ifdef UE_DEBUG_TRACE From 8d904dbf233f2ba29576a3a8c4ce2104b4dce471 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 19 Aug 2025 10:17:51 +0200 Subject: [PATCH 17/49] use common channel level for PUSCH --- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 46 +++++-------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index e8d8efeb28..bd4ee92284 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -7,6 +7,7 @@ #include "PHY/NR_REFSIG/ptrs_nr.h" #include "PHY/NR_ESTIMATION/nr_ul_estimation.h" #include "PHY/defs_nr_common.h" +#include "PHY/nr_phy_common/inc/nr_phy_common.h" #include "common/utils/nr/nr_common.h" #include #include "PHY/sse_intrin.h" @@ -210,31 +211,6 @@ static int get_nb_re_pusch (NR_DL_FRAME_PARMS *frame_parms, nfapi_nr_pusch_pdu_t } else return(rel15_ul->rb_size * NR_NB_SC_PER_RB); } -// compute average channel_level on each (TX,RX) antenna pair -static void nr_ulsch_channel_level(int size_est, - int ul_ch_estimates_ext[][size_est], - NR_DL_FRAME_PARMS *frame_parms, - int32_t *avg, - uint8_t symbol, - uint32_t len, - uint8_t nrOfLayers) -{ - int16_t x = factor2(len); - int16_t y = (len)>>x; - - for (int aatx = 0; aatx < nrOfLayers; aatx++) { - for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { - - simde__m128i *ul_ch128 = (simde__m128i *)&ul_ch_estimates_ext[aatx * frame_parms->nb_antennas_rx + aarx][symbol * len]; - - //compute average level - avg[aatx * frame_parms->nb_antennas_rx + aarx] = simde_mm_average(ul_ch128, len, x, y); - //LOG_D(PHY, "Channel level: %d\n", avg[aatx * frame_parms->nb_antennas_rx + aarx]); - } - } - -} - static void nr_ulsch_channel_compensation(uint32_t buffer_length, int nb_rx_ant, c16_t rxFext[][buffer_length], @@ -1394,8 +1370,6 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, rel15_ul, frame_parms); - int avgs = 0; - int avg[frame_parms->nb_antennas_rx*rel15_ul->nrOfLayers]; uint8_t shift_ch_ext = rel15_ul->nrOfLayers > 1 ? log2_approx(max_ch >> 11) : 0; //---------------------------------------------------------- @@ -1410,15 +1384,17 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, rel15_ul->nrOfLayers, rel15_ul->rb_size, shift_ch_ext); - - nr_ulsch_channel_level(size_est, - ul_ch_estimates_ext, - frame_parms, - avg, - meas_symbol, // index of the start symbol - nb_re_pusch, // number of the re in pusch - rel15_ul->nrOfLayers); + int avg[frame_parms->nb_antennas_rx*rel15_ul->nrOfLayers]; + nr_channel_level(meas_symbol, + size_est, + (c16_t (*)[size_est])ul_ch_estimates_ext, + frame_parms->nb_antennas_rx, + rel15_ul->nrOfLayers, + avg, + nb_re_pusch); + + int avgs = 0; for (int nl = 0; nl < rel15_ul->nrOfLayers; nl++) for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) avgs = cmax(avgs, avg[nl * frame_parms->nb_antennas_rx + aarx]); From 6af8f5619a0513290cbab3a31263532ced0db6ca Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 19 Aug 2025 11:30:00 +0200 Subject: [PATCH 18/49] use common channel level for PDSCH --- .../NR_UE_TRANSPORT/nr_dlsch_demodulation.c | 65 +++---------------- 1 file changed, 8 insertions(+), 57 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index d0457b3bb9..39f91d3b4e 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -193,20 +193,6 @@ static void nr_dlsch_channel_compensation(uint32_t rx_size_symbol, unsigned char output_shift, PHY_NR_MEASUREMENTS *measurements); -/** \brief This function computes the average channel level over all allocated RBs and antennas (TX/RX) in order to compute output shift for compensated signal - @param dl_ch_estimates_ext Channel estimates in allocated RBs - @param frame_parms Pointer to frame descriptor - @param avg Pointer to average signal strength - @param pilots_flag Flag to indicate pilots in symbol - @param nb_rb Number of allocated RBs -*/ -static void nr_dlsch_channel_level(uint32_t rx_size_symbol, - int32_t dl_ch_estimates_ext[][rx_size_symbol], - uint8_t n_rx, - uint8_t n_tx, - int32_t avg[MAX_ANT][MAX_ANT], - uint32_t len); - void nr_dlsch_scale_channel(uint32_t rx_size_symbol, int32_t dl_ch_estimates_ext[][rx_size_symbol], NR_DL_FRAME_PARMS *frame_parms, @@ -538,25 +524,25 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, //---------------------------------------------------------- start_meas_nr_ue_phy(ue, DLSCH_CHANNEL_LEVEL_STATS); if (first_symbol_flag) { - int32_t avg[MAX_ANT][MAX_ANT] = {}; + int32_t avg[nl * n_rx]; if (nb_re_pdsch) - nr_dlsch_channel_level(rx_size_symbol, dl_ch_estimates_ext, frame_parms->nb_antennas_rx, nl, avg, nb_re_pdsch); + nr_channel_level(0, rx_size_symbol, (c16_t (*)[rx_size_symbol])dl_ch_estimates_ext, n_rx, nl, avg, nb_re_pdsch); else LOG_E(NR_PHY, "Average channel level is 0: nb_rb_pdsch = %d, nb_re_pdsch = %d\n", nb_rb_pdsch, nb_re_pdsch); int avgs = 0; int32_t median[MAX_ANT][MAX_ANT]; - for (int aatx = 0; aatx < nl; aatx++) + for (int l = 0; l < nl; l++) for (int aarx = 0; aarx < n_rx; aarx++) { - avgs = cmax(avgs, avg[aatx][aarx]); - LOG_D(PHY, "nb_rb %d avg_%d_%d Power per SC is %d\n", nb_rb_pdsch, aarx, aatx, avg[aatx][aarx]); + avgs = cmax(avgs, avg[l * n_rx + aarx]); + LOG_D(PHY, "nb_rb %d avg_%d_%d Power per SC is %d\n", nb_rb_pdsch, aarx, l, avg[l * n_rx + aarx]); LOG_D(PHY, "avgs Power per SC is %d\n", avgs); - median[aatx][aarx] = avg[aatx][aarx]; + median[l][aarx] = avg[l * n_rx + aarx]; } if (nl > 1) { nr_dlsch_channel_level_median(rx_size_symbol, dl_ch_estimates_ext, median, nl, n_rx, nb_re_pdsch); - for (int aatx = 0; aatx < nl; aatx++) { + for (int l = 0; l < nl; l++) { for (int aarx = 0; aarx < n_rx; aarx++) { - avgs = cmax(avgs, median[aatx][aarx]); + avgs = cmax(avgs, median[l][aarx]); } } } @@ -1032,41 +1018,6 @@ void nr_dlsch_scale_channel(uint32_t rx_size_symbol, } } - -/** @brief This function computes the average channel level over all allocated RBs - * and antennas (TX/RX) in order to compute output shift for compensated signal - @param rx_size_symbol - @param dl_ch_estimates_ext Channel estimates in allocated RBs - @param n_rx number of antennas in RX - @param n_tx number of antennas in TX - @param avg Pointer to average signal strength - @param len Number of allocated PDSCH REs -*/ -void nr_dlsch_channel_level(uint32_t rx_size_symbol, - int32_t dl_ch_estimates_ext[][rx_size_symbol], - uint8_t n_rx, - uint8_t n_tx, - int32_t avg[MAX_ANT][MAX_ANT], - uint32_t len) -{ - int16_t x = factor2(len); - int16_t y = (len) >> x; - - uint32_t nb_rb_0 = len / NR_NB_SC_PER_RB + ((len % NR_NB_SC_PER_RB) ? 1 : 0); - LOG_D(NR_PHY, "nb_rb_0 %d len %d = %d * 2^(%d)\n", nb_rb_0, len, y, x); - - for (int aatx = 0; aatx < n_tx; aatx++) { - for (int aarx = 0; aarx < n_rx; aarx++) { - - simde__m128i *dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[(aatx * n_rx) + aarx]; - - //compute average level - avg[aatx][aarx] = simde_mm_average(dl_ch128, nb_rb_0 * 12, x, y); - //LOG_D(PHY, "Channel level: %d\n", avg[aatx][aarx]); - } - } -} - static void nr_dlsch_channel_level_median(uint32_t rx_size_symbol, int32_t dl_ch_estimates_ext[][rx_size_symbol], int32_t median[MAX_ANT][MAX_ANT], From b4796b4ddae0137da6d6535f07b288c50b86022f Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 15 Oct 2024 08:22:35 +0200 Subject: [PATCH 19/49] remove unused deinterleaving function from dlsch ue --- .../NR_UE_TRANSPORT/nr_dlsch_demodulation.c | 47 ------------------- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 7 --- 2 files changed, 54 deletions(-) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index 39f91d3b4e..1da2ef8d45 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -781,53 +781,6 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, return (0); } -void nr_dlsch_deinterleaving(uint8_t symbol, - uint8_t start_symbol, - uint16_t L, - uint16_t *llr, - uint16_t *llr_deint, - uint16_t nb_rb_pdsch) -{ - - uint32_t bundle_idx, N_bundle, R, C, r,c; - int32_t m,k; - uint8_t nb_re; - - R=2; - N_bundle = nb_rb_pdsch/L; - C=N_bundle/R; - - uint32_t bundle_deint[N_bundle]; - memset(bundle_deint, 0 , sizeof(bundle_deint)); - - printf("N_bundle %u L %d nb_rb_pdsch %d\n",N_bundle, L,nb_rb_pdsch); - - if (symbol==start_symbol) - nb_re = 6; - else - nb_re = 12; - - - AssertFatal(llr!=NULL,"nr_dlsch_deinterleaving: FATAL llr is Null\n"); - - - for (c =0; c< C; c++){ - for (r=0; r Date: Tue, 29 Jul 2025 16:54:43 +0200 Subject: [PATCH 20/49] Change tracy version to latest release --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 898b387237..5d756434c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,7 +285,9 @@ add_boolean_option(UE_TIMING_TRACE False "Activate UE timing trace" ON) add_boolean_option(TRACY_ENABLE OFF "Enable tracy instrumentation" ON) if (TRACY_ENABLE) - CPMAddPackage("gh:wolfpld/tracy#v0.11.1") + # the tracy version here should match the tracy server version + # below is latest release as of this commit + CPMAddPackage("gh:wolfpld/tracy#0.12.2") endif() set (OCP_ITTI ${OPENAIR_DIR}/common/utils/ocp_itti) From c4f4f8d9198630872a3da9890538d9851b70f3ee Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 29 Jul 2025 16:56:17 +0200 Subject: [PATCH 21/49] Remove threequarter_fs, log_level; make functions static 2 functions with same name as static to make it clearer they are separate. remove unused copy of log_level global var. remove threequarter sampling variable from the radio board general configuration. The decision of sampling rate is made above. A better behavior can be done later, like ask to the radio board what sampling rate it can do,then use it to decide the appropiate sampling rate from the gNB configuration. In UE case, it is another issue, as the sampling rate would change after we decode SIB1, but also it is not developped yet. --- executables/lte-uesoftmodem.c | 3 ++- executables/nr-uesoftmodem.c | 3 +-- openair1/SIMULATION/NR_PHY/ulsim.c | 6 ++++-- radio/COMMON/common_lib.h | 4 ---- radio/ETHERNET/ethernet_lib.c | 1 - radio/fhi_72/oran-config.c | 2 -- 6 files changed, 7 insertions(+), 12 deletions(-) diff --git a/executables/lte-uesoftmodem.c b/executables/lte-uesoftmodem.c index ea48edd801..7de3014ee9 100644 --- a/executables/lte-uesoftmodem.c +++ b/executables/lte-uesoftmodem.c @@ -384,7 +384,8 @@ void set_default_frame_parms(LTE_DL_FRAME_PARMS *frame_parms[MAX_NUM_CCs]) { } } -void init_openair0(LTE_DL_FRAME_PARMS *frame_parms,int rxgain) { +static void init_openair0(LTE_DL_FRAME_PARMS *frame_parms, int rxgain) +{ int card; int i; diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 6ed407a507..8b331e4976 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -249,7 +249,7 @@ void set_options(int CC_id, PHY_VARS_NR_UE *UE){ } -void init_openair0() +static void init_openair0() { int card; int freq_off = 0; @@ -261,7 +261,6 @@ void init_openair0() for (card=0; cardthreequarter_fs; openair0_cfg[card].sample_rate = frame_parms->samples_per_subframe * 1e3; openair0_cfg[card].samples_per_frame = frame_parms->samples_per_frame; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index b2b2dc38bb..b5bd02aefd 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -237,6 +237,8 @@ int main(int argc, char *argv[]) int max_ldpc_iterations = 5; int num_antennas_per_thread = 1; uint32_t log_format = 0; + int threequarter_fs = 0; + if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == 0) { exit_fun("[NR_ULSIM] Error, configuration module init failed\n"); } @@ -351,7 +353,7 @@ int main(int argc, char *argv[]) case 'k': printf("Setting threequarter_fs_flag\n"); - openair0_cfg[0].threequarter_fs= 1; + threequarter_fs = 1; break; case 'm': @@ -584,7 +586,7 @@ int main(int argc, char *argv[]) uint32_t samples; get_samplerate_and_bw(mu, N_RB_DL, - openair0_cfg[0].threequarter_fs, + threequarter_fs, &sampling_frequency, &samples, &tx_bandwidth, diff --git a/radio/COMMON/common_lib.h b/radio/COMMON/common_lib.h index 86d09b00fb..f928d554a8 100644 --- a/radio/COMMON/common_lib.h +++ b/radio/COMMON/common_lib.h @@ -204,8 +204,6 @@ typedef struct split7_config { typedef struct openair0_config { //! Module ID for this configuration int Mod_id; - //! device log level - int log_level; //! duplexing mode duplex_mode_t duplex_mode; //! number of downlink resource blocks @@ -292,8 +290,6 @@ typedef struct openair0_config { double rx_sample_rate; //! the sample rate for transmit. double tx_sample_rate; - //! check for threequarter sampling rate - int8_t threequarter_fs; //! Flag to indicate this configuration is for NR int nr_flag; //! NR band number diff --git a/radio/ETHERNET/ethernet_lib.c b/radio/ETHERNET/ethernet_lib.c index eb23ef28cc..2a4d021afc 100644 --- a/radio/ETHERNET/ethernet_lib.c +++ b/radio/ETHERNET/ethernet_lib.c @@ -617,7 +617,6 @@ void dump_dev(openair0_device *device) eth_state_t *eth = (eth_state_t*)device->priv; printf("Ethernet device interface %i configuration:\n" ,device->openair0_cfg->Mod_id); - printf(" Log level is %i :\n" ,device->openair0_cfg->log_level); printf(" RB number: %i, sample rate: %lf \n" , device->openair0_cfg->num_rb_dl, device->openair0_cfg->sample_rate); printf(" RAU configured for %i tx/%i rx channels)\n", diff --git a/radio/fhi_72/oran-config.c b/radio/fhi_72/oran-config.c index 4eff24aacf..89c1838fa3 100644 --- a/radio/fhi_72/oran-config.c +++ b/radio/fhi_72/oran-config.c @@ -884,8 +884,6 @@ static bool set_fh_config(void *mplane_api, int ru_idx, int num_rus, enum xran_c for (int i = 1; i < oai0->rx_num_channels; ++i) DevAssert(oai0->rx_freq[0] == oai0->rx_freq[i]); DevAssert(oai0->nr_band > 0); - AssertFatal(oai0->threequarter_fs == 0, "cannot use three-quarter sampling with O-RAN 7.2 split\n"); - paramdef_t FHconfigs[] = ORAN_FH_DESC; paramlist_def_t FH_ConfigList = {CONFIG_STRING_ORAN_FH}; char aprefix[MAX_OPTNAME_SIZE] = {0}; From 7cc35cf7e07c44d79511b2239e1836d5988433ed Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 29 Jul 2025 17:22:23 +0200 Subject: [PATCH 22/49] remove dead function --- openair1/PHY/INIT/nr_init.c | 1 - openair1/PHY/nr_phy_common/inc/nr_phy_common.h | 7 ------- openair1/PHY/nr_phy_common/src/nr_phy_common.c | 16 ---------------- 3 files changed, 24 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index dcdc4ee433..60bc63dac4 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -131,7 +131,6 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) crcTableInit(); init_byte2m128i(); - init_byte2bit16(); init_pucch2_luts(); nr_init_fde(); // Init array for frequency equalization of transform precoding of PUSCH diff --git a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h index d52b6d9c43..134222d675 100644 --- a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h +++ b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h @@ -26,15 +26,8 @@ #include "PHY/NR_REFSIG/nr_refsig_common.h" #include "PHY/MODULATION/nr_modulation.h" -extern simde__m128i byte2bit16_lut[256]; -void init_byte2bit16(void); void init_byte2m128i(void); -static inline simde__m128i byte2bit16(uint8_t b) -{ - return byte2bit16_lut[b]; -} - typedef struct { int size; int ports; diff --git a/openair1/PHY/nr_phy_common/src/nr_phy_common.c b/openair1/PHY/nr_phy_common/src/nr_phy_common.c index e86652bd49..233645fb2d 100644 --- a/openair1/PHY/nr_phy_common/src/nr_phy_common.c +++ b/openair1/PHY/nr_phy_common/src/nr_phy_common.c @@ -26,22 +26,6 @@ #endif #define PEAK_DETECT_THRESHOLD 15 - -simde__m128i byte2bit16_lut[256]; -void init_byte2bit16(void) -{ - for (int s = 0; s < 256; s++) { - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], s & 1, 0); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 1) & 1, 1); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 2) & 1, 2); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 3) & 1, 3); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 4) & 1, 4); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 5) & 1, 5); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 6) & 1, 6); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 7) & 1, 7); - } -} - simde__m128i byte2m128i[256]; void init_byte2m128i(void) { From 2dae7b0db4787b4328d62bb2c36466a2823d0e85 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 29 Jul 2025 17:36:33 +0200 Subject: [PATCH 23/49] Limit visibilty of roundKeys --- openair3/NAS/UE/API/USIM/aka_functions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair3/NAS/UE/API/USIM/aka_functions.c b/openair3/NAS/UE/API/USIM/aka_functions.c index d2e3fc36c5..1f461c3bbf 100644 --- a/openair3/NAS/UE/API/USIM/aka_functions.c +++ b/openair3/NAS/UE/API/USIM/aka_functions.c @@ -303,7 +303,7 @@ void ComputeOPc(const u8 op[16], u8 op_c_pP[16]) return; } /* end of function ComputeOPc */ /*-------------------- Rijndael round subkeys ---------------------*/ -u8 roundKeys[11][4][4]; +static u8 roundKeys[11][4][4]; /*--------------------- Rijndael S box table ----------------------*/ const u8 S[256] = { 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, From 4a3ebb30330f549c7b18e55d98651ee4324861ce Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 29 Jul 2025 17:58:00 +0200 Subject: [PATCH 24/49] remove dead code and replace unmanaged asn1_xer_print global variable by the appropriate existing debug flag --- CMakeLists.txt | 1 - doc/Doxyfile | 1 - executables/nr-cuup.c | 1 - openair2/E1AP/e1ap_common.h | 2 -- openair2/GNB_APP/gnb_paramdef.h | 3 -- openair2/M2AP/m2ap_common.c | 4 +-- openair2/M2AP/m2ap_common.h | 2 -- openair2/M2AP/m2ap_decoder.c | 2 +- openair2/M2AP/m2ap_encoder.c | 2 +- openair2/X2AP/x2ap_common.c | 6 ++-- openair2/X2AP/x2ap_common.h | 2 -- openair2/X2AP/x2ap_eNB_decoder.c | 2 +- openair2/X2AP/x2ap_eNB_encoder.c | 2 +- openair3/M3AP/m3ap_common.c | 4 +-- openair3/M3AP/m3ap_common.h | 2 -- openair3/M3AP/m3ap_decoder.c | 2 +- openair3/M3AP/m3ap_encoder.c | 2 +- openair3/NGAP/ngap_common.c | 2 -- openair3/NGAP/ngap_common.h | 3 -- openair3/NGAP/ngap_gNB_encoder.c | 2 +- openair3/NGAP/ngap_gNB_nas_procedures.c | 4 +-- openair3/S1AP/s1ap_common.c | 41 ------------------------- openair3/S1AP/s1ap_common.h | 8 ----- openair3/S1AP/s1ap_eNB_encoder.c | 6 ++-- openair3/TEST/test_s1ap.c | 4 --- 25 files changed, 19 insertions(+), 91 deletions(-) delete mode 100644 openair3/S1AP/s1ap_common.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d756434c4..2544c86d7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -339,7 +339,6 @@ target_link_libraries(nr_rrc PUBLIC asn1_nr_rrc asn1_lte_rrc) set(S1AP_DIR ${OPENAIR3_DIR}/S1AP) include_directories ("${S1AP_DIR}") add_library(s1ap - ${S1AP_DIR}/s1ap_common.c ${S1AP_DIR}/s1ap_eNB.c ${S1AP_DIR}/s1ap_eNB_context_management_procedures.c ${S1AP_DIR}/s1ap_eNB_decoder.c diff --git a/doc/Doxyfile b/doc/Doxyfile index f128e3eb88..e67078a043 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -1021,7 +1021,6 @@ INPUT = \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_ue_context.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_management_procedures.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_context_management_procedures.c \ -@CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_common.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_decoder.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_management_procedures.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_handlers.h \ diff --git a/executables/nr-cuup.c b/executables/nr-cuup.c index 6467683cac..403f78ddb3 100644 --- a/executables/nr-cuup.c +++ b/executables/nr-cuup.c @@ -37,7 +37,6 @@ RAN_CONTEXT_t RC; THREAD_STRUCT thread_struct; uint64_t downlink_frequency[MAX_NUM_CCs][4]; int32_t uplink_frequency_offset[MAX_NUM_CCs][4]; -int asn1_xer_print; int oai_exit = 0; instance_t CUuniqInstance = 0; diff --git a/openair2/E1AP/e1ap_common.h b/openair2/E1AP/e1ap_common.h index 452762965c..6c556a6cc6 100644 --- a/openair2/E1AP/e1ap_common.h +++ b/openair2/E1AP/e1ap_common.h @@ -41,8 +41,6 @@ typedef struct e1ap_upcp_inst_s { e1ap_net_config_t net_config; } e1ap_upcp_inst_t; -extern int asn1_xer_print; - // forward declaration so we don't require E1AP ASN.1 when including this // header struct E1AP_E1AP_PDU; diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index db04c5a8da..b751d78733 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -49,9 +49,6 @@ #define GNB_CONF_STRING_OTG_APP_TYPE "app_type" #define GNB_CONF_STRING_OTG_BG_TRAFFIC "bg_traffic" -extern int asn_debug; -extern int asn1_xer_print; - #ifdef LIBCONFIG_LONG #define libconfig_int long #else diff --git a/openair2/M2AP/m2ap_common.c b/openair2/M2AP/m2ap_common.c index aa090d124d..d9183df6fb 100644 --- a/openair2/M2AP/m2ap_common.c +++ b/openair2/M2AP/m2ap_common.c @@ -47,7 +47,7 @@ ssize_t m2ap_generate_initiating_message( pdu.choice.initiatingMessage.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.initiatingMessage.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M2AP_M2AP_PDU, (void *)&pdu); } @@ -80,7 +80,7 @@ ssize_t m2ap_generate_unsuccessfull_outcome( pdu.choice.successfulOutcome.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.successfulOutcome.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M2AP_M2AP_PDU, (void *)&pdu); } diff --git a/openair2/M2AP/m2ap_common.h b/openair2/M2AP/m2ap_common.h index 266b303518..a875d8d078 100644 --- a/openair2/M2AP/m2ap_common.h +++ b/openair2/M2AP/m2ap_common.h @@ -50,8 +50,6 @@ # define M2AP_PORT 36423 #endif -extern int asn1_xer_print; - #include "common/utils/LOG/log.h" #include "m2ap_default_values.h" #define M2AP_INFO(x, args...) LOG_I(M2AP, x, ##args) diff --git a/openair2/M2AP/m2ap_decoder.c b/openair2/M2AP/m2ap_decoder.c index 1237c4ec8e..4abd43f564 100644 --- a/openair2/M2AP/m2ap_decoder.c +++ b/openair2/M2AP/m2ap_decoder.c @@ -165,7 +165,7 @@ int m2ap_decode_pdu(M2AP_M2AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t length, 0, 0); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M2AP_M2AP_PDU, pdu); } diff --git a/openair2/M2AP/m2ap_encoder.c b/openair2/M2AP/m2ap_encoder.c index 8f798ff55c..81a3bfce92 100644 --- a/openair2/M2AP/m2ap_encoder.c +++ b/openair2/M2AP/m2ap_encoder.c @@ -44,7 +44,7 @@ int m2ap_encode_pdu(M2AP_M2AP_PDU_t *pdu, uint8_t **buffer, uint32_t *len) DevAssert(buffer != NULL); DevAssert(len != NULL); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M2AP_M2AP_PDU, (void *)pdu); } diff --git a/openair2/X2AP/x2ap_common.c b/openair2/X2AP/x2ap_common.c index a687b90666..0fc63b7de5 100644 --- a/openair2/X2AP/x2ap_common.c +++ b/openair2/X2AP/x2ap_common.c @@ -47,7 +47,7 @@ ssize_t x2ap_generate_initiating_message( pdu.choice.initiatingMessage.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.initiatingMessage.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_X2AP_X2AP_PDU, (void *)&pdu); } @@ -79,7 +79,7 @@ ssize_t x2ap_generate_successfull_outcome( pdu.choice.successfulOutcome.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.successfulOutcome.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_X2AP_X2AP_PDU, (void *)&pdu); } @@ -111,7 +111,7 @@ ssize_t x2ap_generate_unsuccessfull_outcome( pdu.choice.successfulOutcome.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.successfulOutcome.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_X2AP_X2AP_PDU, (void *)&pdu); } diff --git a/openair2/X2AP/x2ap_common.h b/openair2/X2AP/x2ap_common.h index 81880cb71c..36dfe24287 100644 --- a/openair2/X2AP/x2ap_common.h +++ b/openair2/X2AP/x2ap_common.h @@ -49,8 +49,6 @@ # define X2AP_PORT 36422 #endif -extern int asn1_xer_print; - #include "common/utils/LOG/log.h" #define X2AP_INFO(x, args...) LOG_I(X2AP, x, ##args) #define X2AP_ERROR(x, args...) LOG_E(X2AP, x, ##args) diff --git a/openair2/X2AP/x2ap_eNB_decoder.c b/openair2/X2AP/x2ap_eNB_decoder.c index 6d7897bf14..81cb77dfef 100644 --- a/openair2/X2AP/x2ap_eNB_decoder.c +++ b/openair2/X2AP/x2ap_eNB_decoder.c @@ -156,7 +156,7 @@ int x2ap_eNB_decode_pdu(X2AP_X2AP_PDU_t *pdu, const uint8_t *const buffer, uint3 length, 0, 0); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_X2AP_X2AP_PDU, pdu); } diff --git a/openair2/X2AP/x2ap_eNB_encoder.c b/openair2/X2AP/x2ap_eNB_encoder.c index 8b1c030d35..a82eb03f9a 100644 --- a/openair2/X2AP/x2ap_eNB_encoder.c +++ b/openair2/X2AP/x2ap_eNB_encoder.c @@ -44,7 +44,7 @@ int x2ap_eNB_encode_pdu(X2AP_X2AP_PDU_t *pdu, uint8_t **buffer, uint32_t *len) DevAssert(buffer != NULL); DevAssert(len != NULL); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_X2AP_X2AP_PDU, (void *)pdu); } diff --git a/openair3/M3AP/m3ap_common.c b/openair3/M3AP/m3ap_common.c index e43275d96c..669d7eaa5e 100644 --- a/openair3/M3AP/m3ap_common.c +++ b/openair3/M3AP/m3ap_common.c @@ -47,7 +47,7 @@ ssize_t m3ap_generate_initiating_message( pdu.choice.initiatingMessage.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.initiatingMessage.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M3AP_M3AP_PDU, (void *)&pdu); } @@ -80,7 +80,7 @@ ssize_t m3ap_generate_unsuccessfull_outcome( pdu.choice.successfulOutcome.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.successfulOutcome.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M3AP_M3AP_PDU, (void *)&pdu); } diff --git a/openair3/M3AP/m3ap_common.h b/openair3/M3AP/m3ap_common.h index 7c8ea18a5d..f5acb6bdf5 100644 --- a/openair3/M3AP/m3ap_common.h +++ b/openair3/M3AP/m3ap_common.h @@ -48,8 +48,6 @@ # define M3AP_PORT 36444 #endif -extern int asn1_xer_print; - #include "common/utils/LOG/log.h" #include "m3ap_default_values.h" #define M3AP_INFO(x, args...) LOG_I(M3AP, x, ##args) diff --git a/openair3/M3AP/m3ap_decoder.c b/openair3/M3AP/m3ap_decoder.c index cee0a5ff41..36c8f64fae 100644 --- a/openair3/M3AP/m3ap_decoder.c +++ b/openair3/M3AP/m3ap_decoder.c @@ -188,7 +188,7 @@ int m3ap_decode_pdu(M3AP_M3AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t length, 0, 0); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M3AP_M3AP_PDU, pdu); } diff --git a/openair3/M3AP/m3ap_encoder.c b/openair3/M3AP/m3ap_encoder.c index 2e1cbf80af..86a710ca90 100644 --- a/openair3/M3AP/m3ap_encoder.c +++ b/openair3/M3AP/m3ap_encoder.c @@ -44,7 +44,7 @@ int m3ap_encode_pdu(M3AP_M3AP_PDU_t *pdu, uint8_t **buffer, uint32_t *len) DevAssert(buffer != NULL); DevAssert(len != NULL); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M3AP_M3AP_PDU, (void *)pdu); } diff --git a/openair3/NGAP/ngap_common.c b/openair3/NGAP/ngap_common.c index 7b960fed0b..82c2ccaf2a 100644 --- a/openair3/NGAP/ngap_common.c +++ b/openair3/NGAP/ngap_common.c @@ -31,8 +31,6 @@ #include "conversions.h" #include "ngap_common.h" -int asn1_xer_print = 0; - void encode_ngap_cause(NGAP_Cause_t *out, const ngap_cause_t *in) { switch (in->type) { diff --git a/openair3/NGAP/ngap_common.h b/openair3/NGAP/ngap_common.h index 82a8fe9234..dd7fdebc54 100644 --- a/openair3/NGAP/ngap_common.h +++ b/openair3/NGAP/ngap_common.h @@ -48,9 +48,6 @@ #define NGAP_UE_ID_FMT "0x%06"PRIX32 -extern int asn_debug; -extern int asn1_xer_print; - # include "common/utils/LOG/log.h" # include "ngap_gNB_default_values.h" # define NGAP_ERROR(x, args...) LOG_E(NGAP, x, ##args) diff --git a/openair3/NGAP/ngap_gNB_encoder.c b/openair3/NGAP/ngap_gNB_encoder.c index ee3b37e07d..c3c095541d 100644 --- a/openair3/NGAP/ngap_gNB_encoder.c +++ b/openair3/NGAP/ngap_gNB_encoder.c @@ -115,7 +115,7 @@ int ngap_gNB_encode_pdu(NGAP_NGAP_PDU_t *pdu, uint8_t **buffer, uint32_t *len) DevAssert(pdu != NULL); DevAssert(buffer != NULL); DevAssert(len != NULL); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, (void *)pdu); } switch (pdu->present) { diff --git a/openair3/NGAP/ngap_gNB_nas_procedures.c b/openair3/NGAP/ngap_gNB_nas_procedures.c index 1aafeca8c9..b291f6b96c 100644 --- a/openair3/NGAP/ngap_gNB_nas_procedures.c +++ b/openair3/NGAP/ngap_gNB_nas_procedures.c @@ -690,7 +690,7 @@ int ngap_gNB_initial_ctxt_resp(instance_t instance, ngap_initial_context_setup_r // ie->value.choice.CriticalityDiagnostics =; } - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, &pdu); } @@ -772,7 +772,7 @@ int ngap_gNB_initial_ctxt_fail(instance_t instance, ngap_initial_context_setup_f ie->value.present = NGAP_InitialContextSetupFailureIEs__value_PR_Cause; encode_ngap_cause(&ie->value.choice.Cause, &initial_ctxt_fail->cause); } - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, &pdu); } if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) { diff --git a/openair3/S1AP/s1ap_common.c b/openair3/S1AP/s1ap_common.c deleted file mode 100644 index d31c6e5839..0000000000 --- a/openair3/S1AP/s1ap_common.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 - */ - -/*! \file s1ap_common.c - * \brief s1ap procedures for both eNB and MME - * \author Sebastien ROUX and Navid Nikaein - * \email navid.nikaein@eurecom.fr - * \date 2012-2015 - * \version 0.1 - */ - -#include - -#include "s1ap_common.h" -#include "S1AP_S1AP-PDU.h" - - -int asn1_xer_print = 0; - - -void s1ap_handle_criticality(S1AP_Criticality_t criticality) -{ -} diff --git a/openair3/S1AP/s1ap_common.h b/openair3/S1AP/s1ap_common.h index 797bc7be8a..7ef7e589ca 100644 --- a/openair3/S1AP/s1ap_common.h +++ b/openair3/S1AP/s1ap_common.h @@ -52,8 +52,6 @@ #define S1AP_UE_ID_FMT "0x%06"PRIX32 -extern int asn1_xer_print; - #include "common/utils/LOG/log.h" #include "s1ap_eNB_default_values.h" #define S1AP_ERROR(x, args...) LOG_E(S1AP, x, ##args) @@ -89,10 +87,4 @@ extern int asn1_xer_print; **/ typedef int (*s1ap_message_decoded_callback)(sctp_assoc_t assoc_id, uint32_t stream, S1AP_S1AP_PDU_t *pdu); -/** \brief Handle criticality - \param criticality Criticality of the IE - @returns void - **/ -void s1ap_handle_criticality(S1AP_Criticality_t criticality); -/** @}*/ #endif /* S1AP_COMMON_H_ */ diff --git a/openair3/S1AP/s1ap_eNB_encoder.c b/openair3/S1AP/s1ap_eNB_encoder.c index 8317f02731..6ff837a7f1 100644 --- a/openair3/S1AP/s1ap_eNB_encoder.c +++ b/openair3/S1AP/s1ap_eNB_encoder.c @@ -129,7 +129,7 @@ int s1ap_eNB_encode_initiating(S1AP_S1AP_PDU_t *pdu, return -1; } - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_S1AP_S1AP_PDU, (void *)pdu); } @@ -183,7 +183,7 @@ int s1ap_eNB_encode_successfull_outcome(S1AP_S1AP_PDU_t *pdu, return -1; } - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_S1AP_S1AP_PDU, (void *)pdu); } @@ -214,7 +214,7 @@ int s1ap_eNB_encode_unsuccessfull_outcome(S1AP_S1AP_PDU_t *pdu, return -1; } - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_S1AP_S1AP_PDU, (void *)pdu); } diff --git a/openair3/TEST/test_s1ap.c b/openair3/TEST/test_s1ap.c index 8e61c338ac..1ecb30d476 100644 --- a/openair3/TEST/test_s1ap.c +++ b/openair3/TEST/test_s1ap.c @@ -154,10 +154,6 @@ void doit (void) { int i; break_on_error = 1; - - // asn1_xer_print = 1; - // asn_debug = 1; - for (i = 0; i < sizeof(s1ap_test) / sizeof(s1ap_test_t); i++) { S1AP_S1AP_PDU_t pdu; uint8_t *buffer; From c2d9f17242bd845b5306d7593ff5d5724766e2e1 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 29 Jul 2025 17:59:53 +0200 Subject: [PATCH 25/49] remove a dependancy of gNB to LTE that is not used and adds global vars to gNB --- executables/nr-softmodem.c | 1 - 1 file changed, 1 deletion(-) diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 5cc5e3a4fc..261dc9a66b 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -24,7 +24,6 @@ #include "common/config/config_userapi.h" #include "common/utils/load_module_shlib.h" -#include "RRC/LTE/rrc_vars.h" #ifdef SMBV #include "PHY/TOOLS/smbv.h" unsigned short config_frames[4] = {2,9,11,13}; From 4b47d26ea291fdd71dd5d8384aa4fdab8fb89999 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 29 Jul 2025 18:07:51 +0200 Subject: [PATCH 26/49] remove dirty and unused functions with global vars (not working anyway) --- executables/lte-softmodem.c | 49 ----------------------------------- executables/lte-uesoftmodem.c | 49 ----------------------------------- executables/nr-softmodem.c | 46 -------------------------------- 3 files changed, 144 deletions(-) diff --git a/executables/lte-softmodem.c b/executables/lte-softmodem.c index 6a7e607586..3034512b2c 100644 --- a/executables/lte-softmodem.c +++ b/executables/lte-softmodem.c @@ -204,55 +204,6 @@ void nr_slot_select() {}; void NR_UL_indication(NR_UL_IND_t *UL_INFO) {}; void gNB_dlsch_ulsch_scheduler() {}; -/*---------------------BMC: timespec helpers -----------------------------*/ - -struct timespec min_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; -struct timespec max_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - -struct timespec clock_difftime(struct timespec start, struct timespec end) { - struct timespec temp; - - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - - return temp; -} - -void print_difftimes(void) { -#ifdef DEBUG - printf("difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec); -#else - LOG_I(HW,"difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec); -#endif -} - -void update_difftimes(struct timespec start, struct timespec end) { - struct timespec diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - int changed = 0; - diff_time = clock_difftime(start, end); - - if ((min_diff_time.tv_nsec == 0) || (diff_time.tv_nsec < min_diff_time.tv_nsec)) { - min_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - - if ((max_diff_time.tv_nsec == 0) || (diff_time.tv_nsec > max_diff_time.tv_nsec)) { - max_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - -#if 1 - - if (changed) print_difftimes(); - -#endif -} - /*------------------------------------------------------------------------*/ unsigned int build_rflocal(int txi, int txq, int rxi, int rxq) { diff --git a/executables/lte-uesoftmodem.c b/executables/lte-uesoftmodem.c index 7de3014ee9..b212ceb3f1 100644 --- a/executables/lte-uesoftmodem.c +++ b/executables/lte-uesoftmodem.c @@ -174,55 +174,6 @@ instance_t CUuniqInstance=0; /* forward declarations */ void set_default_frame_parms(LTE_DL_FRAME_PARMS *frame_parms[MAX_NUM_CCs]); -/*---------------------BMC: timespec helpers -----------------------------*/ - -struct timespec min_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; -struct timespec max_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - -struct timespec clock_difftime(struct timespec start, struct timespec end) { - struct timespec temp; - - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - - return temp; -} - -void print_difftimes(void) { -#ifdef DEBUG - printf("difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec); -#else - LOG_I(HW,"difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec); -#endif -} - -void update_difftimes(struct timespec start, struct timespec end) { - struct timespec diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - int changed = 0; - diff_time = clock_difftime(start, end); - - if ((min_diff_time.tv_nsec == 0) || (diff_time.tv_nsec < min_diff_time.tv_nsec)) { - min_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - - if ((max_diff_time.tv_nsec == 0) || (diff_time.tv_nsec > max_diff_time.tv_nsec)) { - max_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - -#if 1 - - if (changed) print_difftimes(); - -#endif -} - /*------------------------------------------------------------------------*/ unsigned int build_rflocal(int txi, int txq, int rxi, int rxq) { diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 261dc9a66b..c3875bf267 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -129,52 +129,6 @@ void pdcp_run(const protocol_ctxt_t *const ctxt_pP) abort(); } -/*---------------------BMC: timespec helpers -----------------------------*/ - -struct timespec min_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; -struct timespec max_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - -struct timespec clock_difftime(struct timespec start, struct timespec end) { - struct timespec temp; - - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - - return temp; -} - -void print_difftimes(void) -{ - LOG_I(HW, "difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec); -} - -void update_difftimes(struct timespec start, struct timespec end) { - struct timespec diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - int changed = 0; - diff_time = clock_difftime(start, end); - - if ((min_diff_time.tv_nsec == 0) || (diff_time.tv_nsec < min_diff_time.tv_nsec)) { - min_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - - if ((max_diff_time.tv_nsec == 0) || (diff_time.tv_nsec > max_diff_time.tv_nsec)) { - max_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - -#if 1 - - if (changed) print_difftimes(); - -#endif -} - /*------------------------------------------------------------------------*/ unsigned int build_rflocal(int txi, int txq, int rxi, int rxq) { From 2bda52b054faf2f0e99148d5d0d2416d25267fb3 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 29 Jul 2025 19:11:04 +0200 Subject: [PATCH 27/49] rename a global "ru" in root_unit and include correctly the .h file, that leads to mistakes fixes --- openair1/PHY/LTE_TRANSPORT/prach.c | 7 ++- openair1/PHY/LTE_TRANSPORT/prach_common.c | 13 ++--- openair1/PHY/LTE_TRANSPORT/prach_extern.h | 14 ++---- openair1/PHY/LTE_UE_TRANSPORT/prach_ue.c | 47 +++++++------------ .../PHY/LTE_UE_TRANSPORT/transport_proto_ue.h | 3 +- openair1/PHY/defs_UE.h | 2 +- openair1/PHY/defs_eNB.h | 4 +- 7 files changed, 34 insertions(+), 56 deletions(-) diff --git a/openair1/PHY/LTE_TRANSPORT/prach.c b/openair1/PHY/LTE_TRANSPORT/prach.c index e220549045..90c9d63925 100644 --- a/openair1/PHY/LTE_TRANSPORT/prach.c +++ b/openair1/PHY/LTE_TRANSPORT/prach.c @@ -69,8 +69,7 @@ void rx_prach0(PHY_VARS_eNB *eNB, uint32_t preamble_shift2; uint16_t preamble_index0=0,n_shift_ra=0,n_shift_ra_bar; uint16_t d_start=0; - uint16_t numshift=0; - uint16_t *prach_root_sequence_map; + uint16_t numshift = 0; uint8_t not_found; int k=0; uint16_t u; @@ -221,7 +220,7 @@ void rx_prach0(PHY_VARS_eNB *eNB, if (eNB) start_meas(&eNB->rx_prach); - prach_root_sequence_map = (prach_fmt < 4) ? prach_root_sequence_map0_3 : prach_root_sequence_map4; + const uint16_t *prach_root_sequence_map = (prach_fmt < 4) ? prach_root_sequence_map0_3 : prach_root_sequence_map4; // PDP is oversampled, e.g. 1024 sample instead of 839 // Adapt the NCS (zero-correlation zones) with oversampling factor e.g. 1024/839 NCS2 = (N_ZC==839) ? ((NCS<<10)/839) : ((NCS<<8)/139); @@ -495,7 +494,7 @@ void rx_prach0(PHY_VARS_eNB *eNB, new_dft = 0; if (br_flag == 1) { - Xu=(c16_t *)eNB->X_u_br[ce_level][preamble_offset-first_nonzero_root_idx]; + Xu = eNB->X_u_br[ce_level][preamble_offset - first_nonzero_root_idx]; prach_ifft = prach_ifftp[prach_ifft_cnt++]; if (eNB->prach_vars_br.repetition_number[ce_level]==1) memset(prach_ifft,0,((N_ZC==839)?2048:256)*sizeof(int32_t)); diff --git a/openair1/PHY/LTE_TRANSPORT/prach_common.c b/openair1/PHY/LTE_TRANSPORT/prach_common.c index 2d9ef9d712..0939398c60 100644 --- a/openair1/PHY/LTE_TRANSPORT/prach_common.c +++ b/openair1/PHY/LTE_TRANSPORT/prach_common.c @@ -35,12 +35,13 @@ #include "PHY/phy_extern_ue.h" #include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/lte/prach_utils.h" +#include "openair1/PHY/LTE_TRANSPORT/prach_extern.h" const uint16_t NCS_unrestricted[16] = {0, 13, 15, 18, 22, 26, 32, 38, 46, 59, 76, 93, 119, 167, 279, 419}; const uint16_t NCS_restricted[15] = {15, 18, 22, 26, 32, 38, 46, 55, 68, 82, 100, 128, 158, 202, 237}; // high-speed case const uint16_t NCS_4[7] = {2, 4, 6, 8, 10, 12, 15}; -int16_t ru[2*839]; // quantized roots of unity +c16_t root_unit[839]; // quantized roots of unity static uint32_t ZC_inv[839]; // multiplicative inverse for roots u uint16_t du[838]; @@ -367,13 +368,13 @@ int is_prach_subframe(LTE_DL_FRAME_PARMS *frame_parms, uint32_t frame, uint8_t s return(prach_mask); } - void compute_prach_seq(uint16_t rootSequenceIndex, uint8_t prach_ConfigIndex, uint8_t zeroCorrelationZoneConfig, uint8_t highSpeedFlag, frame_type_t frame_type, - uint32_t X_u[64][839]) { + c16_t X_u[64][839]) +{ // Compute DFT of x_u => X_u[k] = x_u(inv(u)*k)^* X_u[k] = exp(j\pi u*inv(u)*k*(inv(u)*k+1)/N_ZC) unsigned int k,inv_u,i,NCS=0,num_preambles; int N_ZC; @@ -495,7 +496,7 @@ void compute_prach_seq(uint16_t rootSequenceIndex, for (k=0; kframe_parms.prach_config_common.prach_ConfigInfo.prach_ConfigIndex, ue->frame_parms.prach_config_common.prach_ConfigInfo.prach_FreqOffset, tdd_mapindex, Nf); - prach_root_sequence_map = (prach_fmt<4) ? prach_root_sequence_map0_3 : prach_root_sequence_map4; + const uint16_t *prach_root_sequence_map = (prach_fmt < 4) ? prach_root_sequence_map0_3 : prach_root_sequence_map4; /* // this code is not part of get_prach_prb_offset if (frame_type == TDD) { // TDD @@ -215,20 +210,6 @@ int32_t generate_prach( PHY_VARS_UE *ue, uint8_t eNB_id, uint8_t subframe, uint1 #endif // nsymb = (frame_parms->Ncp==0) ? 14:12; // subframe_offset = (unsigned int)frame_parms->ofdm_symbol_size*subframe*nsymb; - k = (12*n_ra_prb) - 6*ue->frame_parms.N_RB_UL; - - if (k<0) - k+=ue->frame_parms.ofdm_symbol_size; - - k*=12; - k+=13; - Xu = (int16_t *)ue->X_u[preamble_offset-first_nonzero_root_idx]; - /* - k+=(12*ue->frame_parms.first_carrier_offset); - if (k>(12*ue->frame_parms.ofdm_symbol_size)) - k-=(12*ue->frame_parms.ofdm_symbol_size); - */ - k*=2; switch (ue->frame_parms.N_RB_UL) { case 6: @@ -259,20 +240,26 @@ int32_t generate_prach( PHY_VARS_UE *ue, uint8_t eNB_id, uint8_t subframe, uint1 break; } + { + int k = (12 * n_ra_prb) - 6 * ue->frame_parms.N_RB_UL; - for (offset=0,offset2=0; offset= N_ZC) - offset2 -= N_ZC; + if (k < 0) + k += ue->frame_parms.ofdm_symbol_size; - Xu_re = (((int32_t)Xu[offset<<1]*amp)>>15); - Xu_im = (((int32_t)Xu[1+(offset<<1)]*amp)>>15); - prachF[k++]= ((Xu_re*ru[offset2<<1]) - (Xu_im*ru[1+(offset2<<1)]))>>15; - prachF[k++]= ((Xu_im*ru[offset2<<1]) + (Xu_re*ru[1+(offset2<<1)]))>>15; + k *= 12; + k += 13; + c16_t *Xu_ue = ue->X_u[preamble_offset - first_nonzero_root_idx]; + c16_t *complex_prachF = (c16_t *)prachF; + for (int offset = 0, offset2 = 0; offset < N_ZC; offset++, offset2 += preamble_shift) { + if (offset2 >= N_ZC) + offset2 -= N_ZC; + c16_t Xu = c16mulRealShift(*Xu_ue++, amp, 15); + complex_prachF[k++] = c16mulShift(Xu, root_unit[offset2], 15); - if (k==(12*2*ue->frame_parms.ofdm_symbol_size)) - k=0; + if (k == 12 * ue->frame_parms.ofdm_symbol_size) + k = 0; + } } - switch (prach_fmt) { case 0: Ncp = 3168; diff --git a/openair1/PHY/LTE_UE_TRANSPORT/transport_proto_ue.h b/openair1/PHY/LTE_UE_TRANSPORT/transport_proto_ue.h index 9aab56e52a..06bf87fc74 100644 --- a/openair1/PHY/LTE_UE_TRANSPORT/transport_proto_ue.h +++ b/openair1/PHY/LTE_UE_TRANSPORT/transport_proto_ue.h @@ -1649,8 +1649,7 @@ void compute_prach_seq(uint16_t rootSequenceIndex, uint8_t zeroCorrelationZoneConfig, uint8_t highSpeedFlag, frame_type_t frame_type, - uint32_t X_u[64][839]); - + c16_t X_u[64][839]); void init_prach_tables(int N_ZC); diff --git a/openair1/PHY/defs_UE.h b/openair1/PHY/defs_UE.h index 17ae02610e..20d5a9ab8d 100644 --- a/openair1/PHY/defs_UE.h +++ b/openair1/PHY/defs_UE.h @@ -640,7 +640,7 @@ typedef struct { /// mbsfn reference symbols uint32_t lte_gold_mbsfn_khz_1dot25_table[10][150]; - uint32_t X_u[64][839]; + c16_t X_u[64][839]; uint32_t high_speed_flag; uint32_t perfect_ce; diff --git a/openair1/PHY/defs_eNB.h b/openair1/PHY/defs_eNB.h index 68ebf65462..98d1080ee3 100644 --- a/openair1/PHY/defs_eNB.h +++ b/openair1/PHY/defs_eNB.h @@ -600,8 +600,8 @@ typedef struct PHY_VARS_eNB_s { // PUCCH1a/b energy detection parameters for eMTC per CE-level int pucch1ab_DTX_threshold_emtc[4]; - uint32_t X_u[64][839]; - uint32_t X_u_br[4][64][839]; + c16_t X_u[64][839]; + c16_t X_u_br[4][64][839]; uint8_t pbch_configured; uint8_t pbch_pdu[4]; //PBCH_PDU_SIZE char eNB_generate_rar; From 13f5c258ca5b188dcd1b3df7e1f3745143b310d6 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 29 Jul 2025 19:24:24 +0200 Subject: [PATCH 28/49] remove unused global --- nfapi/oai_integration/nfapi_pnf.c | 1 - 1 file changed, 1 deletion(-) diff --git a/nfapi/oai_integration/nfapi_pnf.c b/nfapi/oai_integration/nfapi_pnf.c index 48e31b7264..3de11d8003 100644 --- a/nfapi/oai_integration/nfapi_pnf.c +++ b/nfapi/oai_integration/nfapi_pnf.c @@ -88,7 +88,6 @@ extern int nfapi_sync_var; extern int sync_var; nfapi_tx_request_pdu_t *tx_request_pdu[1023][10][10]; // [frame][subframe][max_num_pdus] -nfapi_nr_pdu_t *tx_data_request[1023][20][10]; //[frame][slot][max_num_pdus] uint8_t tx_pdus[32][8][4096]; nfapi_ue_release_request_body_t release_rntis; From fc1c8a9be066eeee0f90a9d9ce67ad24ff478be0 Mon Sep 17 00:00:00 2001 From: Sagar Arora Date: Wed, 20 Aug 2025 13:17:45 +0200 Subject: [PATCH 29/49] Align add_boolean_option as per cmake documentation use only ON/OFF for uniformity Signed-off-by: Sagar Arora --- CMakeLists.txt | 42 +++++++++++++++++++++--------------------- doc/CMakeLists.txt | 2 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 898b387237..cba949fbad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,26 +194,26 @@ set(CMAKE_CXX_FLAGS "${C_FLAGS_PROCESSOR} ${commonOpts} -std=c++11 ${CMAKE_CXX_FLAGS}") -add_boolean_option(SANITIZE_ADDRESS False "enable the address sanitizer (ASan)" ON) +add_boolean_option(SANITIZE_ADDRESS OFF "enable the address sanitizer (ASan)" ON) if (SANITIZE_ADDRESS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fno-common") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fno-common") endif () -add_boolean_option(SANITIZE_THREAD False "enable the address sanitizer (TSan)" ON) +add_boolean_option(SANITIZE_THREAD OFF "enable the address sanitizer (TSan)" ON) if (SANITIZE_THREAD) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -fno-omit-frame-pointer -fno-common") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -fno-omit-frame-pointer -fno-common") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread") endif() -add_boolean_option(SANITIZE_UNDEFINED False "enable the undefined behavior sanitizer (UBSan)" ON) +add_boolean_option(SANITIZE_UNDEFINED OFF "enable the undefined behavior sanitizer (UBSan)" ON) if (SANITIZE_UNDEFINED) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") endif () -add_boolean_option(SANITIZE_MEMORY False "enable the memory sanitizer (MSan, requires clang, incompatible with ASan/UBSan)" ON) +add_boolean_option(SANITIZE_MEMORY OFF "enable the memory sanitizer (MSan, requires clang, incompatible with ASan/UBSan)" ON) if(SANITIZE_MEMORY) if (SANITIZE_UNDEFINED OR SANITIZE_ADDRESS) message(FATAL_ERROR "memory sanitizer cannot coexist with address sanitizer or undefined behavior sanitizer, please disable either MSan or ASan and UBSan") @@ -278,10 +278,10 @@ endif() # OAI uses this feature to re-use OAI LOG_I(ASN1, ...) # see common/utils/config.h add_boolean_option(TRACE_ASN1C_ENC_DEC OFF "Enable ASN1 encoder/decoder debug traces via OAI logging system" ON) -add_boolean_option(T_TRACER True "Activate the T tracer, a debugging/monitoring framework" ON) -add_boolean_option(ENABLE_LTTNG False "Activate the LTTNG tracer, a debugging/monitoring framework" ON) -add_boolean_option(UE_DEBUG_TRACE False "Activate UE debug trace" ON) -add_boolean_option(UE_TIMING_TRACE False "Activate UE timing trace" ON) +add_boolean_option(T_TRACER ON "Activate the T tracer, a debugging/monitoring framework" ON) +add_boolean_option(ENABLE_LTTNG OFF "Activate the LTTNG tracer, a debugging/monitoring framework" ON) +add_boolean_option(UE_DEBUG_TRACE OFF "Activate UE debug trace" ON) +add_boolean_option(UE_TIMING_TRACE OFF "Activate UE timing trace" ON) add_boolean_option(TRACY_ENABLE OFF "Enable tracy instrumentation" ON) if (TRACY_ENABLE) @@ -509,34 +509,34 @@ include_directories ("${OPENAIR_DIR}/radio/COMMON") # ???!!! TO BE DOCUMENTED OPTIONS !!!??? ############################################################## -add_boolean_option(UE_EXPANSION False "enable UE_EXPANSION with max 256 UE" ON) -add_boolean_option(PHY_TX_THREAD False "enable UE_EXPANSION with max 256 UE" ON) -add_boolean_option(PRE_SCD_THREAD False "enable UE_EXPANSION with max 256 UE" ON) +add_boolean_option(UE_EXPANSION OFF "enable UE_EXPANSION with max 256 UE" ON) +add_boolean_option(PHY_TX_THREAD OFF "enable UE_EXPANSION with max 256 UE" ON) +add_boolean_option(PRE_SCD_THREAD OFF "enable UE_EXPANSION with max 256 UE" ON) ########################## # SCHEDULING/REAL-TIME/PERF options ########################## -add_boolean_option(ENABLE_USE_CPU_EXECUTION_TIME False "Add data in vcd traces: disable it if perf issues" ON) -add_boolean_option(ENABLE_VCD False "always true now, time measurements of proc calls and var displays" ON) -add_boolean_option(ENABLE_VCD_FIFO False "time measurements of proc calls and var displays sent to FIFO (one more thread)" ON) +add_boolean_option(ENABLE_USE_CPU_EXECUTION_TIME OFF "Add data in vcd traces: disable it if perf issues" ON) +add_boolean_option(ENABLE_VCD OFF "always true now, time measurements of proc calls and var displays" ON) +add_boolean_option(ENABLE_VCD_FIFO OFF "time measurements of proc calls and var displays sent to FIFO (one more thread)" ON) ########################## # PHY options ########################## -add_integer_option(MAX_NUM_CCs 1 "Carrier component data arrays size (oai doesn't support carrier aggreagtion for now)" ON) -add_boolean_option(SMBV False "Rohde&Schwarz SMBV100A vector signal generator" ON) -add_boolean_option(DEBUG_PHY False "Enable PHY layer debugging options" ON) -add_boolean_option(DEBUG_PHY_PROC False "Enable debugging of PHY layer procedures" ON) +add_integer_option(MAX_NUM_CCs 1 "Carrier component data arrays size (oai doesn't support carrier aggregation for now)" ON) +add_boolean_option(SMBV OFF "Rohde&Schwarz SMBV100A vector signal generator" ON) +add_boolean_option(DEBUG_PHY OFF "Enable PHY layer debugging options" ON) +add_boolean_option(DEBUG_PHY_PROC OFF "Enable debugging of PHY layer procedures" ON) ########################## # NAS LAYER OPTIONS ########################## -add_boolean_option(NAS_BUILT_IN_UE True "UE NAS layer present in this executable" ON) +add_boolean_option(NAS_BUILT_IN_UE ON "UE NAS layer present in this executable" ON) ########################## # RRC LAYER OPTIONS ########################## -add_boolean_option(RRC_DEFAULT_RAB_IS_AM True "set the LTE RLC mode to AM for the default bearer, otherwise it is UM." ON) +add_boolean_option(RRC_DEFAULT_RAB_IS_AM ON "set the LTE RLC mode to AM for the default bearer, otherwise it is UM." ON) # add the binary tree to the search path for include files # We will find common/oai_version.h after generation in target directory @@ -603,7 +603,7 @@ include_directories("${OPENAIR_DIR}") ############### # include T directory even if the T is off because T macros are in the code -# no matter what. Note: sone files are generated, so we have both source and +# no matter what. Note: some files are generated, so we have both source and # binary directories include_directories(${CMAKE_CURRENT_SOURCE_DIR}/common/utils/T ${CMAKE_CURRENT_BINARY_DIR}/common/utils/T) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 2b6c1dd0bb..7f46dc1517 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,4 +1,4 @@ -add_boolean_option(GENERATE_DOXYGEN False "Generate source code doc using doxygen" OFF) +add_boolean_option(GENERATE_DOXYGEN OFF "Generate source code doc using doxygen" OFF) if(GENERATE_DOXYGEN) find_package(Doxygen REQUIRED) From 16f529920382a8e43b3fd4d82ddfe075289246aa Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Tue, 15 Apr 2025 16:12:13 +0200 Subject: [PATCH 30/49] Taps client client for vrtsim Added a libary that can connect to a channel emulator and provide taps as they are generated. - Enable connection from vrtsim to channel emulation server - Depends on flatbuffers definition file from raytracing-channel-emulator - Depends on libanomsg-dev and flatbuffers-compiler (Ubuntu) or equivalent - Added a compile time option ENABLE_TAPS_CLIENT to prevent other users from being affected by additional dependencies --- openair1/SIMULATION/TOOLS/sim.h | 2 + radio/vrtsim/CMakeLists.txt | 25 ++++ radio/vrtsim/taps_client.cpp | 198 ++++++++++++++++++++++++++++++++ radio/vrtsim/taps_client.h | 38 ++++++ radio/vrtsim/vrtsim.c | 70 ++++++++--- 5 files changed, 318 insertions(+), 15 deletions(-) create mode 100644 radio/vrtsim/taps_client.cpp create mode 100644 radio/vrtsim/taps_client.h diff --git a/openair1/SIMULATION/TOOLS/sim.h b/openair1/SIMULATION/TOOLS/sim.h index 98b4fb26c6..359df00b55 100644 --- a/openair1/SIMULATION/TOOLS/sim.h +++ b/openair1/SIMULATION/TOOLS/sim.h @@ -77,6 +77,8 @@ typedef struct { struct complexd **a; ///interpolated (sample-spaced) channel impulse response. size(ch) = (n_tx * n_rx) * channel_length. ATTENTION: the dimensions of ch are the transposed ones of a. This is to allow the use of BLAS when applying the correlation matrices to the state. struct complexd **ch; + ///Same as above but single precision + struct complexf **ch_ps; ///Sampled frequency response (90 kHz resolution) struct complexd **chF; ///Maximum path delay in mus. diff --git a/radio/vrtsim/CMakeLists.txt b/radio/vrtsim/CMakeLists.txt index 79eb4d88da..9ac1555099 100644 --- a/radio/vrtsim/CMakeLists.txt +++ b/radio/vrtsim/CMakeLists.txt @@ -2,3 +2,28 @@ add_library(vrtsim MODULE vrtsim.c noise_device.c) set_target_properties(vrtsim PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) target_link_libraries(vrtsim PRIVATE SIMU shm_td_iq_channel actor) add_dependencies(vrtsim generate_T) + +add_boolean_option(OAI_VRTSIM_TAPS_CLIENT OFF "Enable taps client" ON) +if (OAI_VRTSIM_TAPS_CLIENT) + target_link_libraries(vrtsim PRIVATE taps_client) + file(DOWNLOAD + "https://gitlab.eurecom.fr/oai/raytracing-channel-emulator/-/raw/59589ad2a534881810b2aa7dd81e63066245bb8d/server/api/taps.fbs?inline=false" + ${CMAKE_SOURCE_DIR}/taps.fbs + ) + + set(TAPS_IF_PATH ${CMAKE_SOURCE_DIR}/taps.fbs) + set(TAPS_API_HEADER taps_generated.h) + + add_custom_command(OUTPUT ${TAPS_API_HEADER} + COMMAND flatc -c ${TAPS_IF_PATH} + DEPENDS ${TAPS_IF_PATH} + COMMENT "Generating flatbuffers API from ${TAPS_IF_PATH}" + ) + add_library(taps_api INTERFACE) + target_include_directories(taps_api INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) + + add_library(taps_client taps_client.cpp ${TAPS_API_HEADER}) + target_include_directories(taps_client PUBLIC .) + find_package(Flatbuffers REQUIRED) + target_link_libraries(taps_client PUBLIC flatbuffers taps_api SIMU nanomsg) +endif() diff --git a/radio/vrtsim/taps_client.cpp b/radio/vrtsim/taps_client.cpp new file mode 100644 index 0000000000..6ebfc450fd --- /dev/null +++ b/radio/vrtsim/taps_client.cpp @@ -0,0 +1,198 @@ +/* + * 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 + */ +#include +#include +#include +#include +#include +#include "pthread.h" +#include "taps_generated.h" +#include "SIMULATION/TOOLS/sim.h" +extern "C" { +#include "assertions.h" +#include "common/utils/LOG/log.h" +#include "sim.h" +#include "utils.h" +} +#include + +#define NUM_TAPS_BUFFERS 4 +#define MAX_TAPS_LEN 100 +#define MAX_TAPS_MSG_SIZE (sizeof(struct complexf) * MAX_TAPS_LEN * 4 * 4 + 20) + +static pthread_t client_thread; +static bool should_run = true; +typedef struct { + int id; + int sock; + uint32_t num_tx_antennas; + uint32_t num_rx_antennas; + channel_desc_t **channel_desc; +} client_thread_args_t; + +typedef struct { + void *taps_msg; + channel_desc_t *channel_desc; +} taps_buffer_t; + +typedef struct { + taps_buffer_t taps_buffers[NUM_TAPS_BUFFERS]; + int current_buffer; +} taps_storage_t; + +void ascii_line_plot(const float *data, size_t size, char *buffer) +{ + const char levels[] = "_.-=#"; // ASCII characters for different levels + size_t num_levels = sizeof(levels) - 1; // Number of levels (excluding the null terminator) + + // Calculate min and max values from the data + float min_val = FLT_MAX; + float max_val = FLT_MIN; + for (size_t i = 0; i < size; i++) { + if (data[i] < min_val) + min_val = data[i]; + if (data[i] > max_val) + max_val = data[i]; + } + + // Handle edge case where all values are the same + if (min_val == max_val) { + min_val -= 1.0f; + max_val += 1.0f; + } + + // Normalize and map data to levels + for (size_t i = 0; i < size; i++) { + float normalized = (data[i] - min_val) / (max_val - min_val); // Normalize to [0, 1] + size_t level_index = (size_t)(normalized * num_levels); // Map to level index + if (level_index > num_levels) + level_index = num_levels; // Clamp to valid range + snprintf(buffer + i, 2, "%c", levels[level_index]); + } +} + +static void init_taps_storage(taps_storage_t *storage, int num_tx_antennas, int num_rx_antennas) +{ + for (int i = 0; i < NUM_TAPS_BUFFERS; i++) { + storage->taps_buffers[i].taps_msg = calloc_or_fail(1, MAX_TAPS_MSG_SIZE); + storage->taps_buffers[i].channel_desc = (channel_desc_t *)calloc_or_fail(1, sizeof(channel_desc_t)); + storage->taps_buffers[i].channel_desc->ch_ps = + (struct complexf **)calloc_or_fail(num_rx_antennas * num_tx_antennas, sizeof(struct complexf *)); + } + storage->current_buffer = 0; +} + +static taps_storage_t taps_storage; + +void *client_thread_func(void *args) +{ + client_thread_args_t *client_thread_args = (client_thread_args_t *)args; + while (should_run) { + int next_buffer = (taps_storage.current_buffer + 1) % NUM_TAPS_BUFFERS; + taps_buffer_t *taps_buffer = &taps_storage.taps_buffers[next_buffer]; + int ret = nn_recv(client_thread_args->sock, taps_buffer->taps_msg, MAX_TAPS_MSG_SIZE, 0); + if (ret < 0) { + LOG_E(HW, "nn_recv() failed: errno: %d, %s\n", errno, strerror(errno)); + continue; + } + auto taps_message = Phy::GetTaps(taps_buffer->taps_msg); + if (taps_message->num_rx_antennas() != client_thread_args->num_rx_antennas + || taps_message->num_tx_antennas() != client_thread_args->num_tx_antennas) { + LOG_E(HW, + "Number of antennas mismatch: expected %d x %d, got %d x %d\n", + client_thread_args->num_rx_antennas, + client_thread_args->num_tx_antennas, + taps_message->num_rx_antennas(), + taps_message->num_tx_antennas()); + continue; + } + channel_desc_t *channel_desc = taps_buffer->channel_desc; + const flatbuffers::Vector *taps = taps_message->taps(); + struct complexf *base_pointer = (struct complexf *)taps->data(); + int taps_len = taps_message->taps_len(); + AssertFatal(taps_len < MAX_TAPS_LEN, "Too many samples in the taps array, taps_len = %d\n", taps_len); + for (unsigned int aarx = 0U; aarx < client_thread_args->num_rx_antennas; aarx++) { + for (unsigned int aatx = 0U; aatx < client_thread_args->num_tx_antennas; aatx++) { + channel_desc->ch_ps[aarx + (client_thread_args->num_rx_antennas * aatx)] = + &base_pointer[(aarx + (client_thread_args->num_rx_antennas * aatx)) * taps_len]; + } + } + channel_desc->path_loss_dB = 0; + channel_desc->channel_length = taps_len; + *client_thread_args->channel_desc = channel_desc; + taps_storage.current_buffer = next_buffer; + LOG_A(HW, "Receved new taps message, channel_length %d, buffer %d\n", channel_desc->channel_length, next_buffer); + for (unsigned int aarx = 0; aarx < client_thread_args->num_rx_antennas; aarx++) { + for (unsigned int aatx = 0; aatx < client_thread_args->num_tx_antennas; aatx++) { + char buffer[MAX_TAPS_LEN + 1]; + memset(buffer, 0, sizeof(buffer)); + float magnitudes[MAX_TAPS_LEN]; + cf_t *channel = channel_desc->ch_ps[aarx + (client_thread_args->num_rx_antennas * aatx)]; + for (int i = 0; i < channel_desc->channel_length; i++) { + magnitudes[i] = sqrtf(powf(channel[i].r, 2) + powf(channel[i].i, 2)); + } + ascii_line_plot(magnitudes, channel_desc->channel_length, buffer); + LOG_A(HW, "Taps message %d, channel %d x %d: %s\n", client_thread_args->id, aarx, aatx, buffer); + } + } + } + return NULL; +} + +extern "C" void taps_client_connect(int id, + const char *socket_path, + int num_tx_antennas, + int num_rx_antennas, + channel_desc_t **channel_desc) +{ + // Create a socket + int sock = nn_socket(AF_SP, NN_SUB); + AssertFatal(sock >= 0, "nn_socket() failed: errno: %d, %s\n", errno, strerror(errno)); + + int ret = nn_connect(sock, socket_path); + AssertFatal(ret >= 0, "nn_connect() failed: errno: %d, %s\n", errno, strerror(errno)); + + // Subscribe to all messages + ret = nn_setsockopt(sock, NN_SUB, NN_SUB_SUBSCRIBE, "", 0); + AssertFatal(ret == 0, "nn_setsockopt() failed, errno %d, %s\n", errno, strerror(errno)); + + init_taps_storage(&taps_storage, num_tx_antennas, num_rx_antennas); + + client_thread_args_t *client_thread_args = static_cast(malloc(sizeof(client_thread_args_t))); + client_thread_args->id = id; + client_thread_args->sock = sock; + client_thread_args->num_rx_antennas = num_rx_antennas; + client_thread_args->num_tx_antennas = num_tx_antennas; + client_thread_args->channel_desc = channel_desc; + ret = pthread_create(&client_thread, NULL, client_thread_func, client_thread_args); + AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno)); +} + +extern "C" void taps_client_stop() +{ + should_run = false; + pthread_join(client_thread, NULL); + for (int i = 0; i < NUM_TAPS_BUFFERS; i++) { + free(taps_storage.taps_buffers[i].taps_msg); + free(taps_storage.taps_buffers[i].channel_desc->ch_ps); + free(taps_storage.taps_buffers[i].channel_desc); + } +} diff --git a/radio/vrtsim/taps_client.h b/radio/vrtsim/taps_client.h new file mode 100644 index 0000000000..c0a577c787 --- /dev/null +++ b/radio/vrtsim/taps_client.h @@ -0,0 +1,38 @@ +/* + * 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 + */ + +#ifndef TAPS_CLIENT_H +#define TAPS_CLIENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sim.h" + +void taps_client_connect(int id, const char *socket_path, int num_tx_antennas, int num_rx_antennas, channel_desc_t **channel_desc); +void taps_client_stop(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index 0e602b3686..9d498d450b 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -47,6 +47,7 @@ #include "actor.h" #include "noise_device.h" #include "simde/x86/avx512.h" +#include "taps_client.h" // Simulator role typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; @@ -60,6 +61,7 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; #define VRTSIM_SECTION "vrtsim" #define TIME_SCALE_HLP \ "sample time scale. 1.0 means realtime. Values > 1 mean faster than realtime. Values < 1 mean slower than realtime\n" +#define TAPS_SOCKET_HLP "Socket to connect to the channel emulation server\n" // clang-format off #define VRTSIM_PARAMS_DESC \ @@ -68,6 +70,7 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; {"role", "either client or server\n", 0, .strptr = &role, .defstrval = ROLE_CLIENT_STRING, TYPE_STRING, 0}, \ {"timescale", TIME_SCALE_HLP, 0, .dblptr = &vrtsim_state->timescale, .defdblval = 1.0, TYPE_DOUBLE, 0}, \ {"chanmod", "Enable channel modelling", 0, .iptr = &vrtsim_state->chanmod, .defintval = 0, TYPE_INT, 0}, \ + {"taps-socket", TAPS_SOCKET_HLP, 0, .strptr = &vrtsim_state->taps_socket, .defstrval = NULL, TYPE_STRING, 0}, \ }; // clang-format on @@ -112,6 +115,7 @@ typedef struct { int rx_num_channels; channel_desc_t *channel_desc; Actor_t *channel_modelling_actors; + char *taps_socket; } vrtsim_state_t; // Sample history for channel impulse response @@ -183,6 +187,15 @@ static void vrtsim_readconfig(vrtsim_state_t *vrtsim_state) } else { AssertFatal(false, "Invalid role configuration\n"); } +#ifdef ENABLE_TAPS_CLIENT + if (vrtsim_state->taps_socket) { + LOG_A(HW, "VRTSIM: will use taps socket %s\n", vrtsim_state->taps_socket); + } +#else + if (vrtsim_state->taps_socket) { + AssertFatal(false, "Invalid configuration: Build with ENABLE_TAPS_CLIENT to use taps socket\n"); + } +#endif } static void *vrtsim_timing_job(void *arg) @@ -326,12 +339,20 @@ static int vrtsim_connect(openair0_device *device) // Handle channel modelling after number of RX antennas are known int num_tx_stats = 1; - if (vrtsim_state->chanmod) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { vrtsim_state->channel_modelling_actors = calloc_or_fail(vrtsim_state->peer_info.num_rx_antennas, sizeof(Actor_t)); for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) { init_actor(&vrtsim_state->channel_modelling_actors[i], "chanmod", -1); } - load_channel_model(vrtsim_state); + if (vrtsim_state->taps_socket) { + taps_client_connect(0, + vrtsim_state->taps_socket, + device->openair0_cfg[0].tx_num_channels, + vrtsim_state->peer_info.num_rx_antennas, + &vrtsim_state->channel_desc); + } else { + load_channel_model(vrtsim_state); + } num_tx_stats = vrtsim_state->peer_info.num_rx_antennas; } vrtsim_state->tx_timing = calloc_or_fail(num_tx_stats, sizeof(tx_timing_t)); @@ -401,6 +422,7 @@ typedef struct { static void perform_channel_modelling(void *arg) { channel_modelling_args_t *channel_modelling_args = arg; + vrtsim_state_t *vrtsim_state = channel_modelling_args->vrtsim_state; int nsamps = channel_modelling_args->nsamps; int aarx = channel_modelling_args->aarx; int nb_tx_ant = channel_modelling_args->nbAnt; @@ -411,23 +433,36 @@ static void perform_channel_modelling(void *arg) // Apply noise from global settings get_noise_vector((float *)samples, nsamps * 2); - channel_desc_t *channel_desc = channel_modelling_args->vrtsim_state->channel_desc; - const float pathloss_linear = powf(10, channel_desc->path_loss_dB / 20.0); + channel_desc_t *channel_desc = vrtsim_state->channel_desc; + + if (channel_desc == NULL) { + return; + } - // Convert channel impulse response to float + apply pathloss cf_t channel_impulse_response[nb_tx_ant][channel_desc->channel_length]; - for (int aatx = 0; aatx < nb_tx_ant; aatx++) { - const struct complexd *channelModel = channel_desc->ch[aarx + (aatx * channel_desc->nb_rx)]; - for (int i = 0; i < channel_desc->channel_length; i++) { - channel_impulse_response[aatx][i].r = channelModel[i].r * pathloss_linear; - channel_impulse_response[aatx][i].i = channelModel[i].i * pathloss_linear; + cf_t *channel_impulse_response_p[nb_tx_ant]; + if (!vrtsim_state->taps_socket) { + const float pathloss_linear = powf(10, channel_desc->path_loss_dB / 20.0); + // Convert channel impulse response to float + apply pathloss + for (int aatx = 0; aatx < nb_tx_ant; aatx++) { + const struct complexd *channelModel = channel_desc->ch[aarx + (aatx * channel_desc->nb_rx)]; + for (int i = 0; i < channel_desc->channel_length; i++) { + channel_impulse_response[aatx][i].r = channelModel[i].r * pathloss_linear; + channel_impulse_response[aatx][i].i = channelModel[i].i * pathloss_linear; + } + channel_impulse_response_p[aatx] = channel_impulse_response[aatx]; + } + } else { + for (int aatx = 0; aatx < nb_tx_ant; aatx++) { + struct complexf *channelModel = channel_desc->ch_ps[aarx + (aatx * channel_desc->nb_rx)]; + channel_impulse_response_p[aatx] = channelModel; } } for (int aatx = 0; aatx < nb_tx_ant; aatx++) { c16_t *previous_samples = saved_samples[aatx]; for (int i = 0; i < nsamps; i++) { - cf_t *impulse_response = channel_impulse_response[aatx]; + cf_t *impulse_response = channel_impulse_response_p[aatx]; for (int l = 0; l < channel_desc->channel_length; l++) { int idx = i - l; // TODO: Use AVX2 for this @@ -515,8 +550,9 @@ static int vrtsim_write(openair0_device *device, openair0_timestamp timestamp, v { timestamp -= device->openair0_cfg->command_line_sample_advance; vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; - return vrtsim_state->chanmod ? vrtsim_write_with_chanmod(vrtsim_state, timestamp, samplesVoid, nsamps, nbAnt, flags) - : vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[0], nsamps, 0, flags, 0); + bool channel_modelling = vrtsim_state->chanmod || vrtsim_state->taps_socket; + return channel_modelling ? vrtsim_write_with_chanmod(vrtsim_state, timestamp, samplesVoid, nsamps, nbAnt, flags) + : vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[0], nsamps, 0, flags, 0); } static int vrtsim_read(openair0_device *device, openair0_timestamp *ptimestamp, void **samplesVoid, int nsamps, int nbAnt) @@ -545,7 +581,7 @@ static void vrtsim_end(openair0_device *device) } tx_timing_t *tx_timing = vrtsim_state->tx_timing; - if (vrtsim_state->chanmod) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) { shutdown_actor(&vrtsim_state->channel_modelling_actors[i]); } @@ -559,6 +595,9 @@ static void vrtsim_end(openair0_device *device) } tx_timing->average_tx_budget /= vrtsim_state->peer_info.num_rx_antennas; free_noise_device(); + if (vrtsim_state->taps_socket) { + taps_client_stop(); + } } // produce 1 second of extra samples so threads can finish shm_td_iq_channel_produce_samples(vrtsim_state->channel, vrtsim_state->sample_rate); @@ -621,10 +660,11 @@ __attribute__((__visibility__("default"))) int device_init(openair0_device *devi vrtsim_state->tx_num_channels = openair0_cfg->tx_num_channels; vrtsim_state->rx_num_channels = openair0_cfg->rx_num_channels; - if (vrtsim_state->chanmod) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { init_channelmod(); int noise_power_dBFS = get_noise_power_dBFS(); int16_t noise_power = noise_power_dBFS == INVALID_DBFS_VALUE ? 0 : (int16_t)(32767.0 / powf(10.0, .05 * -noise_power_dBFS)); + LOG_A(HW, "VRTSIM: Noise power %d sample value\n", noise_power); init_noise_device(noise_power); } return 0; From fcea07ca715aae57efb7b600c44eeeeb1bad0b47 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Fri, 27 Jun 2025 10:46:53 +0200 Subject: [PATCH 31/49] Detect disconnecton & handle error states in vrtsim Add handling for error states in vrtsim which allows to close the softmodems with CTRL+C without hanging, improving user experience - Client detection of stale timer. - Abort state for client and server --- .../utils/shm_iq_channel/shm_td_iq_channel.c | 63 ++++++++++++++++--- .../utils/shm_iq_channel/shm_td_iq_channel.h | 19 +++++- .../tests/test_shm_td_iq_channel.c | 4 +- radio/vrtsim/vrtsim.c | 13 +++- 4 files changed, 85 insertions(+), 14 deletions(-) diff --git a/common/utils/shm_iq_channel/shm_td_iq_channel.c b/common/utils/shm_iq_channel/shm_td_iq_channel.c index 3920fe027f..1086979a75 100644 --- a/common/utils/shm_iq_channel/shm_td_iq_channel.c +++ b/common/utils/shm_iq_channel/shm_td_iq_channel.c @@ -52,6 +52,7 @@ typedef struct ShmTDIQChannel_s { char name[256]; sample_t *tx_iq_data; sample_t *rx_iq_data; + bool abort; } ShmTDIQChannel; ShmTDIQChannel *shm_td_iq_channel_create(const char *name, int num_tx_ant, int num_rx_ant) @@ -237,10 +238,11 @@ void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, size_t num_sampl mutexunlock(data->mutex); } -void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp) +void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t timeout_uS) { ShmTDIQChannelData *data = channel->data; if (data->is_connected == false) { + fprintf(stderr, "Error: Channel is not connected.\n"); abort(); return; } @@ -249,13 +251,41 @@ void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp) if (current_timestamp >= timestamp) { return; } - mutexlock(data->mutex); - while (current_timestamp < timestamp) { - condwait(data->cond, data->mutex); - current_timestamp = data->timestamp; + if (timeout_uS == 0) { + mutexlock(data->mutex); + while (current_timestamp < timestamp && !channel->abort) { + condwait(data->cond, data->mutex); + current_timestamp = data->timestamp; + } + mutexunlock(data->mutex); + } else { + struct timespec ts = {.tv_sec = 0, .tv_nsec = 0}; + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { + fprintf(stderr, "Error: clock_gettime failed: %s\n", strerror(errno)); + channel->abort = true; + return; + } + + ts.tv_sec += timeout_uS / 1000000; // Convert microseconds to seconds + ts.tv_nsec += (timeout_uS % 1000000) * 1000; // Convert remaining microseconds to nanoseconds + + mutexlock(data->mutex); + while (current_timestamp < timestamp && !channel->abort) { + int ret = pthread_cond_timedwait(&data->cond, &data->mutex, &ts); + if (ret == ETIMEDOUT) { + channel->abort = true; + fprintf(stderr, "Error: Timed out waiting for samples. Aborting vrtsim\n");\ + break; + } else if (ret != 0) { + channel->abort = true; + fprintf(stderr, "Error: pthread_cond_timedwait failed: %s\n", strerror(ret)); + break; + } else { + current_timestamp = data->timestamp; + } + } + mutexunlock(data->mutex); } - mutexunlock(data->mutex); - return; } uint64_t shm_td_iq_channel_get_current_sample(const ShmTDIQChannel *channel) @@ -269,15 +299,32 @@ bool shm_td_iq_channel_is_connected(const ShmTDIQChannel *channel) return channel->data->is_connected; } +void shm_td_iq_channel_abort(ShmTDIQChannel *channel) +{ + ShmTDIQChannelData *data = channel->data; + mutexlock(data->mutex); + channel->abort = true; + condbroadcast(data->cond); + mutexunlock(data->mutex); +} + +bool shm_td_iq_channel_is_aborted(const ShmTDIQChannel *channel) +{ + return channel->abort; +} + void shm_td_iq_channel_destroy(ShmTDIQChannel *channel) { ShmTDIQChannelData *data = channel->data; size_t tx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * data->num_antennas_tx; size_t rx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * data->num_antennas_rx; size_t total_size = sizeof(ShmTDIQChannelData) + tx_buffer_size + rx_buffer_size; - munmap(data, total_size); if (channel->type == IQ_CHANNEL_TYPE_SERVER) { + data->is_connected = false; + munmap(data, total_size); shm_unlink(channel->name); + } else { + munmap(data, total_size); } free(channel); } diff --git a/common/utils/shm_iq_channel/shm_td_iq_channel.h b/common/utils/shm_iq_channel/shm_td_iq_channel.h index 7c5a730914..30c7cd73f1 100644 --- a/common/utils/shm_iq_channel/shm_td_iq_channel.h +++ b/common/utils/shm_iq_channel/shm_td_iq_channel.h @@ -118,8 +118,10 @@ void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, uint64_t num_sam * @brief Wait until sample at the specified timestamp is available * * @param channel The ShmTDIQChannel structure. + * @param timestamp The timestamp for which to wait. + * @param timeout_uS The timeout in microseconds to wait for the sample. 0 means wait indefinitely. */ -void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp); +void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t timeout_uS); /** * @brief Checks if the IQ channel is connected. @@ -129,6 +131,21 @@ void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp); */ bool shm_td_iq_channel_is_connected(const ShmTDIQChannel *channel); +/** + * @brief Aborts the IQ channel causing the wait to return immediately + * + * @param channel The ShmTDIQChannel structure. + */ +void shm_td_iq_channel_abort(ShmTDIQChannel *channel); + +/** + * @brief Checks if the IQ channel is aborted. + * + * @param channel The ShmTDIQChannel structure. + * @return True if the channel is aborted, false otherwise. + */ +bool shm_td_iq_channel_is_aborted(const ShmTDIQChannel *channel); + /** * @brief Destroys the shared memory IQ channel. * diff --git a/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c b/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c index f5170b8144..351bdd678d 100644 --- a/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c +++ b/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c @@ -104,7 +104,7 @@ void server(void) uint64_t timestamp = 0; int iq_contents = 0; while (timestamp < num_samples_per_update * num_updates) { - shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update); + shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update, 0); uint64_t target_timestamp = timestamp + num_samples_per_update; timestamp += num_samples_per_update; uint32_t iq_data[num_samples_per_update]; @@ -141,7 +141,7 @@ int client(void) int iq_contents = 0; while (timestamp < num_samples_per_update * num_updates) { - shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update); + shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update, 0); // Server starts producing from second slot if (timestamp > num_samples_per_update) { uint64_t target_timestamp = timestamp - num_samples_per_update; diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index 9d498d450b..7fb85589ea 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -558,7 +558,15 @@ static int vrtsim_write(openair0_device *device, openair0_timestamp timestamp, v static int vrtsim_read(openair0_device *device, openair0_timestamp *ptimestamp, void **samplesVoid, int nsamps, int nbAnt) { vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; - shm_td_iq_channel_wait(vrtsim_state->channel, vrtsim_state->last_received_sample + nsamps); + if (shm_td_iq_channel_is_aborted(vrtsim_state->channel)) { + LOG_E(HW, "Channel is aborted, returning void samples\n"); + for (int i = 0; i < nbAnt; i++) { + memset(samplesVoid[i], 0, sizeof(c16_t) * nsamps); + } + return nsamps; + } + uint64_t timeout_uS = 2 * 1000 * 1000 / vrtsim_state->timescale; // 2 seconds in uS + shm_td_iq_channel_wait(vrtsim_state->channel, vrtsim_state->last_received_sample + nsamps, timeout_uS); int ret = shm_td_iq_channel_rx(vrtsim_state->channel, vrtsim_state->last_received_sample, nsamps, 0, samplesVoid[0]); if (ret == CHANNEL_ERROR_TOO_LATE) { vrtsim_state->rx_samples_late += nsamps; @@ -599,8 +607,7 @@ static void vrtsim_end(openair0_device *device) taps_client_stop(); } } - // produce 1 second of extra samples so threads can finish - shm_td_iq_channel_produce_samples(vrtsim_state->channel, vrtsim_state->sample_rate); + shm_td_iq_channel_abort(vrtsim_state->channel); sleep(1); shm_td_iq_channel_destroy(vrtsim_state->channel); From acba82b874930b00a623f14b3f34c3a528f10592 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Thu, 7 Aug 2025 14:03:05 +0200 Subject: [PATCH 32/49] Simplify shm td iq channel implementation Move the channel state management outside the channel implementation. This simplifies the channel implementation but forces users of the channel to manage it's state manually. --- .../utils/shm_iq_channel/shm_td_iq_channel.c | 27 ------------------- .../utils/shm_iq_channel/shm_td_iq_channel.h | 8 ------ .../tests/test_shm_td_iq_channel.c | 18 ------------- radio/vrtsim/vrtsim.c | 4 --- 4 files changed, 57 deletions(-) diff --git a/common/utils/shm_iq_channel/shm_td_iq_channel.c b/common/utils/shm_iq_channel/shm_td_iq_channel.c index 1086979a75..126c386be9 100644 --- a/common/utils/shm_iq_channel/shm_td_iq_channel.c +++ b/common/utils/shm_iq_channel/shm_td_iq_channel.c @@ -40,7 +40,6 @@ typedef struct { int num_antennas_tx; int num_antennas_rx; uint64_t timestamp; - bool is_connected; pthread_mutex_t mutex; pthread_cond_t cond; } ShmTDIQChannelData; @@ -48,7 +47,6 @@ typedef struct { typedef struct ShmTDIQChannel_s { IQChannelType type; ShmTDIQChannelData *data; - uint64_t last_timestamp; char name[256]; sample_t *tx_iq_data; sample_t *rx_iq_data; @@ -76,14 +74,12 @@ ShmTDIQChannel *shm_td_iq_channel_create(const char *name, int num_tx_ant, int n memset(shm_ptr, 0, total_size); shm_ptr->num_antennas_tx = num_tx_ant; shm_ptr->num_antennas_rx = num_rx_ant; - shm_ptr->is_connected = false; ShmTDIQChannel *channel = calloc_or_fail(1, sizeof(ShmTDIQChannel)); strncpy(channel->name, name, sizeof(channel->name) - 1); channel->tx_iq_data = (sample_t *)(shm_ptr + 1); channel->rx_iq_data = channel->tx_iq_data + tx_buffer_size / sizeof(sample_t); channel->data = shm_ptr; channel->type = IQ_CHANNEL_TYPE_SERVER; - channel->last_timestamp = 0; pthread_mutexattr_t mutex_attr; pthread_condattr_t cond_attr; int ret = pthread_mutexattr_init(&mutex_attr); @@ -138,12 +134,10 @@ ShmTDIQChannel *shm_td_iq_channel_connect(const char *name, int timeout_in_secon size_t tx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * channel->data->num_antennas_tx; channel->rx_iq_data = channel->tx_iq_data + tx_buffer_size / sizeof(sample_t); channel->type = IQ_CHANNEL_TYPE_CLIENT; - channel->last_timestamp = 0; while (shm_ptr->magic != SHM_MAGIC_NUMBER) { printf("Waiting for server to initialize shared memory\n"); sleep(1); } - shm_ptr->is_connected = true; close(fd); return channel; } @@ -155,9 +149,6 @@ IQChannelErrorType shm_td_iq_channel_tx(ShmTDIQChannel *channel, const sample_t *tx_iq_data) { ShmTDIQChannelData *data = channel->data; - if (data->is_connected == false) { - return CHANNEL_ERROR_NOT_CONNECTED; - } // timestamp in the past uint64_t current_time = data->timestamp; if (timestamp < current_time) { @@ -194,9 +185,6 @@ IQChannelErrorType shm_td_iq_channel_rx(ShmTDIQChannel *channel, sample_t *tx_iq_data) { ShmTDIQChannelData *data = channel->data; - if (data->is_connected == false) { - return CHANNEL_ERROR_NOT_CONNECTED; - } // timestamp in the future uint64_t current_time = data->timestamp; if (timestamp > current_time) { @@ -229,9 +217,6 @@ IQChannelErrorType shm_td_iq_channel_rx(ShmTDIQChannel *channel, void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, size_t num_samples) { ShmTDIQChannelData *data = channel->data; - if (data->is_connected == false) { - return; - } mutexlock(data->mutex); data->timestamp += num_samples; condbroadcast(data->cond); @@ -241,12 +226,6 @@ void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, size_t num_sampl void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t timeout_uS) { ShmTDIQChannelData *data = channel->data; - if (data->is_connected == false) { - fprintf(stderr, "Error: Channel is not connected.\n"); - abort(); - return; - } - size_t current_timestamp = data->timestamp; if (current_timestamp >= timestamp) { return; @@ -294,11 +273,6 @@ uint64_t shm_td_iq_channel_get_current_sample(const ShmTDIQChannel *channel) return data->timestamp; } -bool shm_td_iq_channel_is_connected(const ShmTDIQChannel *channel) -{ - return channel->data->is_connected; -} - void shm_td_iq_channel_abort(ShmTDIQChannel *channel) { ShmTDIQChannelData *data = channel->data; @@ -320,7 +294,6 @@ void shm_td_iq_channel_destroy(ShmTDIQChannel *channel) size_t rx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * data->num_antennas_rx; size_t total_size = sizeof(ShmTDIQChannelData) + tx_buffer_size + rx_buffer_size; if (channel->type == IQ_CHANNEL_TYPE_SERVER) { - data->is_connected = false; munmap(data, total_size); shm_unlink(channel->name); } else { diff --git a/common/utils/shm_iq_channel/shm_td_iq_channel.h b/common/utils/shm_iq_channel/shm_td_iq_channel.h index 30c7cd73f1..8c7bfad318 100644 --- a/common/utils/shm_iq_channel/shm_td_iq_channel.h +++ b/common/utils/shm_iq_channel/shm_td_iq_channel.h @@ -123,14 +123,6 @@ void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, uint64_t num_sam */ void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t timeout_uS); -/** - * @brief Checks if the IQ channel is connected. - * - * @param channel The ShmTDIQChannel structure. - * @return True if the channel is connected, false otherwise. - */ -bool shm_td_iq_channel_is_connected(const ShmTDIQChannel *channel); - /** * @brief Aborts the IQ channel causing the wait to return immediately * diff --git a/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c b/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c index 351bdd678d..05140a0f63 100644 --- a/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c +++ b/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c @@ -87,15 +87,6 @@ void server(void) int num_ant_tx = 1; int num_ant_rx = 1; ShmTDIQChannel *channel = shm_td_iq_channel_create(SHM_CHANNEL_NAME, num_ant_tx, num_ant_rx); - for (int i = 0; i < 10; i++) { - if (shm_td_iq_channel_is_connected(channel)) { - printf("Server connected\n"); - break; - } - printf("Waiting for client\n"); - sleep(1); - } - AssertFatal(shm_td_iq_channel_is_connected(channel), "Server failed to connect\n"); pthread_t producer_thread; int ret = pthread_create(&producer_thread, NULL, produce_symbols, channel); @@ -127,15 +118,6 @@ int client(void) { int total_errors = 0; ShmTDIQChannel *channel = shm_td_iq_channel_connect(SHM_CHANNEL_NAME, 10); - for (int i = 0; i < 10; i++) { - if (shm_td_iq_channel_is_connected(channel)) { - printf("Client connected\n"); - break; - } - printf("Waiting for server\n"); - sleep(1); - } - AssertFatal(shm_td_iq_channel_is_connected(channel), "Client failed to connect\n"); uint64_t timestamp = 0; int iq_contents = 0; diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index 7fb85589ea..c668bac64c 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -368,10 +368,6 @@ static int vrtsim_connect(openair0_device *device) vrtsim_state->peer_info.num_rx_antennas, device->openair0_cfg[0].rx_num_channels); vrtsim_state->run_timing_thread = true; - while (!shm_td_iq_channel_is_connected(vrtsim_state->channel)) { - LOG_I(HW, "Waiting for client\n"); - sleep(1); - } int ret = pthread_create(&vrtsim_state->timing_thread, NULL, vrtsim_timing_job, vrtsim_state); AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno)); } else { From fc28e2d3fc6220325e35a1b9d4c4cae8892c7ec3 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Thu, 7 Aug 2025 14:05:06 +0200 Subject: [PATCH 33/49] Make vrtsim output to stdout more readable. --- radio/vrtsim/vrtsim.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index c668bac64c..f5d2410bf1 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -555,7 +555,6 @@ static int vrtsim_read(openair0_device *device, openair0_timestamp *ptimestamp, { vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; if (shm_td_iq_channel_is_aborted(vrtsim_state->channel)) { - LOG_E(HW, "Channel is aborted, returning void samples\n"); for (int i = 0; i < nbAnt; i++) { memset(samplesVoid[i], 0, sizeof(c16_t) * nsamps); } @@ -615,7 +614,7 @@ static void vrtsim_end(openair0_device *device) "VRTSIM: Read/write too early (suspected radio implementaton error) TX: %lu, RX: %lu\n", tx_timing->tx_early, vrtsim_state->rx_early); - LOG_I(HW, "VRTSIM: Average TX budget %.3lf uS\n", tx_timing->average_tx_budget); + LOG_I(HW, "VRTSIM: Average TX budget %.3lf uS (more is better)\n", tx_timing->average_tx_budget); histogram_print(&tx_timing->tx_histogram); free(vrtsim_state->tx_timing); } From 54fdd3547bc8125721b3b0520a7b433f614d7625 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Fri, 8 Aug 2025 16:51:22 +0200 Subject: [PATCH 34/49] Clean exit of vrtsim client if server is stopped first Force exit in case current sample is not modified by the server with 2s. --- .../utils/shm_iq_channel/shm_td_iq_channel.c | 18 +++++----- .../utils/shm_iq_channel/shm_td_iq_channel.h | 4 ++- radio/vrtsim/vrtsim.c | 35 +++++++++++++++---- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/common/utils/shm_iq_channel/shm_td_iq_channel.c b/common/utils/shm_iq_channel/shm_td_iq_channel.c index 126c386be9..ecebbaf494 100644 --- a/common/utils/shm_iq_channel/shm_td_iq_channel.c +++ b/common/utils/shm_iq_channel/shm_td_iq_channel.c @@ -223,12 +223,12 @@ void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, size_t num_sampl mutexunlock(data->mutex); } -void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t timeout_uS) +int shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t timeout_uS) { ShmTDIQChannelData *data = channel->data; size_t current_timestamp = data->timestamp; if (current_timestamp >= timestamp) { - return; + return 0; } if (timeout_uS == 0) { mutexlock(data->mutex); @@ -241,8 +241,7 @@ void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_ struct timespec ts = {.tv_sec = 0, .tv_nsec = 0}; if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { fprintf(stderr, "Error: clock_gettime failed: %s\n", strerror(errno)); - channel->abort = true; - return; + return 1; } ts.tv_sec += timeout_uS / 1000000; // Convert microseconds to seconds @@ -252,19 +251,20 @@ void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_ while (current_timestamp < timestamp && !channel->abort) { int ret = pthread_cond_timedwait(&data->cond, &data->mutex, &ts); if (ret == ETIMEDOUT) { - channel->abort = true; - fprintf(stderr, "Error: Timed out waiting for samples. Aborting vrtsim\n");\ - break; + fprintf(stderr, "Error: Timed out waiting for samples.\n"); + mutexunlock(data->mutex); + return 1; } else if (ret != 0) { - channel->abort = true; fprintf(stderr, "Error: pthread_cond_timedwait failed: %s\n", strerror(ret)); - break; + mutexunlock(data->mutex); + return 1; } else { current_timestamp = data->timestamp; } } mutexunlock(data->mutex); } + return 0; } uint64_t shm_td_iq_channel_get_current_sample(const ShmTDIQChannel *channel) diff --git a/common/utils/shm_iq_channel/shm_td_iq_channel.h b/common/utils/shm_iq_channel/shm_td_iq_channel.h index 8c7bfad318..b71720c1f9 100644 --- a/common/utils/shm_iq_channel/shm_td_iq_channel.h +++ b/common/utils/shm_iq_channel/shm_td_iq_channel.h @@ -120,8 +120,10 @@ void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, uint64_t num_sam * @param channel The ShmTDIQChannel structure. * @param timestamp The timestamp for which to wait. * @param timeout_uS The timeout in microseconds to wait for the sample. 0 means wait indefinitely. + * + * @return 0 if the sample is available, 1 if timed out */ -void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t timeout_uS); +int shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t timeout_uS); /** * @brief Aborts the IQ channel causing the wait to return immediately diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index f5d2410bf1..97c820a859 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -223,6 +223,7 @@ static void *vrtsim_timing_job(void *arg) samples_to_produce += 1; leftover_samples -= 1; } + AssertFatal(samples_to_produce >= 0, "Negative samples to produce: %f\n", samples_to_produce); shm_td_iq_channel_produce_samples(vrtsim_state->channel, samples_to_produce); usleep(1); } @@ -544,6 +545,12 @@ static int vrtsim_write_with_chanmod(vrtsim_state_t *vrtsim_state, static int vrtsim_write(openair0_device *device, openair0_timestamp timestamp, void **samplesVoid, int nsamps, int nbAnt, int flags) { + AssertFatal(nsamps > 0, "Number of samples must be greater than 0\n"); + AssertFatal(nbAnt > 0 && nbAnt <= MAX_NUM_ANTENNAS_TX, + "Number of antennas %d must be between 1 and %d\n", + nbAnt, + MAX_NUM_ANTENNAS_TX); + AssertFatal(timestamp >= 0, "Timestamp must be non-negative, got %ld\n", timestamp); timestamp -= device->openair0_cfg->command_line_sample_advance; vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; bool channel_modelling = vrtsim_state->chanmod || vrtsim_state->taps_socket; @@ -555,13 +562,29 @@ static int vrtsim_read(openair0_device *device, openair0_timestamp *ptimestamp, { vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; if (shm_td_iq_channel_is_aborted(vrtsim_state->channel)) { - for (int i = 0; i < nbAnt; i++) { - memset(samplesVoid[i], 0, sizeof(c16_t) * nsamps); - } - return nsamps; + return 0; } - uint64_t timeout_uS = 2 * 1000 * 1000 / vrtsim_state->timescale; // 2 seconds in uS - shm_td_iq_channel_wait(vrtsim_state->channel, vrtsim_state->last_received_sample + nsamps, timeout_uS); + if (vrtsim_state->role == ROLE_SERVER) { + uint64_t timeout_uS = 0; // 0 means no timeout + shm_td_iq_channel_wait(vrtsim_state->channel, vrtsim_state->last_received_sample + nsamps, timeout_uS); + } else { + uint64_t start_sample = shm_td_iq_channel_get_current_sample(vrtsim_state->channel); + uint64_t timeout_uS = 2 * 1000 * 1000; // 2 seconds timeout waiting for sample number to change + // + while (shm_td_iq_channel_wait(vrtsim_state->channel, vrtsim_state->last_received_sample + nsamps, timeout_uS) == 1) { + uint64_t sample = shm_td_iq_channel_get_current_sample(vrtsim_state->channel); + if (sample == start_sample) { + LOG_E(HW, + "VRTSIM: Read timeout waiting for sample %lu to change, aborting channel\n", + vrtsim_state->last_received_sample + nsamps); + shm_td_iq_channel_abort(vrtsim_state->channel); + break; + } else { + start_sample = sample; + } + } + } + int ret = shm_td_iq_channel_rx(vrtsim_state->channel, vrtsim_state->last_received_sample, nsamps, 0, samplesVoid[0]); if (ret == CHANNEL_ERROR_TOO_LATE) { vrtsim_state->rx_samples_late += nsamps; From ea3a4b991c33be95c19b8e3e0b978650d8e677a0 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Fri, 8 Aug 2025 17:24:40 +0200 Subject: [PATCH 35/49] VRTSIM: allow client reconnection via a connection descriptor file Replace the unwieldy unix socket implementation of peer information exchange with an authorative connection descriptor file that informs the client of the connection method an number of antennas on the server. This allows the server to run without the client and allows the client to reconnect as long as the server is running, as many times as needed. --- radio/vrtsim/vrtsim.c | 186 ++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 108 deletions(-) diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index 97c820a859..9e678a5305 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -62,15 +63,20 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; #define TIME_SCALE_HLP \ "sample time scale. 1.0 means realtime. Values > 1 mean faster than realtime. Values < 1 mean slower than realtime\n" #define TAPS_SOCKET_HLP "Socket to connect to the channel emulation server\n" +#define CLIENT_NUM_RX_HLP "Number of RX antennas of the client, specified on the server\n" +#define CONNECTION_DESCRIPTOR_HLP "Path to the file written by the server that the client can use to connect." +#define DEFAULT_CHANNEL_NAME "vrtsim_channel" +#define DEFAULT_DESCRIPTOR "/tmp/vrtsim_connection" // clang-format off #define VRTSIM_PARAMS_DESC \ { \ - {"channel_name", "shared memory channel name\n", 0, .strptr = &channel_name, .defstrval = "vrtsim_channel", TYPE_STRING, 0}, \ - {"role", "either client or server\n", 0, .strptr = &role, .defstrval = ROLE_CLIENT_STRING, TYPE_STRING, 0}, \ - {"timescale", TIME_SCALE_HLP, 0, .dblptr = &vrtsim_state->timescale, .defdblval = 1.0, TYPE_DOUBLE, 0}, \ - {"chanmod", "Enable channel modelling", 0, .iptr = &vrtsim_state->chanmod, .defintval = 0, TYPE_INT, 0}, \ - {"taps-socket", TAPS_SOCKET_HLP, 0, .strptr = &vrtsim_state->taps_socket, .defstrval = NULL, TYPE_STRING, 0}, \ + {"connection_descriptor", CONNECTION_DESCRIPTOR_HLP, 0, .strptr = &vrtsim_state->connection_descriptor, .defstrval = DEFAULT_DESCRIPTOR, TYPE_STRING, 0}, \ + {"role", "either client or server\n", 0, .strptr = &role, .defstrval = ROLE_CLIENT_STRING, TYPE_STRING, 0}, \ + {"timescale", TIME_SCALE_HLP, 0, .dblptr = &vrtsim_state->timescale, .defdblval = 1.0, TYPE_DOUBLE, 0}, \ + {"chanmod", "Enable channel modelling", 0, .iptr = &vrtsim_state->chanmod, .defintval = 0, TYPE_INT, 0}, \ + {"taps-socket", TAPS_SOCKET_HLP, 0, .strptr = &vrtsim_state->taps_socket, .defstrval = NULL, TYPE_STRING, 0}, \ + {"client-num-rx-antennas", CLIENT_NUM_RX_HLP, 0, .iptr = &vrtsim_state->client_num_rx_antennas, .defintval = 1, TYPE_INT, 0}, \ }; // clang-format on @@ -96,7 +102,7 @@ typedef struct tx_timing_s { typedef struct { int role; - char channel_name[40]; + char *connection_descriptor; ShmTDIQChannel *channel; uint64_t last_received_sample; pthread_t timing_thread; @@ -116,6 +122,7 @@ typedef struct { channel_desc_t *channel_desc; Actor_t *channel_modelling_actors; char *taps_socket; + int client_num_rx_antennas; } vrtsim_state_t; // Sample history for channel impulse response @@ -174,12 +181,10 @@ static void load_channel_model(vrtsim_state_t *vrtsim_state) static void vrtsim_readconfig(vrtsim_state_t *vrtsim_state) { - char *channel_name = NULL; char *role = NULL; paramdef_t vrtsim_params[] = VRTSIM_PARAMS_DESC; int ret = config_get(config_get_if(), vrtsim_params, sizeofArray(vrtsim_params), VRTSIM_SECTION); AssertFatal(ret >= 0, "configuration couldn't be performed\n"); - strncpy(vrtsim_state->channel_name, channel_name, sizeof(vrtsim_state->channel_name) - 1); if (strncmp(role, ROLE_CLIENT_STRING, strlen(ROLE_CLIENT_STRING)) == 0) { vrtsim_state->role = ROLE_CLIENT; } else if (strncmp(role, ROLE_SERVER_STRING, strlen(ROLE_SERVER_STRING)) == 0) { @@ -230,112 +235,80 @@ static void *vrtsim_timing_job(void *arg) return 0; } +typedef struct client_info_s { + int server_num_rx_antennas; + int client_num_rx_antennas; +} client_info_t; + /** - * @brief Exchanges peer information between the server and the client. + * @brief Publishes the client information information to a file for the client to read. * - * This function sets up a Unix domain socket server, waits for a client to connect, - * and exchanges peer information (number of RX antennas) with the client. + * The server writes its client_info (number of RX antennas) to a file, which the client reads. + * The server does not wait for the client to write back; the client can connect at any point. * - * @param peer_info The peer information to send to the client. - * @return The peer information received from the client. - * - * @note This function will block until a client connects and the information is exchanged. + * @param client_info The client information to publish. + * @return The peer information (same as input, server is authoritative). */ -static peer_info_t server_exchange_peer_info(peer_info_t peer_info) +static void server_publish_client_info(client_info_t client_info, char *descriptor_file) { - int server_fd, client_fd; - - // Create a Unix domain socket - server_fd = socket(AF_UNIX, SOCK_STREAM, 0); - AssertFatal(server_fd >= 0, "socket() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Bind the socket to a file path - struct sockaddr_un addr = {.sun_family = AF_UNIX}; - strncpy(addr.sun_path, "/tmp/vrtsim_socket", sizeof(addr.sun_path) - 1); - unlink(addr.sun_path); // Ensure the path does not already exist - int ret = bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)); - AssertFatal(ret == 0, "bind() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Listen for incoming connections - ret = listen(server_fd, 1); - AssertFatal(ret == 0, "listen() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Accept a connection from a client - socklen_t addr_len = sizeof(addr); - client_fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); - AssertFatal(client_fd >= 0, "accept() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Send the antenna info to the server - ssize_t bytes_sent = send(client_fd, &peer_info, sizeof(peer_info), 0); - AssertFatal(bytes_sent == sizeof(peer_info), "send() failed: errno: %d, %s\n", errno, strerror(errno)); - - ssize_t bytes_received = recv(client_fd, &peer_info, sizeof(peer_info), 0); - AssertFatal(bytes_received == sizeof(peer_info), "recv() failed: errno: %d, %s\n", errno, strerror(errno)); - - LOG_I(HW, "Received client info: RX %d\n", peer_info.num_rx_antennas); - - // Close the client and server sockets - close(client_fd); - close(server_fd); - return peer_info; + FILE *fp = fopen(descriptor_file, "wb"); + AssertFatal(fp != NULL, "Failed to open client info file for writing: %s\n", strerror(errno)); + size_t written = fwrite(&client_info, sizeof(client_info), 1, fp); + AssertFatal(written == 1, "Failed to write client info to file\n"); + fclose(fp); } -/** - * @brief Exchanges peer information between the client and the server. - * - * This function connects to a Unix domain socket server, exchanges peer information - * (number of RX antennas) with the server, and returns the peer information received - * from the server. - * - * @param peer_info The peer information to send to the server. - * @return The peer information received from the server. - * - * @note This function will block until the information is exchanged. - */ -static peer_info_t client_exchange_peer_info(peer_info_t peer_info) +static client_info_t client_read_info(char *descriptor_file) { - int client_fd; - - // Create a Unix domain socket - client_fd = socket(AF_UNIX, SOCK_STREAM, 0); - AssertFatal(client_fd >= 0, "socket() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Connect to the server - struct sockaddr_un addr = {.sun_family = AF_UNIX}; - strncpy(addr.sun_path, "/tmp/vrtsim_socket", sizeof(addr.sun_path) - 1); - int ret = -1; + client_info_t client_info; int tries = 0; - while (ret != 0 && tries < 10) { - ret = connect(client_fd, (struct sockaddr *)&addr, sizeof(addr)); - tries++; + while (tries < 10) { + FILE *fp = fopen(descriptor_file, "rb"); + if (fp) { + size_t read = fread(&client_info, sizeof(client_info), 1, fp); + fclose(fp); + if (read == 1) { + return client_info; + } + } sleep(1); + tries++; } - AssertFatal(ret == 0, "connect() failed: errno: %d, %s\n", errno, strerror(errno)); - - peer_info_t peer_info_received; - ssize_t bytes_received = recv(client_fd, &peer_info_received, sizeof(peer_info_received), 0); - AssertFatal(bytes_received == sizeof(peer_info_received), "recv() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Send the antenna info to the server - ssize_t bytes_sent = send(client_fd, &peer_info, sizeof(peer_info), 0); - AssertFatal(bytes_sent == sizeof(peer_info), "send() failed: errno: %d, %s\n", errno, strerror(errno)); - - LOG_I(HW, "Received peer info: RX %d\n", peer_info_received.num_rx_antennas); - - // Close the client socket - close(client_fd); - return peer_info_received; + AssertFatal(0, "Timeout waiting for client info\n"); + return client_info; } static int vrtsim_connect(openair0_device *device) { vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; - // Exchange peer info - peer_info_t peer_info = {.num_rx_antennas = device->openair0_cfg[0].rx_num_channels}; + + // Setup a shared memory channel if (vrtsim_state->role == ROLE_SERVER) { - vrtsim_state->peer_info = server_exchange_peer_info(peer_info); + vrtsim_state->peer_info.num_rx_antennas = vrtsim_state->client_num_rx_antennas; + vrtsim_state->channel = shm_td_iq_channel_create(DEFAULT_CHANNEL_NAME, + vrtsim_state->peer_info.num_rx_antennas, + device->openair0_cfg[0].rx_num_channels); + // Exchange peer info + client_info_t client_info = { + .server_num_rx_antennas = device->openair0_cfg[0].rx_num_channels, + .client_num_rx_antennas = vrtsim_state->client_num_rx_antennas, + }; + server_publish_client_info(client_info, vrtsim_state->connection_descriptor); + + vrtsim_state->run_timing_thread = true; + int ret = pthread_create(&vrtsim_state->timing_thread, NULL, vrtsim_timing_job, vrtsim_state); + AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno)); } else { - vrtsim_state->peer_info = client_exchange_peer_info(peer_info); + client_info_t client_info = client_read_info(vrtsim_state->connection_descriptor); + AssertFatal(client_info.server_num_rx_antennas > 0, "Server did not publish valid client info, aborting client connection\n"); + AssertFatal( + client_info.client_num_rx_antennas == device->openair0_cfg[0].rx_num_channels, + "Server expects different number of RX antennas. %d != %d. Use server command line option --client-num-rx-antennas\n", + client_info.client_num_rx_antennas, + device->openair0_cfg[0].rx_num_channels); + vrtsim_state->channel = shm_td_iq_channel_connect(DEFAULT_CHANNEL_NAME, 10); + vrtsim_state->peer_info.num_rx_antennas = client_info.server_num_rx_antennas; + vrtsim_state->last_received_sample = shm_td_iq_channel_get_current_sample(vrtsim_state->channel); } // Handle channel modelling after number of RX antennas are known @@ -363,18 +336,6 @@ static int vrtsim_connect(openair0_device *device) vrtsim_state->tx_timing[i].tx_histogram.range = 3000.0; } - // Setup a shared memory channel - if (vrtsim_state->role == ROLE_SERVER) { - vrtsim_state->channel = shm_td_iq_channel_create(vrtsim_state->channel_name, - vrtsim_state->peer_info.num_rx_antennas, - device->openair0_cfg[0].rx_num_channels); - vrtsim_state->run_timing_thread = true; - int ret = pthread_create(&vrtsim_state->timing_thread, NULL, vrtsim_timing_job, vrtsim_state); - AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno)); - } else { - vrtsim_state->channel = shm_td_iq_channel_connect(vrtsim_state->channel_name, 10); - } - return 0; } @@ -640,6 +601,15 @@ static void vrtsim_end(openair0_device *device) LOG_I(HW, "VRTSIM: Average TX budget %.3lf uS (more is better)\n", tx_timing->average_tx_budget); histogram_print(&tx_timing->tx_histogram); free(vrtsim_state->tx_timing); + + if (vrtsim_state->role == ROLE_SERVER) { + int ret = remove(vrtsim_state->connection_descriptor); + if (ret != 0) { + LOG_E(HW, "Failed to remove connection descriptor file %s: %s\n", vrtsim_state->connection_descriptor, strerror(errno)); + } else { + LOG_A(HW, "Removed connection descriptor file %s\n", vrtsim_state->connection_descriptor); + } + } } static int vrtsim_stub(openair0_device *device) From 74fc56813f8157ca7051768f1121004d1db90fcf Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Fri, 8 Aug 2025 17:55:12 +0200 Subject: [PATCH 36/49] Update vrtsim documentation --- radio/vrtsim/README.md | 80 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/radio/vrtsim/README.md b/radio/vrtsim/README.md index 71455175c3..2cd90e7886 100644 --- a/radio/vrtsim/README.md +++ b/radio/vrtsim/README.md @@ -4,12 +4,24 @@ This implements a shared memory realtime and near-realtime radio interface. This is performed using `shm_td_iq_channel` which handles the shared memory interface. +# Architecture + +The `vrtsim` architecture follows a server-client model: + +- **Server (gNB)**: On startup, the server writes a file, default location is + `/tmp/vrtsim_connection`. This file contains information required by the client + to establish communication. +- **Client (UE)**: The client reads this file from, verifies its contents, and uses + the provided information to open and connect to the shared memory channel. +- **Shared Memory Channel**: Both server and client use the `shm_td_iq_channel` to + exchange IQ samples and control data in real time or near-real time. + +## Channel modelling: + + # Limitations - Only 1gNB to 1nrUE: gNB as a server, UE as a client. - - Server only accepts connections during device bringup. This means that - restarting the client will not make it reconnect, the server has to be - restarted as well. # Usage @@ -21,5 +33,63 @@ is the default and means realtime. Channel modelling can be enabled by adding `vrtsim.chanmod 1` to the command line and should work the same as channel modelling in rfsimulator, -see rfsimulator [documentation](../rfsimulator/README.md), provided that your -CPU is fast enough. +see rfsimulator [documentation](../rfsimulator/README.md) with a slight difference: +channel modelling is done on transmission instead of reception. To refect this, +the model names are slightly different: the server expects a model named +`server_tx_channel_model` while the client expects a model named `client_tx_channel_model` + +Additionally, `taps_client` is available in vrtsim. This allows to connect to +channel emulation server and receive external taps (or channel impulse responses). +To use external taps, use `--taps-socket` and provide a nanomsg PUB socket address +that will publish the taps. + +# Debugging + +## Realtime issues + +Realtime issues can prevent the UE from connecting to the gNB, especially with +channel modelling enabled. Consider adjusting `timescale` parameter until +the UE can connect reliably. + +On exit, vrtsim will output a histogram of the measured transmission budget, which +is defined as the number of microseconds before the transmitted samples could be +read by peer. + +Example: + +``` +[HW] VRTSIM: Average TX budget 939.210 uS (more is better) +[HW] VRTSIM: TX budget histogram: 216 samples +[HW] Bin 0 [0.0 - 100.0uS]: 0 +[HW] Bin 1 [100.0 - 200.0uS]: 0 +[HW] Bin 2 [200.0 - 300.0uS]: 0 +[HW] Bin 3 [300.0 - 400.0uS]: 0 +[HW] Bin 4 [400.0 - 500.0uS]: 0 +[HW] Bin 5 [500.0 - 600.0uS]: 0 +[HW] Bin 6 [600.0 - 700.0uS]: 0 +[HW] Bin 7 [700.0 - 800.0uS]: 0 +[HW] Bin 8 [800.0 - 900.0uS]: 0 +[HW] Bin 9 [900.0 - 1000.0uS]: 117 +[HW] Bin 10 [1000.0 - 1100.0uS]: 0 +[HW] Bin 11 [1100.0 - 1200.0uS]: 0 +[HW] Bin 12 [1200.0 - 1300.0uS]: 0 +[HW] Bin 13 [1300.0 - 1400.0uS]: 0 +[HW] Bin 14 [1400.0 - 1500.0uS]: 0 +[HW] Bin 15 [1500.0 - 1600.0uS]: 0 +[HW] Bin 16 [1600.0 - 1700.0uS]: 0 +[HW] Bin 17 [1700.0 - 1800.0uS]: 0 +[HW] Bin 18 [1800.0 - 1900.0uS]: 0 +[HW] Bin 19 [1900.0 - 2000.0uS]: 0 +[HW] Bin 20 [2000.0 - 2100.0uS]: 0 +[HW] Bin 21 [2100.0 - 2200.0uS]: 0 +[HW] Bin 22 [2200.0 - 2300.0uS]: 0 +[HW] Bin 23 [2300.0 - 2400.0uS]: 0 +[HW] Bin 24 [2400.0 - 2500.0uS]: 0 +[HW] Bin 25 [2500.0 - 2600.0uS]: 0 +[HW] Bin 26 [2600.0 - 2700.0uS]: 0 +[HW] Bin 27 [2700.0 - 2800.0uS]: 0 +[HW] Bin 28 [2800.0 - 2900.0uS]: 0 +[HW] Bin 29 [2900.0 - 3000.0uS]: 0 +``` + +Samples in bin 0 indicate realtime issues. From 171b3f206f415f28fda86d6fa2934d384b77de0b Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Mon, 18 Aug 2025 09:43:34 +0200 Subject: [PATCH 37/49] VRTsim: Add a timeout in taps client Add a timeout in nanomsg receive code to allow the client thread to exit cleanly. --- radio/vrtsim/taps_client.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/radio/vrtsim/taps_client.cpp b/radio/vrtsim/taps_client.cpp index 6ebfc450fd..c314c3981c 100644 --- a/radio/vrtsim/taps_client.cpp +++ b/radio/vrtsim/taps_client.cpp @@ -108,8 +108,13 @@ void *client_thread_func(void *args) while (should_run) { int next_buffer = (taps_storage.current_buffer + 1) % NUM_TAPS_BUFFERS; taps_buffer_t *taps_buffer = &taps_storage.taps_buffers[next_buffer]; - int ret = nn_recv(client_thread_args->sock, taps_buffer->taps_msg, MAX_TAPS_MSG_SIZE, 0); + int ret = nn_recv(client_thread_args->sock, taps_buffer->taps_msg, MAX_TAPS_MSG_SIZE, NN_DONTWAIT); if (ret < 0) { + if (errno == EAGAIN) { + // Timeout: no message available, sleep briefly and continue + usleep(100); + continue; + } LOG_E(HW, "nn_recv() failed: errno: %d, %s\n", errno, strerror(errno)); continue; } From 513173ae0d1bc6a1304c663ecd0407042a786919 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Mon, 18 Aug 2025 09:51:27 +0200 Subject: [PATCH 38/49] Rename VRTSIM cmake option to OAI_VRTSIM --- radio/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/CMakeLists.txt b/radio/CMakeLists.txt index 654fb037f0..e68c84ec90 100644 --- a/radio/CMakeLists.txt +++ b/radio/CMakeLists.txt @@ -38,7 +38,7 @@ if(OAI_USRP) add_subdirectory(USRP) endif() -add_boolean_option(VRTSIM_RADIO ON "Activate OAI's shared memory radio driver" OFF) -if(VRTSIM_RADIO) +add_boolean_option(OAI_VRTSIM ON "Activate OAI's shared memory radio driver" OFF) +if(OAI_VRTSIM) add_subdirectory(vrtsim) endif() From c35f07b0b91e39d1fda9688232af45fb6dfa00b3 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 24 Jul 2025 17:05:43 +0200 Subject: [PATCH 39/49] GTPU: add an API to better deal with extension headers And adapt existing code to use it. Only for transmission for the moment. --- CMakeLists.txt | 3 +- openair3/ocp-gtpu/gtp_itf.cpp | 131 +++++++++++++------------- openair3/ocp-gtpu/gtpu_extensions.c | 137 ++++++++++++++++++++++++++++ openair3/ocp-gtpu/gtpu_extensions.h | 75 +++++++++++++++ 4 files changed, 276 insertions(+), 70 deletions(-) create mode 100644 openair3/ocp-gtpu/gtpu_extensions.c create mode 100644 openair3/ocp-gtpu/gtpu_extensions.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 898b387237..bfd4b3f7c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1395,9 +1395,10 @@ include_directories("${OPENAIR1_DIR}/SCHED_NR_UE") add_library (GTPV1U ${RRC_DIR}/rrc_eNB_GTPV1U.c ${OPENAIR3_DIR}/ocp-gtpu/gtp_itf.cpp + ${OPENAIR3_DIR}/ocp-gtpu/gtpu_extensions.c ) target_link_libraries(GTPV1U PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) -target_link_libraries(GTPV1U PRIVATE SIMU) +target_link_libraries(GTPV1U PRIVATE SIMU ds) include_directories(${OPENAIR3_DIR}/ocp-gtp) set (MME_APP_SRC diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 9e07a44747..5d910e8027 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -21,6 +21,7 @@ extern "C" { #include #include #include "openair2/SDAP/nr_sdap/nr_sdap.h" +#include "openair3/ocp-gtpu/gtpu_extensions.h" #include "sim.h" #pragma pack(1) @@ -230,11 +231,9 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, int msgLen, bool seqNumFlag, bool npduNumFlag, - int extHdrType, - uint8_t *extensionHeader_buffer, - uint8_t extensionHeader_length) + gtpu_extension_header_t *extensions, + int extensions_count) { - DevAssert(msgLen + HDR_MAX < 65536); // maximum size of UDP packet uint8_t buffer[msgLen + HDR_MAX]; uint8_t *curPtr = buffer; @@ -242,7 +241,7 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, // N should be 0 for us (it was used only in 2G and 3G) msgHdr->PN = npduNumFlag; msgHdr->S = seqNumFlag; - msgHdr->E = extHdrType != NO_MORE_EXT_HDRS; + msgHdr->E = extensions_count != 0; msgHdr->spare = 0; // PT=0 is for GTP' TS 32.295 (charging) msgHdr->PT = 1; @@ -252,32 +251,32 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, curPtr += sizeof(Gtpv1uMsgHeaderT); - if (seqNumFlag || (extHdrType != NO_MORE_EXT_HDRS) || npduNumFlag) { + if (msgHdr->PN || msgHdr->S || msgHdr->E) { *(uint16_t *)curPtr = seqNumFlag ? bearer->seqNum : 0x0000; curPtr += sizeof(uint16_t); *(uint8_t *)curPtr = npduNumFlag ? bearer->npduNum : 0x00; curPtr++; - *(uint8_t *)curPtr = extHdrType; + *curPtr = extensions_count ? serialize_gtpu_extension_type(extensions[0].type) : 0; curPtr++; } - // Bug: if there is more than one extension, infinite loop on extensionHeader_buffer - while (extHdrType != NO_MORE_EXT_HDRS) { - if (extensionHeader_length > 0) { - memcpy(curPtr, extensionHeader_buffer, extensionHeader_length); - curPtr += extensionHeader_length; - LOG_D(GTPU, - "Extension Header for DDD added. The length is: %d, extension header type is: %x \n", - extensionHeader_length, - *((uint8_t *)(buffer + 11))); - extHdrType = extensionHeader_buffer[extensionHeader_length - 1]; - LOG_D(GTPU, "Next extension header type is: %x \n", *((uint8_t *)(buffer + 11))); - } else { - LOG_W(GTPU, "Extension header type not supported, returning... \n"); + for (int i = 0; i < extensions_count; i++) { + int available_size = sizeof(buffer) - (curPtr - buffer); + gtpu_extension_header_type_t next = i == extensions_count - 1 ? GTPU_EXT_NONE : extensions[i + 1].type; + int len = serialize_extension(&extensions[i], next, curPtr, available_size); + if (len == -1) { + LOG_E(GTPU, "GTP extension serialization: buffer too small\n"); + return GTPNOK; } + curPtr += len; } if (Msg != NULL) { + int available_size = sizeof(buffer) - (curPtr - buffer); + if (msgLen > available_size) { + LOG_E(GTPU, "GTP message creation: buffer too small\n"); + return GTPNOK; + } memcpy(curPtr, Msg, msgLen); curPtr += msgLen; } @@ -347,18 +346,22 @@ void gtpv1uSendDirect(instance_t instance, gtpv1u_bearer_t bearer = ptr2->second; pthread_mutex_unlock(&globGtp.gtp_lock); - Gtpv1uExtHeaderT *opt_ext = NULL; - Gtpv1uExtHeaderT ext = {0}; + int extension_count = 0; + gtpu_extension_header_t ext = {}; if (bearer.outgoing_qfi != -1) { - opt_ext = &ext; - ext.ExtHeaderLen = 1; // in quad bytes EXT_HDR_LNTH_OCTET_UNITS - ext.pdusession_cntr.spare = 0; - ext.pdusession_cntr.PDU_type = UL_PDU_SESSION_INFORMATION; - ext.pdusession_cntr.QFI = bearer.outgoing_qfi; - ext.pdusession_cntr.Reflective_QoS_activation = false; - ext.pdusession_cntr.Paging_Policy_Indicator = false; - ext.NextExtHeaderType = NO_MORE_EXT_HDRS; - + ext = { + .type = GTPU_EXT_UL_PDU_SESSION_INFORMATION, + .ul_pdu_session_information = { + .qmp = false, + .dl_delay_ind = false, + .ul_delay_ind = false, + .snp = false, + .n3n9_delay_ind = false, + .new_ie_flag = false, + .qfi = bearer.outgoing_qfi + } + }; + extension_count++; } DevAssert(compatInst(instance) == bearer.sock_fd); @@ -368,9 +371,30 @@ void gtpv1uSendDirect(instance_t instance, len, seqNumFlag, npduNumFlag, - opt_ext ? PDU_SESSION_CONTAINER : NO_MORE_EXT_HDRS, - (uint8_t *)opt_ext, - sizeof(*opt_ext)); + &ext, + extension_count); +} + +static void fillDlDeliveryStatusReport(gtpu_extension_header_t *ext, uint32_t RLC_buffer_availability, uint32_t NR_PDCP_PDU_SN) +{ + *ext = { + .type = GTPU_EXT_DL_DATA_DELIVERY_STATUS, + .dl_data_delivery_status = { + /* previous version of the code was sending highest_transmitted_nr_pdcp_sn if + * it is != 0, let's do the same for the moment */ + .highest_transmitted_nr_pdcp_sn_ind = NR_PDCP_PDU_SN != 0, + .highest_delivered_nr_pdcp_sn_ind = false, + .final_frame_ind = false, + .lost_packet_report = false, + .delivered_nr_pdcp_sn_range_ind = false, + .data_rate_ind = false, + .retransmitted_nr_pdcp_sn_ind = false, + .delivered_retransmitted_nr_pdcp_ind = false, + .cause_report = false, + .desired_buffer_size = RLC_buffer_availability, + .highest_transmitted_nr_pdcp_sn = NR_PDCP_PDU_SN + } + }; } static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *req) @@ -950,7 +974,6 @@ static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, const sizeof recovery, true, false, - NO_MORE_EXT_HDRS, NULL, 0); } @@ -1168,37 +1191,8 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st LOG_D(GTPU, "Create and send DL DATA Delivery status for the previously received PDU, NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN); int rlc_tx_buffer_space = nr_rlc_get_available_tx_space(ctxt.rntiMaybeUEid, rb_id + 3); LOG_D(GTPU, "Available buffer size in RLC for Tx: %d \n", rlc_tx_buffer_space); - /*Total size of DDD_status PDU = 1 octet to report extension header length - * size of mandatory part + 3 octets for highest transmitted/delivered PDCP SN - * 1 octet for padding + 1 octet for next extension header type, - * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ - extensionHeader_t *extensionHeader; - extensionHeader = (extensionHeader_t *)calloc(1, sizeof(extensionHeader_t)); - extensionHeader->buffer[0] = (1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1) / 4; - DlDataDeliveryStatus_flagsT DlDataDeliveryStatus; - DlDataDeliveryStatus.deliveredPdcpSn = 0; - DlDataDeliveryStatus.transmittedPdcpSn = 1; - DlDataDeliveryStatus.pduType = 1; - DlDataDeliveryStatus.drbBufferSize = - htonl(rlc_tx_buffer_space); // htonl(10000000); //hardcoded for now but normally we should extract it from RLC - memcpy(extensionHeader->buffer + 1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT)); - uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT) + 1; - - extensionHeader->buffer[offset] = (NR_PDCP_PDU_SN >> 16) & 0xff; - extensionHeader->buffer[offset + 1] = (NR_PDCP_PDU_SN >> 8) & 0xff; - extensionHeader->buffer[offset + 2] = NR_PDCP_PDU_SN & 0xff; - LOG_D(GTPU, - "Octets reporting NR_PDCP_PDU_SN, extensionHeader-> %u:%u:%u \n", - extensionHeader->buffer[offset], - extensionHeader->buffer[offset + 1], - extensionHeader->buffer[offset + 2]); - extensionHeader->buffer[offset + 3] = 0x00; // Padding octet - extensionHeader->buffer[offset + 4] = 0x00; // No more extension headers - /*Total size of DDD_status PDU = size of mandatory part + - * 3 octets for highest transmitted/delivered PDCP SN + - * 1 octet for padding + 1 octet for next extension header type, - * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ - extensionHeader->length = 1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1; + gtpu_extension_header_t ext; + fillDlDeliveryStatusReport(&ext, rlc_tx_buffer_space, NR_PDCP_PDU_SN); uint32_t teid = globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid; gtpv1u_bearer_t bearer = create_bearer(h, addr, teid, 0); gtpv1uCreateAndSendMsg(&bearer, @@ -1207,9 +1201,8 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st 0, false, false, - NR_RAN_CONTAINER, - extensionHeader->buffer, - extensionHeader->length); + &ext, + 1); } LOG_D(GTPU, "[%d] Received a %d bytes packet for: teid:%x\n", h, msgBufLen - offset, ntohl(msgHdr->teid)); diff --git a/openair3/ocp-gtpu/gtpu_extensions.c b/openair3/ocp-gtpu/gtpu_extensions.c new file mode 100644 index 0000000000..b06e20e23e --- /dev/null +++ b/openair3/ocp-gtpu/gtpu_extensions.c @@ -0,0 +1,137 @@ +/* + * 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 + */ + +#include "gtpu_extensions.h" +#include "common/utils/assertions.h" +#include "common/utils/ds/byte_array_producer.h" +#include "LOG/log.h" + +/* 29.281 Figure 5.2.1-3 */ +#define NR_RAN_CONTAINER 0x84 +#define PDU_SESSION_CONTAINER 0x85 + +/* from an extension type, returns its "extension header type" + * as defined in 29.281 Figure 5.2.1-3 + */ +int serialize_gtpu_extension_type(gtpu_extension_header_type_t type) +{ + switch (type) { + case GTPU_EXT_NONE: + /* 0, no more extension */ + return 0; + case GTPU_EXT_UL_PDU_SESSION_INFORMATION: + return PDU_SESSION_CONTAINER; + case GTPU_EXT_DL_DATA_DELIVERY_STATUS: + return NR_RAN_CONTAINER; + default: + AssertFatal(0, "unknown GTPU extension type %d\n", type); + } +} + +/* returns 0 on error, 1 on success */ +static int serialize_ul_pdu_session_information(byte_array_producer_t *b, ul_pdu_session_information_t *ext) +{ + /* see 38.415 5.5.2.2 */ + AssertFatal(!ext->qmp && !ext->dl_delay_ind && !ext->ul_delay_ind + && !ext->snp && !ext->n3n9_delay_ind && !ext->new_ie_flag, + "todo\n"); + + uint8_t b1 = (1 << 4) | (ext->qmp << 3) | (ext->dl_delay_ind << 2) | (ext->ul_delay_ind << 1) | ext->snp; + uint8_t b2 = (ext->n3n9_delay_ind << 7) | (ext->new_ie_flag << 6) | ext->qfi; + return byte_array_producer_put_byte(b, b1) && byte_array_producer_put_byte(b, b2); +} + +/* returns 0 on error, 1 on success */ +static int serialize_dl_data_delivery_status(byte_array_producer_t *b, dl_data_delivery_status_t *ext) +{ + /* see 38.425 5.5.2.2 */ + AssertFatal(!ext->highest_delivered_nr_pdcp_sn_ind && !ext->final_frame_ind + && !ext->lost_packet_report && !ext->delivered_nr_pdcp_sn_range_ind + && !ext->data_rate_ind && !ext->retransmitted_nr_pdcp_sn_ind + && !ext->delivered_retransmitted_nr_pdcp_ind && !ext->cause_report, + "todo\n"); + + uint8_t b1 = (1 << 4) + | (ext->highest_transmitted_nr_pdcp_sn_ind << 3) + | (ext->highest_delivered_nr_pdcp_sn_ind << 2) + | (ext->final_frame_ind << 1) + | ext->lost_packet_report; + uint8_t b2 = (ext->delivered_nr_pdcp_sn_range_ind << 4) + | (ext->data_rate_ind << 3) + | (ext->retransmitted_nr_pdcp_sn_ind << 2) + | (ext->delivered_retransmitted_nr_pdcp_ind << 1) + | ext->cause_report; + if (!byte_array_producer_put_byte(b, b1) || !byte_array_producer_put_byte(b, b2)) + return 0; + + if (!byte_array_producer_put_u32_be(b, ext->desired_buffer_size)) + return 0; + + if (ext->highest_transmitted_nr_pdcp_sn_ind) + if (!byte_array_producer_put_u24_be(b, ext->highest_transmitted_nr_pdcp_sn)) + return 0; + + return 1; +} + +/* returns -1 on error, number of serialized bytes on success */ +int serialize_extension(gtpu_extension_header_t *ext, gtpu_extension_header_type_t next, uint8_t *out_buf, int out_len) +{ + byte_array_producer_t b; + + b = byte_array_producer_from_buffer(out_buf, out_len); + /* length - will be set later */ + if (!byte_array_producer_put_byte(&b, 0)) + goto error; + + switch (ext->type) { + case GTPU_EXT_UL_PDU_SESSION_INFORMATION: + if (!serialize_ul_pdu_session_information(&b, &ext->ul_pdu_session_information)) + goto error; + break; + case GTPU_EXT_DL_DATA_DELIVERY_STATUS: + if (!serialize_dl_data_delivery_status(&b, &ext->dl_data_delivery_status)) + goto error; + break; + default: + LOG_E(GTPU, "unknown extension type %d\n", ext->type); + return -1; + } + + /* padding */ + while ((b.pos & 3) != 3) + if (!byte_array_producer_put_byte(&b, 0)) + goto error; + + /* next */ + if (!byte_array_producer_put_byte(&b, serialize_gtpu_extension_type(next))) + goto error; + + /* length is now know */ + DevAssert(b.pos / 4 <= 255); + out_buf[0] = b.pos / 4; + + return b.pos; + +error: + LOG_E(GTPU, "error serializing extension\n"); + return -1; +} diff --git a/openair3/ocp-gtpu/gtpu_extensions.h b/openair3/ocp-gtpu/gtpu_extensions.h new file mode 100644 index 0000000000..750277eb32 --- /dev/null +++ b/openair3/ocp-gtpu/gtpu_extensions.h @@ -0,0 +1,75 @@ +/* + * 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 + */ + +#ifndef GTPU_EXTENSIONS_H +#define GTPU_EXTENSIONS_H + +#include +#include + +typedef enum { + GTPU_EXT_NONE, + /* 38.415 */ + GTPU_EXT_UL_PDU_SESSION_INFORMATION, + /* 38.425 */ + GTPU_EXT_DL_DATA_DELIVERY_STATUS, +} gtpu_extension_header_type_t; + +/* 38.415 */ +typedef struct { + /* not all fields are present, to be refined if needed */ + bool qmp; + bool dl_delay_ind; + bool ul_delay_ind; + bool snp; + bool n3n9_delay_ind; + bool new_ie_flag; + int qfi; +} ul_pdu_session_information_t; + +/* 38.425 */ +typedef struct { + /* not all fields are present, to be refined if needed */ + bool highest_transmitted_nr_pdcp_sn_ind; + bool highest_delivered_nr_pdcp_sn_ind; + bool final_frame_ind; + bool lost_packet_report; + bool delivered_nr_pdcp_sn_range_ind; + bool data_rate_ind; + bool retransmitted_nr_pdcp_sn_ind; + bool delivered_retransmitted_nr_pdcp_ind; + bool cause_report; + uint32_t desired_buffer_size; + uint32_t highest_transmitted_nr_pdcp_sn; +} dl_data_delivery_status_t; + +typedef struct { + gtpu_extension_header_type_t type; + union { + ul_pdu_session_information_t ul_pdu_session_information; + dl_data_delivery_status_t dl_data_delivery_status; + }; +} gtpu_extension_header_t; + +int serialize_gtpu_extension_type(gtpu_extension_header_type_t type); +int serialize_extension(gtpu_extension_header_t *ext, gtpu_extension_header_type_t next, uint8_t *out_buf, int out_len); + +#endif /* GTPU_EXTENSIONS_H */ From 84ce708f96fcc4531c1643b3793160f6c7d4a036 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 25 Jul 2025 16:32:50 +0200 Subject: [PATCH 40/49] GTPU: basic support of DL USER DATA See 3GPP TS 38.425 5.5.2.1. Only sending part, only dealing with NR-U Sequence Number. --- openair3/ocp-gtpu/gtpu_extensions.c | 30 +++++++++++++++++++++++++++++ openair3/ocp-gtpu/gtpu_extensions.h | 16 +++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/openair3/ocp-gtpu/gtpu_extensions.c b/openair3/ocp-gtpu/gtpu_extensions.c index b06e20e23e..13f0cee053 100644 --- a/openair3/ocp-gtpu/gtpu_extensions.c +++ b/openair3/ocp-gtpu/gtpu_extensions.c @@ -40,6 +40,7 @@ int serialize_gtpu_extension_type(gtpu_extension_header_type_t type) case GTPU_EXT_UL_PDU_SESSION_INFORMATION: return PDU_SESSION_CONTAINER; case GTPU_EXT_DL_DATA_DELIVERY_STATUS: + case GTPU_EXT_DL_USER_DATA: return NR_RAN_CONTAINER; default: AssertFatal(0, "unknown GTPU extension type %d\n", type); @@ -92,6 +93,31 @@ static int serialize_dl_data_delivery_status(byte_array_producer_t *b, dl_data_d return 1; } +/* returns 0 on error, 1 on success */ +static int serialize_dl_user_data(byte_array_producer_t *b, dl_user_data_t *ext) +{ + /* see 38.425 5.5.2.1 */ + AssertFatal(!ext->dl_discard_blocks && !ext->dl_flush && !ext->report_polling + && !ext->request_out_of_seq_report && !ext->report_delivered + && !ext->user_data_existence_flag && !ext->assistance_info_report_polling_flag + && !ext->retransmission_flag, + "todo\n"); + + uint8_t b1 = (0 << 4) | (ext->dl_discard_blocks << 2) | (ext->dl_flush << 1) | ext->report_polling; + uint8_t b2 = (ext->request_out_of_seq_report << 4) + | (ext->report_delivered << 3) + | (ext->user_data_existence_flag << 2) + | (ext->assistance_info_report_polling_flag << 1) + | ext->retransmission_flag; + if (!byte_array_producer_put_byte(b, b1) || !byte_array_producer_put_byte(b, b2)) + return 0; + + if (!byte_array_producer_put_u24_be(b, ext->nru_sequence_number)) + return 0; + + return 1; +} + /* returns -1 on error, number of serialized bytes on success */ int serialize_extension(gtpu_extension_header_t *ext, gtpu_extension_header_type_t next, uint8_t *out_buf, int out_len) { @@ -111,6 +137,10 @@ int serialize_extension(gtpu_extension_header_t *ext, gtpu_extension_header_type if (!serialize_dl_data_delivery_status(&b, &ext->dl_data_delivery_status)) goto error; break; + case GTPU_EXT_DL_USER_DATA: + if (!serialize_dl_user_data(&b, &ext->dl_user_data)) + goto error; + break; default: LOG_E(GTPU, "unknown extension type %d\n", ext->type); return -1; diff --git a/openair3/ocp-gtpu/gtpu_extensions.h b/openair3/ocp-gtpu/gtpu_extensions.h index 750277eb32..b621d27e14 100644 --- a/openair3/ocp-gtpu/gtpu_extensions.h +++ b/openair3/ocp-gtpu/gtpu_extensions.h @@ -31,6 +31,7 @@ typedef enum { GTPU_EXT_UL_PDU_SESSION_INFORMATION, /* 38.425 */ GTPU_EXT_DL_DATA_DELIVERY_STATUS, + GTPU_EXT_DL_USER_DATA, } gtpu_extension_header_type_t; /* 38.415 */ @@ -61,11 +62,26 @@ typedef struct { uint32_t highest_transmitted_nr_pdcp_sn; } dl_data_delivery_status_t; +/* 38.425 */ +typedef struct { + /* not all fields are present, to be refined if needed */ + bool dl_discard_blocks; + bool dl_flush; + bool report_polling; + bool request_out_of_seq_report; + bool report_delivered; + bool user_data_existence_flag; + bool assistance_info_report_polling_flag; + bool retransmission_flag; + uint32_t nru_sequence_number; +} dl_user_data_t; + typedef struct { gtpu_extension_header_type_t type; union { ul_pdu_session_information_t ul_pdu_session_information; dl_data_delivery_status_t dl_data_delivery_status; + dl_user_data_t dl_user_data; }; } gtpu_extension_header_t; From 807b38ddaeb0e517c2ff3ddf58ab4fa9aec7c8f0 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 25 Jul 2025 16:56:51 +0200 Subject: [PATCH 41/49] GTPU: CU has to send NR-U Sequence Number This is in 3GPP TS 38.425. Because it was not done, there was a problem of interoperability between openair CU and srsran DU. No DL traffic was reaching the UE. A new GTPU API function is introduced: gtpv1uSendDirectWithNRUSeqNum(). It is only used in the gNB CU, in PDCP. So it was decided to not add a new parameter to gtpv1uSendDirect() but instead add a new API function. --- openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c | 2 +- openair3/ocp-gtpu/gtp_itf.cpp | 95 ++++++++++++++++++++--- openair3/ocp-gtpu/gtp_itf.h | 5 ++ 3 files changed, 91 insertions(+), 11 deletions(-) diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index e170e90823..4d1c0b5ba0 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -469,7 +469,7 @@ static void deliver_pdu_drb_gnb(void *deliver_pdu_data, ue_id_t ue_id, int rb_id if (NODE_IS_CU(node_type)) { LOG_D(PDCP, "%s() (drb %d) sending message to gtp size %d\n", __func__, rb_id, size); extern instance_t CUuniqInstance; - gtpv1uSendDirect(CUuniqInstance, ue_id, rb_id, (uint8_t *)buf, size, false, false); + gtpv1uSendDirectWithNRUSeqNum(CUuniqInstance, ue_id, rb_id, (uint8_t *)buf, size); } else { uint8_t *memblock = malloc16(size); memcpy(memblock, buf, size); diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 5d910e8027..9be1873612 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -113,6 +113,7 @@ typedef struct gtpv1u_bearer_s { teid_t teid_outgoing; uint16_t seqNum; uint8_t npduNum; + int32_t nru_sequence_number; int outgoing_qfi; } gtpv1u_bearer_t; @@ -306,13 +307,14 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, return !GTPNOK; } -void gtpv1uSendDirect(instance_t instance, - ue_id_t ue_id, - int bearer_id, - uint8_t *buf, - size_t len, - bool seqNumFlag, - bool npduNumFlag) +static void _gtpv1uSendDirect(instance_t instance, + ue_id_t ue_id, + int bearer_id, + uint8_t *buf, + size_t len, + bool seqNumFlag, + bool npduNumFlag, + int32_t nru_seqnum) { pthread_mutex_lock(&globGtp.gtp_lock); getInstRetVoid(compatInst(instance)); @@ -347,9 +349,13 @@ void gtpv1uSendDirect(instance_t instance, pthread_mutex_unlock(&globGtp.gtp_lock); int extension_count = 0; - gtpu_extension_header_t ext = {}; + gtpu_extension_header_t ext[2]; if (bearer.outgoing_qfi != -1) { - ext = { + /* 29.281 Figure 5.2.1-3 note 4 says PDU Session Container must come first. + * GTPU_EXT_UL_PDU_SESSION_INFORMATION is within a PDU Session Container + * so it must be put before any other extension. + */ + ext[extension_count] = { .type = GTPU_EXT_UL_PDU_SESSION_INFORMATION, .ul_pdu_session_information = { .qmp = false, @@ -364,6 +370,24 @@ void gtpv1uSendDirect(instance_t instance, extension_count++; } + if (nru_seqnum != -1) { + ext[extension_count] = { + .type = GTPU_EXT_DL_USER_DATA, + .dl_user_data = { + .dl_discard_blocks = false, + .dl_flush = false, + .report_polling = false, + .request_out_of_seq_report = false, + .report_delivered = false, + .user_data_existence_flag = false, + .assistance_info_report_polling_flag = false, + .retransmission_flag = false, + .nru_sequence_number = (uint32_t)nru_seqnum + } + }; + extension_count++; + } + DevAssert(compatInst(instance) == bearer.sock_fd); gtpv1uCreateAndSendMsg(&bearer, GTP_GPDU, @@ -371,10 +395,61 @@ void gtpv1uSendDirect(instance_t instance, len, seqNumFlag, npduNumFlag, - &ext, + ext, extension_count); } +void gtpv1uSendDirect(instance_t instance, + ue_id_t ue_id, + int bearer_id, + uint8_t *buf, + size_t len, + bool seqNumFlag, + bool npduNumFlag) +{ + _gtpv1uSendDirect(instance, + ue_id, + bearer_id, + buf, + len, + seqNumFlag, + npduNumFlag, + -1); +} + +void gtpv1uSendDirectWithNRUSeqNum(instance_t instance, + ue_id_t ue_id, + int bearer_id, + uint8_t *buf, + size_t len) +{ + pthread_mutex_lock(&globGtp.gtp_lock); + getInstRetVoid(compatInst(instance)); + getUeRetVoid(inst, ue_id); + auto ptr2 = ptrUe->second.bearers.find(bearer_id); + + if (ptr2 == ptrUe->second.bearers.end()) { + LOG_E(GTPU, "[%ld] GTP-U instance: sending a packet to a non existant UE:RAB: %lx/%x\n", instance, ue_id, bearer_id); + pthread_mutex_unlock(&globGtp.gtp_lock); + return; + } + + int32_t nru_seqnum = ptr2->second.nru_sequence_number; + ptr2->second.nru_sequence_number++; + ptr2->second.nru_sequence_number &= (1 << 24) - 1; + + pthread_mutex_unlock(&globGtp.gtp_lock); + + _gtpv1uSendDirect(instance, + ue_id, + bearer_id, + buf, + len, + false, + false, + nru_seqnum); +} + static void fillDlDeliveryStatusReport(gtpu_extension_header_t *ext, uint32_t RLC_buffer_availability, uint32_t NR_PDCP_PDU_SN) { *ext = { diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index d341acb311..4fcb8190e4 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -128,6 +128,11 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int *pdusession_id); void gtpv1uSendDirect(instance_t instance, ue_id_t ue_id, int bearer_id, uint8_t *buf, size_t len, bool seqNumFlag, bool npduNumFlag); + void gtpv1uSendDirectWithNRUSeqNum(instance_t instance, + ue_id_t ue_id, + int bearer_id, + uint8_t *buf, + size_t len); instance_t gtpv1Init(openAddr_t context); void *gtpv1uTask(void *args); From 653ad775eae479ba51b5548fe360d06ef7c64e3d Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 25 Jul 2025 17:03:22 +0200 Subject: [PATCH 42/49] hack: ignore empty Cell Group Config returned by DU Interoperability testing with srsRAN DU shows than when receiving RRCReconfigurationCompleteIndicator in a UEContextModificationRequest the DU replies in the UEContextModificationResponse with an empty CellGroupConfig. This CellGroupConfig is encoded as 2 zero bytes. Let's ignore this message when we receive it, don't forward it to the UE in a RRC Reconfiguration, as it should per standard (38.473 8.3.4.2): If the CellGroupConfig IE is included in the DU to CU RRC Information IE contained in the UE CONTEXT MODIFICATION RESPONSE message, the gNB-CU shall perform RRC Reconfiguration as described in TS 38.331 [8]. The CellGroupConfig IE shall transparently be signaled to the UE as specified in TS 38.331 [8]. To be removed/reworked in the future if the interoperability situation improves. --- openair2/RRC/NR/rrc_gNB.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 75f6838a78..000c4f46cb 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2305,13 +2305,19 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i uper_decode_complete(NULL, &asn_DEF_NR_CellGroupConfig, (void **)&cellGroupConfig, (uint8_t *)cgc->buf, cgc->len); AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n"); - if (UE->masterCellGroup) { - ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); - LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti); + /* hack for interoperability with srsRAN: ignore cell group config if empty */ + /* cell group config is empty if buffer length is 2 and content is all 0 */ + bool cgc_is_empty = cgc->len == 2 && cgc->buf[0] == 0 && cgc->buf[1] == 0; + if (!cgc_is_empty) { + if (UE->masterCellGroup) { + ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); + LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti); + } + UE->masterCellGroup = cellGroupConfig; + rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE); + } else { + LOG_W(NR_RRC, "hack: UE %d: ignore empty CellGroupConfig in UEContextModificationResponse\n", UE->rrc_ue_id); } - UE->masterCellGroup = cellGroupConfig; - - rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE); } // Reconfiguration should have been sent to the UE, so it will attempt the From dd0188dfdce725ee6eabd2ff2152d927106f1e12 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 21 Aug 2025 09:41:14 +0200 Subject: [PATCH 43/49] fix for config UE TCI state ID --- openair2/LAYER2/NR_MAC_UE/config_ue.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 73bb02a575..d764f7cb86 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -700,8 +700,11 @@ static void modlist_coreset(NR_ControlResourceSet_t *source, NR_ControlResourceS if (id == *target->tci_StatesPDCCH_ToAddList->list.array[j]) break; } - if (j == target->tci_StatesPDCCH_ToAddList->list.count) - ASN_SEQUENCE_ADD(&target->tci_StatesPDCCH_ToAddList->list, source->tci_StatesPDCCH_ToAddList->list.array[i]); + if (j == target->tci_StatesPDCCH_ToAddList->list.count) { + long *tci_id = calloc(1, sizeof(*tci_id)); + *tci_id = id; + ASN_SEQUENCE_ADD(&target->tci_StatesPDCCH_ToAddList->list, tci_id); + } } } else UPDATE_IE(target->tci_StatesPDCCH_ToAddList, From 8a091a0b844ca6a6f5638b865bb7eadf5c26fa97 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 21 Aug 2025 10:31:20 +0200 Subject: [PATCH 44/49] DLSCH sched: only sort relevant part of list This reduces the amount of elements to compare. See also: 7ee8e8eaba7f ("Fix bug: only sort relevant part of list") --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 44d4affb08..f831e8af2e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -613,7 +613,7 @@ static void pf_dl(module_id_t module_id, int remainUEs[num_beams]; for (int i = 0; i < num_beams; i++) remainUEs[i] = max_num_ue; - int curUE = 0; + int numUE = 0; int CC_id = 0; int slots_per_frame = mac->frame_structure.numb_slots_frame; @@ -704,13 +704,13 @@ static void pf_dl(module_id_t module_id, tbs, coeff_ue); /* Create UE_sched list for UEs eligible for new transmission*/ - UE_sched[curUE].coef=coeff_ue; - UE_sched[curUE].UE=UE; - curUE++; + UE_sched[numUE].coef = coeff_ue; + UE_sched[numUE].UE = UE; + numUE++; } } - qsort(UE_sched, sizeofArray(UE_sched), sizeof(UEsched_t), comparator); + qsort(UE_sched, numUE, sizeof(UEsched_t), comparator); UEsched_t *iterator = UE_sched; const int min_rbSize = 5; From a30bee2fab87f02656882fd01af38fbf4858f88f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 13 Aug 2025 13:51:35 +0200 Subject: [PATCH 45/49] DLSCH sched: Fix coefficients for UE list ordering Update the comparator() to properly sort UEs based on their coefficient by appliey a consistent logic of -1/0/1 (see man qsort) for smaller/equal/larger. The previous logic was only sorting based on 0/1 (equal/larger), which would result in some UEs with a high coefficient not necessarily advancing to the start of the list, which could reduce the likelihood of them being scheduled when they actually should (in other words, this should improve fairness). See also: 62e4025bc846 ("Integrate inactivity scheduling into main allocation loop") --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index f831e8af2e..c88ad3bd96 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -594,8 +594,15 @@ typedef struct UEsched_s { NR_UE_info_t * UE; } UEsched_t; -static int comparator(const void *p, const void *q) { - return ((UEsched_t*)p)->coef < ((UEsched_t*)q)->coef; +static int comparator(const void *p, const void *q) +{ + const UEsched_t *pp = p; + const UEsched_t *qq = q; + if (pp->coef < qq->coef) + return 1; + else if (pp->coef > qq->coef) + return -1; + return 0; } static void pf_dl(module_id_t module_id, From 1cebf02b1eb4842e12728545466376ac1308c303 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Tue, 5 Aug 2025 11:25:22 +0000 Subject: [PATCH 46/49] feat(rf_emulator): Add parameters Add two parameters: * `enable_noise` to enable noise injection at TX * `noise_level_dBFS` to set noise level --- radio/emulator/rf_emulator.c | 43 ++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/radio/emulator/rf_emulator.c b/radio/emulator/rf_emulator.c index a747afc053..861daab2ba 100644 --- a/radio/emulator/rf_emulator.c +++ b/radio/emulator/rf_emulator.c @@ -32,6 +32,16 @@ #include "common_lib.h" #include "radio/vrtsim/noise_device.h" #include "simde/x86/avx512.h" +#include "SIMULATION/TOOLS/sim.h" + +#define RF_EMULATOR_SECTION "rf_emulator" +// clang-format off +#define RF_EMULATOR_PARAMS_DESC \ + { \ + {"enable_noise", "Enable noise injection", 0, .iptr = &emulator_state->enable_noise, .defintval = 1, TYPE_INT, 0},\ + {"noise_level_dBFS", "Noise level", 0, .iptr = &emulator_state->noise_level_dBFS, .defintval = INVALID_DBFS_VALUE, TYPE_INT, 0},\ + }; +// clang-format on // structures and timing thread job for timing typedef struct { @@ -49,6 +59,8 @@ typedef struct { emulator_timestamp_t emulator_timestamp; pthread_t timing_thread; bool run_timing_thread; + int enable_noise; + int noise_level_dBFS; } emulator_state_t; static void *emulator_timing_job(void *arg) @@ -108,7 +120,13 @@ static int emulator_start(openair0_device *device) // Start the timing thread and the noise device emulator_state_t *emulator_state = (emulator_state_t *)device->priv; if (!emulator_state->run_timing_thread) { - init_noise_device(0); // TODO configure noise level + if (emulator_state->enable_noise > 0) { + float noise_level = emulator_state->noise_level_dBFS == INVALID_DBFS_VALUE + ? 0.0f + : (32767.0 / powf(10.0, .05 * -(emulator_state->noise_level_dBFS))); + init_noise_device(noise_level); + LOG_I(HW, "Using noise level with %d dBFS/%f\n", emulator_state->noise_level_dBFS, noise_level); + } emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; memset(emulator_timestamp, 0, sizeof(emulator_timestamp_t)); @@ -170,7 +188,9 @@ static void emulator_end(openair0_device *device) emulator_state->run_timing_thread = false; int ret = pthread_join(emulator_state->timing_thread, NULL); AssertFatal(ret == 0, "pthread_join() failed: errno: %d, %s\n", errno, strerror(errno)); - free_noise_device(); + if (emulator_state->enable_noise > 0) { + free_noise_device(); + } } } @@ -299,13 +319,26 @@ static int emulator_read(openair0_device *device, openair0_timestamp *ptimestamp *ptimestamp = emulator_state->last_received_sample; emulator_state->last_received_sample += nsamps; - for (int i = 0; i < nbAnt; i++) { - read_noise(buff[i], nsamps); + if (emulator_state->enable_noise > 0) { + for (int i = 0; i < nbAnt; i++) { + read_noise(buff[i], nsamps); + } + } else { + for (int i = 0; i < nbAnt; i++) { + memset(buff[i], 0, nsamps * sizeof(c16_t)); + } } return nsamps; } +static void emulator_readconfig(emulator_state_t *emulator_state) +{ + paramdef_t emulator_params[] = RF_EMULATOR_PARAMS_DESC; + int ret = config_get(config_get_if(), emulator_params, sizeofArray(emulator_params), RF_EMULATOR_SECTION); + AssertFatal(ret >= 0, "configuration couldn't be performed\n"); +} + int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { LOG_I(HW, "This is emulator RF that does nothing\n"); @@ -330,5 +363,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) device->trx_write_func = emulator_write; device->trx_read_func = emulator_read; + emulator_readconfig(emulator_state); + return 0; } From 89e6e53f8c8991719379afdfb02282392e6718db Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 6 Aug 2025 18:50:33 +0200 Subject: [PATCH 47/49] Remove --emulate-rf option and corresponding code With the RFemulator, there is no need for a dedicated --emulate-rf option anymore. For all uses of --emulate-rf, RFemulator should be used instead. --- common/config/DOC/config/rtusage.md | 1 - executables/lte-ru.c | 176 +++++----------------- executables/lte-softmodem.c | 1 - executables/lte-softmodem.h | 1 - executables/nr-ru.c | 203 +++++++++----------------- executables/nr-softmodem-common.h | 1 - executables/nr-softmodem.c | 1 - executables/softmodem-common.h | 5 - openair1/PHY/defs_RU.h | 13 -- openair1/PHY/defs_eNB.h | 5 - openair1/SIMULATION/LTE_PHY/dlsim.c | 1 - openair1/SIMULATION/LTE_PHY/mbmssim.c | 2 - 12 files changed, 104 insertions(+), 306 deletions(-) diff --git a/common/config/DOC/config/rtusage.md b/common/config/DOC/config/rtusage.md index 3b51e1afd2..7c42a6edcc 100644 --- a/common/config/DOC/config/rtusage.md +++ b/common/config/DOC/config/rtusage.md @@ -55,7 +55,6 @@ To get help on supported parameters you can use specific options: --ulsch-max-errors: set the eNodeB max ULSCH erros --phy-test: test UE phy layer, mac disabled --usim-test: use XOR autentication algo in case of test usim mode - --emulate-rf: Emulated RF enabled(disable by defult) --clock: tells hardware to use a clock reference (0:internal, 1:external, 2:gpsdo) --wait-for-sync: Help string not specified -C: Set the downlink frequency for all component carriers diff --git a/executables/lte-ru.c b/executables/lte-ru.c index 326e55b288..eed5a90eb9 100644 --- a/executables/lte-ru.c +++ b/executables/lte-ru.c @@ -531,44 +531,6 @@ volatile late_control_e late_control=STATE_BURST_NORMAL; /* add fail safe for late command end */ -static void *emulatedRF_thread(void *param) { - RU_proc_t *proc = (RU_proc_t *) param; - int microsec = 500; // length of time to sleep, in miliseconds - int numerology = 0 ; - struct timespec req = {0}; - req.tv_sec = 0; - req.tv_nsec = (numerology>0)? ((microsec * 1000L)/numerology):(microsec * 1000L)*2; - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(1,&cpuset); - pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - int policy; - int ret; - struct sched_param sparam; - memset(&sparam, 0, sizeof(sparam)); - sparam.sched_priority = sched_get_priority_max(SCHED_FIFO); - policy = SCHED_FIFO ; - pthread_setschedparam(pthread_self(), policy, &sparam); - wait_sync("emulatedRF_thread"); - - while(!oai_exit) { - nanosleep(&req, (struct timespec *)NULL); - - if(proc->emulate_rf_busy ) { - LOG_E(PHY,"rf being delayed in emulated RF\n"); - } - - proc->emulate_rf_busy = 1; - AssertFatal((ret=pthread_mutex_lock(&proc->mutex_emulateRF))==0,"mutex_lock returns %d\n",ret); - ++proc->instance_cnt_emulateRF; - pthread_cond_signal(&proc->cond_emulateRF); - AssertFatal((ret=pthread_mutex_unlock(&proc->mutex_emulateRF))==0,"mutex_unlock returns %d\n",ret); - } - - return 0; -} - - void rx_rf(RU_t *ru, int *frame, int *subframe) { @@ -586,17 +548,7 @@ void rx_rf(RU_t *ru, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 1 ); old_ts = proc->timestamp_rx; - if(ru->emulate_rf) { - wait_on_condition(&proc->mutex_emulateRF,&proc->cond_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread"); - release_thread(&proc->mutex_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread"); - rxs = fp->samples_per_tti; - } else { - rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, - &ts, - rxp, - fp->samples_per_tti, - ru->nb_rx); - } + rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, fp->samples_per_tti, ru->nb_rx); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 ); ru->south_in_cnt++; @@ -613,11 +565,7 @@ void rx_rf(RU_t *ru, resynch=1; } - if(get_softmodem_params()->emulate_rf) { - proc->timestamp_rx = old_ts + fp->samples_per_tti; - } else { - proc->timestamp_rx = ts-ru->ts_offset; - } + proc->timestamp_rx = ts-ru->ts_offset; // AssertFatal(rxs == fp->samples_per_tti, // "rx_rf: Asked for %d samples, got %d from SDR\n",fp->samples_per_tti,rxs); @@ -1229,7 +1177,6 @@ void wakeup_L1s(RU_t *ru) { // pthread_mutex_unlock(&proc->mutex_RU); // LOG_D(PHY,"wakeup eNB top for for subframe %d\n", ru->proc.tti_rx); // ru->eNB_top(eNB_list[0],ru->proc.frame_rx,ru->proc.tti_rx,string); - ru->proc.emulate_rf_busy = 0; } @@ -1497,7 +1444,6 @@ static void *ru_thread_tx( void *param ) { L1_rxtx_proc_t *L1_proc; cpu_set_t cpuset; CPU_ZERO(&cpuset); - char filename[256]; thread_top_init("ru_thread_tx",1,400000,500000,500000); //CPU_SET(5, &cpuset); //pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); @@ -1530,24 +1476,12 @@ static void *ru_thread_tx( void *param ) { // do OFDM if needed if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) ru->feptx_ofdm(ru,frame_tx,tti_tx); - if(!(ru->emulate_rf)) { //if(!emulate_rf){ - // do outgoing fronthaul (south) if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) ru->fh_south_out(ru,frame_tx,tti_tx,timestamp_tx); + // do outgoing fronthaul (south) if needed + if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) + ru->fh_south_out(ru, frame_tx, tti_tx, timestamp_tx); - if (ru->fh_north_out) ru->fh_north_out(ru); - } else { - for (int i=0; inb_tx; i++) { - if(frame_tx == 2) { - sprintf(filename,"txdataF%d_frame%d_sf%d.m",i,frame_tx,tti_tx); - LOG_M(filename,"txdataF_frame",ru->common.txdataF_BF[i],fp->symbols_per_tti*fp->ofdm_symbol_size, 1, 1); - } - - if(frame_tx == 2 && tti_tx==0) { - sprintf(filename,"txdata%d_frame%d.m",i,frame_tx); - LOG_M(filename,"txdata_frame",ru->common.txdata[i],fp->samples_per_tti*10, 1, 1); - } - } - } + if (ru->fh_north_out) + ru->fh_north_out(ru); LOG_D(PHY,"ru_thread_tx: releasing RU TX in %d.%d\n", frame_tx, tti_tx); release_thread(&proc->mutex_eNBs,&proc->instance_cnt_eNBs,"ru_thread_tx"); @@ -1612,7 +1546,6 @@ static void *ru_thread( void *param ) { int ret; cpu_set_t cpuset; CPU_ZERO(&cpuset); - char filename[256]; // set default return value #if defined(PRE_SCD_THREAD) dlsch_ue_select_tbl_in_use = 1; @@ -1625,21 +1558,7 @@ static void *ru_thread( void *param ) { LOG_I(PHY,"thread ru created id=%ld\n", syscall(__NR_gettid)); LOG_I(PHY,"Starting RU %d (%s,%s),\n", ru->idx, NB_functions[ru->function], NB_timing[ru->if_timing]); - if(get_softmodem_params()->emulate_rf) { - phy_init_RU(ru); - - if (setup_RU_buffers(ru)!=0) { - LOG_I(PHY,"Exiting, cannot initialize RU Buffers\n"); - exit(-1); - } - - LOG_I(PHY, "Signaling main thread that RU %d is ready\n",ru->idx); - AssertFatal((ret=pthread_mutex_lock(ru->ru_mutex))==0,"mutex_lock returns %d\n",ret); - *ru->ru_mask &= ~(1<idx); - pthread_cond_signal(ru->ru_cond); - AssertFatal((ret=pthread_mutex_unlock(ru->ru_mutex))==0,"mutex_unlock returns %d\n",ret); - ru->state = RU_RUN; - } else if (ru->has_ctrl_prt == 0) { + if (ru->has_ctrl_prt == 0) { // There is no control port: start everything here LOG_I(PHY, "RU %d has no OAI ctrl port\n",ru->idx); @@ -1695,18 +1614,22 @@ static void *ru_thread( void *param ) { } else wait_sync("ru_thread"); LOG_D(PHY,"RU %d: Got start from control thread\n",ru->idx); - if(!(ru->emulate_rf)) { - if (ru->is_slave == 0) AssertFatal(ru->state == RU_RUN,"ru-%d state = %s != RU_RUN\n",ru->idx,ru_states[ru->state]); - else if (ru->is_slave == 1) AssertFatal(ru->state == RU_SYNC || ru->state == RU_RUN || - ru->state == RU_CHECK_SYNC,"ru %d state = %s != RU_SYNC or RU_RUN or RU_CHECK_SYNC\n",ru->idx,ru_states[ru->state]); + if (ru->is_slave == 0) + AssertFatal(ru->state == RU_RUN, "ru-%d state = %s != RU_RUN\n", ru->idx, ru_states[ru->state]); + else if (ru->is_slave == 1) + AssertFatal(ru->state == RU_SYNC || ru->state == RU_RUN || ru->state == RU_CHECK_SYNC, + "ru %d state = %s != RU_SYNC or RU_RUN or RU_CHECK_SYNC\n", + ru->idx, + ru_states[ru->state]); - // Start RF device if any - if (ru->start_rf) { - if (ru->start_rf(ru) != 0) - AssertFatal(1==0,"Could not start the RF device\n"); - else LOG_I(PHY,"RU %d rf device ready\n",ru->idx); - } else LOG_D(PHY,"RU %d no rf device\n",ru->idx); - } + // Start RF device if any + if (ru->start_rf) { + if (ru->start_rf(ru) != 0) + AssertFatal(1 == 0, "Could not start the RF device\n"); + else + LOG_I(PHY, "RU %d rf device ready\n", ru->idx); + } else + LOG_D(PHY, "RU %d no rf device\n", ru->idx); // if an asnych_rxtx thread exists // wakeup the thread because the devices are ready at this point @@ -1864,26 +1787,13 @@ static void *ru_thread( void *param ) { // do OFDM if needed if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) ru->feptx_ofdm(ru, proc->frame_tx, proc->tti_tx); - if(!(ru->emulate_rf)) { //if(!emulate_rf){ - // do outgoing fronthaul (south) if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) ru->fh_south_out(ru, proc->frame_tx, proc->tti_tx, proc->timestamp_tx); + // do outgoing fronthaul (south) if needed + if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) + ru->fh_south_out(ru, proc->frame_tx, proc->tti_tx, proc->timestamp_tx); - if ((ru->fh_north_out) && (ru->state!=RU_CHECK_SYNC)) ru->fh_north_out(ru); - } else { - for (int i=0; inb_tx; i++) { - if(proc->frame_tx == 2) { - sprintf(filename,"txdataF%d_frame%d_sf%d.m",i,proc->frame_tx,proc->tti_tx); - LOG_M(filename,"txdataF_frame",ru->common.txdataF_BF[i],ru->frame_parms->symbols_per_tti*ru->frame_parms->ofdm_symbol_size, 1, 1); - } + if ((ru->fh_north_out) && (ru->state != RU_CHECK_SYNC)) + ru->fh_north_out(ru); - if(proc->frame_tx == 2 && proc->tti_tx==0) { - sprintf(filename,"txdata%d_frame%d.m",i,proc->frame_tx); - LOG_M(filename,"txdata_frame",ru->common.txdata[i],ru->frame_parms->samples_per_tti*10, 1, 1); - } - } - } - - proc->emulate_rf_busy = 0; } #else @@ -1903,12 +1813,11 @@ static void *ru_thread( void *param ) { LOG_I(PHY, "Exiting ru_thread \n"); - if (!(ru->emulate_rf)) { - if (ru->stop_rf != NULL) { - if (ru->stop_rf(ru) != 0) - LOG_E(HW,"Could not stop the RF device\n"); - else LOG_I(PHY,"RU %d rf device stopped\n",ru->idx); - } + if (ru->stop_rf != NULL) { + if (ru->stop_rf(ru) != 0) + LOG_E(HW, "Could not stop the RF device\n"); + else + LOG_I(PHY, "RU %d rf device stopped\n", ru->idx); } return NULL; @@ -2128,12 +2037,12 @@ static void *rf_tx( void *param ) { // do OFDM if needed if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) ru->feptx_ofdm(ru,proc->frame_tx,proc->tti_tx); - if(!ru->emulate_rf) { - // do outgoing fronthaul (south) if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) ru->fh_south_out(ru,proc->frame_tx,proc->tti_tx,proc->timestamp_tx); + // do outgoing fronthaul (south) if needed + if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) + ru->fh_south_out(ru, proc->frame_tx, proc->tti_tx, proc->timestamp_tx); - if (ru->fh_north_out) ru->fh_north_out(ru); - } + if (ru->fh_north_out) + ru->fh_north_out(ru); } if (release_thread(&proc->mutex_rf_tx,&proc->instance_cnt_rf_tx,"rf_tx") < 0) break; @@ -2205,7 +2114,7 @@ int start_write_thread(RU_t *ru) { void init_RU_proc(RU_t *ru) { int i=0; RU_proc_t *proc; - pthread_attr_t *attr_FH=NULL, *attr_FH1=NULL, *attr_prach=NULL, *attr_asynch=NULL, *attr_synch=NULL, *attr_emulateRF=NULL, *attr_ctrl=NULL, *attr_prach_br=NULL; + pthread_attr_t *attr_FH=NULL, *attr_FH1=NULL, *attr_prach=NULL, *attr_asynch=NULL, *attr_synch=NULL, *attr_ctrl=NULL, *attr_prach_br=NULL; //pthread_attr_t *attr_fep=NULL; LOG_I(PHY,"Initializing RU proc %d (%s,%s),\n",ru->idx,NB_functions[ru->function],NB_timing[ru->if_timing]); proc = &ru->proc; @@ -2215,7 +2124,6 @@ void init_RU_proc(RU_t *ru) { proc->instance_cnt_synch = -1; proc->instance_cnt_FH = -1; proc->instance_cnt_FH1 = -1; - proc->instance_cnt_emulateRF = -1; proc->instance_cnt_asynch_rxtx = -1; proc->instance_cnt_ru = -1; proc->instance_cnt_eNBs = -1; @@ -2232,20 +2140,17 @@ void init_RU_proc(RU_t *ru) { pthread_mutex_init( &proc->mutex_synch,NULL); pthread_mutex_init( &proc->mutex_FH,NULL); pthread_mutex_init( &proc->mutex_FH1,NULL); - pthread_mutex_init( &proc->mutex_emulateRF,NULL); pthread_mutex_init( &proc->mutex_eNBs, NULL); pthread_mutex_init( &proc->mutex_ru,NULL); pthread_cond_init( &proc->cond_prach, NULL); pthread_cond_init( &proc->cond_FH, NULL); pthread_cond_init( &proc->cond_FH1, NULL); - pthread_cond_init( &proc->cond_emulateRF, NULL); pthread_cond_init( &proc->cond_asynch_rxtx, NULL); pthread_cond_init( &proc->cond_synch,NULL); pthread_cond_init( &proc->cond_eNBs, NULL); pthread_cond_init( &proc->cond_ru_thread,NULL); pthread_attr_init( &proc->attr_FH); pthread_attr_init( &proc->attr_FH1); - pthread_attr_init( &proc->attr_emulateRF); pthread_attr_init( &proc->attr_prach); pthread_attr_init( &proc->attr_synch); pthread_attr_init( &proc->attr_asynch_rxtx); @@ -2289,9 +2194,6 @@ void init_RU_proc(RU_t *ru) { pthread_create( &proc->pthread_rf_tx, NULL, rf_tx, (void *)ru ); #endif - if (ru->emulate_rf) - pthread_create( &proc->pthread_emulateRF, attr_emulateRF, emulatedRF_thread, (void *)proc ); - if (get_thread_parallel_conf() == PARALLEL_RU_L1_SPLIT || get_thread_parallel_conf() == PARALLEL_RU_L1_TRX_SPLIT) pthread_create( &proc->pthread_FH1, attr_FH1, ru_thread_tx, (void *)ru ); diff --git a/executables/lte-softmodem.c b/executables/lte-softmodem.c index 6a7e607586..6dbfbbc7d6 100644 --- a/executables/lte-softmodem.c +++ b/executables/lte-softmodem.c @@ -138,7 +138,6 @@ RU_t **RCconfig_RU(int nb_RU,int nb_L1_inst,PHY_VARS_eNB ***eNB,uint64_t *ru_mas RU_t **RCconfig_RU(int nb_RU,int nb_L1_inst,PHY_VARS_eNB ***eNB,uint64_t *ru_mask,pthread_mutex_t *ru_mutex,pthread_cond_t *ru_cond); int transmission_mode=1; -int emulate_rf = 0; int numerology = 0; THREAD_STRUCT thread_struct; diff --git a/executables/lte-softmodem.h b/executables/lte-softmodem.h index 8c55f0dc65..db6136c351 100644 --- a/executables/lte-softmodem.h +++ b/executables/lte-softmodem.h @@ -127,7 +127,6 @@ extern int sync_var; extern int transmission_mode; extern double cpuf; -extern int emulate_rf; extern int numerology; extern int usrp_tx_thread; diff --git a/executables/nr-ru.c b/executables/nr-ru.c index c0da88e634..39499227a8 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -567,25 +567,6 @@ void fh_if4p5_north_out(RU_t *ru) { stop_meas(&ru->tx_fhaul); } -void *emulatedRF_thread(void *param) { - RU_proc_t *proc = (RU_proc_t *) param; - int microsec = 500; // length of time to sleep, in miliseconds - struct timespec req = {0}; - req.tv_sec = 0; - req.tv_nsec = (numerology>0)? ((microsec * 1000L)/numerology):(microsec * 1000L)*2; - wait_sync("emulatedRF_thread"); - - while(!oai_exit) { - nanosleep(&req, (struct timespec *)NULL); - pthread_mutex_lock(&proc->mutex_emulateRF); - ++proc->instance_cnt_emulateRF; - pthread_mutex_unlock(&proc->mutex_emulateRF); - pthread_cond_signal(&proc->cond_emulateRF); - } - - return 0; -} - static void rx_rf(RU_t *ru, int *frame, int *slot) { RU_proc_t *proc = &ru->proc; @@ -606,14 +587,7 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) openair0_timestamp ts; unsigned int rxs; - if(emulate_rf) { - wait_on_condition(&proc->mutex_emulateRF,&proc->cond_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread"); - release_thread(&proc->mutex_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread"); - rxs = samples_per_slot; - ts = old_ts + rxs; - } else { - rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_per_slot, nb); - } + rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_per_slot, nb); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 ); proc->timestamp_rx = ts-ru->ts_offset; @@ -683,10 +657,8 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) *slot = proc->tti_rx; } - if (!emulate_rf) { - metadata mt = {.slot = *slot, .frame = *frame}; - gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0], sizeof(c16_t), 1, samples_per_slot, 0, &mt); - } + metadata mt = {.slot = *slot, .frame = *frame}; + gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0], sizeof(c16_t), 1, samples_per_slot, 0, &mt); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, (proc->timestamp_rx+ru->ts_offset)&0xffffffff ); @@ -996,11 +968,8 @@ void ru_tx_func(void *param) { processingData_RU_t *info = (processingData_RU_t *) param; RU_t *ru = info->ru; - NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; int frame_tx = info->frame_tx; int slot_tx = info->slot_tx; - int print_frame = 8; - char filename[40]; // do TX front-end processing if needed (precoding and/or IDFTs) if (ru->feptx_prec) @@ -1010,44 +979,12 @@ void ru_tx_func(void *param) if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) ru->feptx_ofdm(ru, frame_tx, slot_tx); - if(!emulate_rf) { - // do outgoing fronthaul (south) if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) - ru->fh_south_out(ru, frame_tx, slot_tx, info->timestamp_tx); + // do outgoing fronthaul (south) if needed + if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) + ru->fh_south_out(ru, frame_tx, slot_tx, info->timestamp_tx); - if (ru->fh_north_out) - ru->fh_north_out(ru); - } else { - if(frame_tx == print_frame) { - for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { - if(slot_tx == 0) { - sprintf(filename,"gNBdataF_frame%d_sl%d.m", print_frame, slot_tx); - LOG_M(filename,"txdataF_frame",&ru->gNB_list[0]->common_vars.txdataF[i][0],fp->samples_per_frame_wCP, 1, 1); - sprintf(filename,"tx%ddataF_frame%d_sl%d.m", i, print_frame, slot_tx); - LOG_M(filename,"txdataF_frame",&ru->common.txdataF[i][0],fp->samples_per_frame_wCP, 1, 1); - sprintf(filename,"tx%ddataF_BF_frame%d_sl%d.m", i, print_frame, slot_tx); - LOG_M(filename,"txdataF_BF_frame",&ru->common.txdataF_BF[i][0],fp->samples_per_subframe_wCP, 1, 1); - } - - if(slot_tx == 9) { - sprintf(filename,"tx%ddata_frame%d.m", i, print_frame); - LOG_M(filename,"txdata_frame",&ru->common.txdata[i][0],fp->samples_per_frame, 1, 1); - sprintf(filename,"tx%ddata_frame%d.dat", i, print_frame); - FILE *output_fd = fopen(filename,"w"); - - if (output_fd) { - fwrite(&ru->common.txdata[i][0], - sizeof(int32_t), - fp->samples_per_frame, - output_fd); - fclose(output_fd); - } else { - LOG_E(PHY,"Cannot write to file %s\n",filename); - } - }//if(slot_tx == 9) - }//for (i=0; inb_tx; i++) - }//if(frame_tx == print_frame) - }//else emulate_rf + if (ru->fh_north_out) + ru->fh_north_out(ru); } /* @brief wait for the next RX TTI to be free @@ -1119,54 +1056,50 @@ void *ru_thread(void *param) fill_rf_config(ru, ru->rf_config_file); fill_split7_2_config(&ru->openair0_cfg.split7, &ru->config, fp->slots_per_frame, fp->ofdm_symbol_size); - if(!emulate_rf) { - // Start IF device if any - if (ru->nr_start_if) { - LOG_I(PHY, "starting transport\n"); - ret = openair0_transport_load(&ru->ifdevice, &ru->openair0_cfg, &ru->eth_params); - AssertFatal(ret == 0, "RU %u: openair0_transport_init() ret %d: cannot initialize transport protocol\n", ru->idx, ret); - - if (ru->ifdevice.get_internal_parameter != NULL) { - /* it seems the device can "overwrite" (request?) to set the callbacks - * for fh_south_in()/fh_south_out() differently */ - void *t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_in"); - if (t != NULL) - ru->fh_south_in = t; - t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_out"); - if (t != NULL) - ru->fh_south_out = t; - } else { - - malloc_IF4p5_buffer(ru); - } - - int cpu = sched_getcpu(); - if (ru->ru_thread_core > -1 && cpu != ru->ru_thread_core) { - /* we start the ru_thread using threadCreate(), which already sets CPU - * affinity; let's force it here again as per feature request #732 */ - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(ru->ru_thread_core, &cpuset); - int ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - AssertFatal(ret == 0, "Error in pthread_getaffinity_np(): ret: %d, errno: %d", ret, errno); - LOG_I(PHY, "RU %d: manually set CPU affinity to CPU %d\n", ru->idx, ru->ru_thread_core); - } - - LOG_I(PHY,"Starting IF interface for RU %d, nb_rx %d\n",ru->idx,ru->nb_rx); - AssertFatal(ru->nr_start_if(ru,NULL) == 0, "Could not start the IF device\n"); - - if (ru->has_ctrl_prt > 0) { - if (ru->if_south == LOCAL_RF) ret = connect_rau(ru); - else ret = attach_rru(ru); - - AssertFatal(ret==0,"Cannot connect to remote radio\n"); - } + // Start IF device if any + if (ru->nr_start_if) { + LOG_I(PHY, "starting transport\n"); + ret = openair0_transport_load(&ru->ifdevice, &ru->openair0_cfg, &ru->eth_params); + AssertFatal(ret == 0, "RU %u: openair0_transport_init() ret %d: cannot initialize transport protocol\n", ru->idx, ret); + if (ru->ifdevice.get_internal_parameter != NULL) { + /* it seems the device can "overwrite" (request?) to set the callbacks + * for fh_south_in()/fh_south_out() differently */ + void *t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_in"); + if (t != NULL) + ru->fh_south_in = t; + t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_out"); + if (t != NULL) + ru->fh_south_out = t; + } else { + malloc_IF4p5_buffer(ru); } - else if (ru->if_south == LOCAL_RF) { // configure RF parameters only - ret = openair0_device_load(&ru->rfdevice,&ru->openair0_cfg); - AssertFatal(ret==0,"Cannot connect to local radio\n"); + + int cpu = sched_getcpu(); + if (ru->ru_thread_core > -1 && cpu != ru->ru_thread_core) { + /* we start the ru_thread using threadCreate(), which already sets CPU + * affinity; let's force it here again as per feature request #732 */ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(ru->ru_thread_core, &cpuset); + int ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); + AssertFatal(ret == 0, "Error in pthread_getaffinity_np(): ret: %d, errno: %d", ret, errno); + LOG_I(PHY, "RU %d: manually set CPU affinity to CPU %d\n", ru->idx, ru->ru_thread_core); } + + LOG_I(PHY,"Starting IF interface for RU %d, nb_rx %d\n",ru->idx,ru->nb_rx); + AssertFatal(ru->nr_start_if(ru,NULL) == 0, "Could not start the IF device\n"); + + if (ru->has_ctrl_prt > 0) { + if (ru->if_south == LOCAL_RF) ret = connect_rau(ru); + else ret = attach_rru(ru); + + AssertFatal(ret==0,"Cannot connect to remote radio\n"); + } + + } else if (ru->if_south == LOCAL_RF) { // configure RF parameters only + ret = openair0_device_load(&ru->rfdevice,&ru->openair0_cfg); + AssertFatal(ret==0,"Cannot connect to local radio\n"); } if (setup_RU_buffers(ru)!=0) { @@ -1181,23 +1114,23 @@ void *ru_thread(void *param) pthread_mutex_unlock(&RC.ru_mutex); wait_sync("ru_thread"); - if(!emulate_rf) { - // Start RF device if any - if (ru->start_rf) { - if (ru->start_rf(ru) != 0) - LOG_E(HW,"Could not start the RF device\n"); - else LOG_I(PHY,"RU %d rf device ready\n",ru->idx); - } else LOG_I(PHY,"RU %d no rf device\n",ru->idx); + // Start RF device if any + if (ru->start_rf) { + if (ru->start_rf(ru) != 0) + LOG_E(HW, "Could not start the RF device\n"); + else + LOG_I(PHY, "RU %d rf device ready\n", ru->idx); + } else + LOG_I(PHY, "RU %d no rf device\n", ru->idx); - LOG_I(PHY, "RU %d RF started cpu_meas_enabled %d\n", ru->idx, cpu_meas_enabled); - // start trx write thread - if(usrp_tx_thread == 1) { - if (ru->start_write_thread) { - if(ru->start_write_thread(ru) != 0) { - LOG_E(HW,"Could not start tx write thread\n"); - } else { - LOG_I(PHY,"tx write thread ready\n"); - } + LOG_I(PHY, "RU %d RF started cpu_meas_enabled %d\n", ru->idx, cpu_meas_enabled); + // start trx write thread + if (usrp_tx_thread == 1) { + if (ru->start_write_thread) { + if (ru->start_write_thread(ru) != 0) { + LOG_E(HW, "Could not start tx write thread\n"); + } else { + LOG_I(PHY, "tx write thread ready\n"); } } } @@ -1358,7 +1291,6 @@ void init_RU_proc(RU_t *ru) { proc = &ru->proc; memset((void *)proc,0,sizeof(RU_proc_t)); proc->ru = ru; - proc->instance_cnt_emulateRF = -1; proc->first_rx = 1; proc->first_tx = 1; proc->frame_offset = 0; @@ -1367,11 +1299,6 @@ void init_RU_proc(RU_t *ru) { for (i=0; i<10; i++) proc->symbol_mask[i]=0; - pthread_mutex_init( &proc->mutex_emulateRF,NULL); - pthread_cond_init( &proc->cond_emulateRF, NULL); - - if(emulate_rf) - threadCreate( &proc->pthread_emulateRF, emulatedRF_thread, (void *)proc, "emulateRF", -1, OAI_PRIORITY_RT ); LOG_I(PHY, "Initialized RU proc %d (%s,%s),\n", ru->idx, NB_functions[ru->function], NB_timing[ru->if_timing]); } diff --git a/executables/nr-softmodem-common.h b/executables/nr-softmodem-common.h index 3ab6d1e73d..61823d43cd 100644 --- a/executables/nr-softmodem-common.h +++ b/executables/nr-softmodem-common.h @@ -133,7 +133,6 @@ extern int sync_var; extern int transmission_mode; extern double cpuf; -extern int emulate_rf; extern int numerology; extern int usrp_tx_thread; void wait_gNBs(void); diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 5cc5e3a4fc..9a0ee6ec9e 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -119,7 +119,6 @@ double rx_gain_off = 0.0; static int tx_max_power[MAX_NUM_CCs]; /* = {0,0}*/; int chain_offset = 0; -int emulate_rf = 0; int numerology = 0; double cpuf; diff --git a/executables/softmodem-common.h b/executables/softmodem-common.h index 33d4f4cf04..d122b1a1d5 100644 --- a/executables/softmodem-common.h +++ b/executables/softmodem-common.h @@ -96,7 +96,6 @@ extern "C" #define CONFIG_HLP_NUMEROLOGY "adding numerology for 5G\n" #define CONFIG_HLP_BAND "band index\n" -#define CONFIG_HLP_EMULATE_RF "Emulated RF enabled(disable by defult)\n" #define CONFIG_HLP_PARALLEL_CMD "three config for level of parallelism 'PARALLEL_SINGLE_THREAD', 'PARALLEL_RU_L1_SPLIT', or 'PARALLEL_RU_L1_TRX_SPLIT'\n" #define CONFIG_HLP_WORKER_CMD "two option for worker 'WORKER_DISABLE' or 'WORKER_ENABLE'\n" #define CONFIG_HLP_USRP_THREAD "having extra thead for usrp tx\n" @@ -128,7 +127,6 @@ extern "C" #define CHAIN_OFFSET softmodem_params.chain_offset #define NUMEROLOGY softmodem_params.numerology #define BAND softmodem_params.band -#define EMULATE_RF softmodem_params.emulate_rf #define CLOCK_SOURCE softmodem_params.clock_source #define TIMING_SOURCE softmodem_params.timing_source #define TUNE_OFFSET softmodem_params.tune_offset @@ -164,7 +162,6 @@ extern int usrp_tx_thread; {"q" , CONFIG_HLP_STMON, PARAMFLAG_BOOL, .iptr=&cpu_meas_enabled, .defintval=0, TYPE_INT, 0}, \ {"numerology" , CONFIG_HLP_NUMEROLOGY, 0, .iptr=&NUMEROLOGY, .defintval=1, TYPE_INT, 0}, \ {"band" , CONFIG_HLP_BAND, 0, .iptr=&BAND, .defintval=78, TYPE_INT, 0}, \ - {"emulate-rf" , CONFIG_HLP_EMULATE_RF, PARAMFLAG_BOOL, .iptr=&EMULATE_RF, .defintval=0, TYPE_INT, 0}, \ {"parallel-config", CONFIG_HLP_PARALLEL_CMD, 0, .strptr=¶llel_config, .defstrval=NULL, TYPE_STRING, 0}, \ {"worker-config", CONFIG_HLP_WORKER_CMD, 0, .strptr=&worker_config, .defstrval=NULL, TYPE_STRING, 0}, \ {"noS1", CONFIG_HLP_NOS1, PARAMFLAG_BOOL, .uptr=&noS1, .defintval=0, TYPE_UINT, 0}, \ @@ -217,7 +214,6 @@ extern int usrp_tx_thread; { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ - { .s5 = { NULL } }, \ { .s3a = { config_checkstr_assign_integer, \ {"MONOLITHIC", "PNF", "VNF", "AERIAL","UE_STUB_PNF","UE_STUB_OFFNET","STANDALONE_PNF"}, \ {NFAPI_MONOLITHIC, NFAPI_MODE_PNF, NFAPI_MODE_VNF, NFAPI_MODE_AERIAL,NFAPI_UE_STUB_PNF,NFAPI_UE_STUB_OFFNET,NFAPI_MODE_STANDALONE_PNF}, \ @@ -320,7 +316,6 @@ typedef struct { int phy_test; int do_ra; uint8_t sl_mode; - int emulate_rf; int chain_offset; int numerology; int band; diff --git a/openair1/PHY/defs_RU.h b/openair1/PHY/defs_RU.h index a2c39852f5..e72465c4a9 100644 --- a/openair1/PHY/defs_RU.h +++ b/openair1/PHY/defs_RU.h @@ -255,7 +255,6 @@ typedef struct RU_proc_t_s { int instance_cnt_feptx; /// \internal This variable is protected by \ref mutex_ru_thread int instance_cnt_ru; - int instance_cnt_emulateRF; /// pthread structure for RU FH processing thread pthread_t pthread_FH; pthread_t pthread_FH1; @@ -271,8 +270,6 @@ typedef struct RU_proc_t_s { pthread_t pthread_fep[8]; /// pthread struct for RU TX FEP worker thread pthread_t pthread_feptx; - /// pthread struct for emulated RF - pthread_t pthread_emulateRF; /// pthread structure for asychronous RX/TX processing thread pthread_t pthread_asynch_rxtx; /// flag to indicate first RX acquisition @@ -296,8 +293,6 @@ typedef struct RU_proc_t_s { pthread_attr_t attr_fep; /// pthread attributes for worker feptx thread pthread_attr_t attr_feptx; - /// pthread attributes for emulated RF - pthread_attr_t attr_emulateRF; /// scheduling parameters for RU FH thread struct sched_param sched_param_FH; struct sched_param sched_param_FH1; @@ -324,8 +319,6 @@ typedef struct RU_proc_t_s { pthread_cond_t cond_fep[8]; /// condition varible for RU TX FEP thread pthread_cond_t cond_feptx; - /// condition varible for emulated RF - pthread_cond_t cond_emulateRF; /// condition variable for eNB signal pthread_cond_t cond_eNBs; /// condition variable for gNB signal @@ -353,8 +346,6 @@ typedef struct RU_proc_t_s { pthread_mutex_t mutex_feptx; /// mutex for ru_thread pthread_mutex_t mutex_ru; - /// mutex for emulated RF thread - pthread_mutex_t mutex_emulateRF; /// symbol mask for IF4p5 reception per subframe uint32_t symbol_mask[10]; /// time measurements for each subframe @@ -394,7 +385,6 @@ typedef struct RU_proc_t_s { /// pipeline ready state int ru_rx_ready; int ru_tx_ready; - int emulate_rf_busy; /// structure for precoding thread RU_prec_t prec[16]; @@ -407,7 +397,6 @@ typedef enum { REMOTE_IF4p5 =3, REMOTE_IF1pp =4, MAX_RU_IF_TYPES =5 - //EMULATE_RF =6 } RU_if_south_t; @@ -453,8 +442,6 @@ typedef struct RU_t_s { node_function_t function; /// Ethernet parameters for fronthaul interface eth_params_t eth_params; - /// flag to indicate RF emulation mode - int emulate_rf; /// numerology index int numerology; /// flag to indicate the RU is in sync with a master reference diff --git a/openair1/PHY/defs_eNB.h b/openair1/PHY/defs_eNB.h index 68ebf65462..2a889c4570 100644 --- a/openair1/PHY/defs_eNB.h +++ b/openair1/PHY/defs_eNB.h @@ -255,11 +255,6 @@ typedef struct { tpool_t *threadPool; int nbDecode; notifiedFIFO_t *respDecode; - pthread_mutex_t mutex_emulateRF; - int instance_cnt_emulateRF; - pthread_t pthread_emulateRF; - pthread_attr_t attr_emulateRF; - pthread_cond_t cond_emulateRF; int first_rx; } L1_rxtx_proc_t; diff --git a/openair1/SIMULATION/LTE_PHY/dlsim.c b/openair1/SIMULATION/LTE_PHY/dlsim.c index 5e6140db9d..a593a0dfb4 100644 --- a/openair1/SIMULATION/LTE_PHY/dlsim.c +++ b/openair1/SIMULATION/LTE_PHY/dlsim.c @@ -90,7 +90,6 @@ static int cmpdouble(const void *p1, const void *p2) { } RAN_CONTEXT_t RC; -int emulate_rf = 0; void handler(int sig) { void *array[10]; size_t size; diff --git a/openair1/SIMULATION/LTE_PHY/mbmssim.c b/openair1/SIMULATION/LTE_PHY/mbmssim.c index ac50bf62bd..dac151e4fe 100644 --- a/openair1/SIMULATION/LTE_PHY/mbmssim.c +++ b/openair1/SIMULATION/LTE_PHY/mbmssim.c @@ -89,8 +89,6 @@ int n_rx_dropped = 0; /*!< \brief initial max process time for rx */ THREAD_STRUCT thread_struct; -int emulate_rf = 0; - void handler(int sig) { void *array[10]; size_t size; From 946939e8ccca9ea864d662e2ab4d10f542c62fcf Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 14 Aug 2025 16:35:49 +0200 Subject: [PATCH 48/49] CI: integrate RFemulator phytest in timing-phytest pipeline --- .../gnb_phytest_rfemulator_run_100_2x2.xml | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 ci-scripts/xml_files/gnb_phytest_rfemulator_run_100_2x2.xml diff --git a/ci-scripts/xml_files/gnb_phytest_rfemulator_run_100_2x2.xml b/ci-scripts/xml_files/gnb_phytest_rfemulator_run_100_2x2.xml new file mode 100644 index 0000000000..3d2efb5084 --- /dev/null +++ b/ci-scripts/xml_files/gnb_phytest_rfemulator_run_100_2x2.xml @@ -0,0 +1,70 @@ + + + gNB-PHY-Test-100-2x2-RFemu + Timing phytest 100 MHz 2x2 RFemulator + tasks + +100000 390101 000001 390109 200000 + + + + Custom_Command + Disable Sleep States + caracal + sudo cpupower idle-set -D 0 + + + Custom_Command + true + Enable Sleep States + caracal + sudo cpupower idle-set -E + + + + Initialize_eNB + Initialize gNB USRP + caracal + -O ci-scripts/conf_files/gnb.band79.273prb.usrpn300.phytest-dora.conf --phy-test -q -U 768 -T 273 -t 23 -D 127 -m 23 -M 273 -l 2 --device.name rf_emulator --rf_emulator.noise_level_dBFS -36 --log_config.global_log_options level,nocolor,time + datalog_rt_stats.100.2x2.yaml + NR + numactl --cpunodebind=netdev:enp94s0f0np0 --membind=netdev:enp94s0f0np0 + + + + IdleSleep + Sleep + 60 + + + + + Terminate_eNB + caracal + true + Terminate gNB + NR + + + From 42ad189bf3904e5bffe88543d0edfc57d2db2fc8 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Thu, 14 Aug 2025 16:30:09 +0000 Subject: [PATCH 49/49] feat(rf_emulator): documentation --- doc/README.md | 1 + radio/emulator/README.md | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 radio/emulator/README.md diff --git a/doc/README.md b/doc/README.md index 40f838750a..8ed2d8896b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -118,6 +118,7 @@ Some directories under `radio` contain READMEs: - [IQPlayer](../radio/iqplayer/DOC/iqrecordplayer_usage.md), and [general documentation](./iqrecordplayer_usage.md) - [fhi_72](../radio/fhi_72/README.md) - [vrtsim](../radio/vrtsim/README.md) +- [rf_emulator](../radio/emulator/README.md) The other SDRs (AW2S, LimeSDR, ...) have no READMEs. diff --git a/radio/emulator/README.md b/radio/emulator/README.md new file mode 100644 index 0000000000..b3b58ba6a7 --- /dev/null +++ b/radio/emulator/README.md @@ -0,0 +1,25 @@ +# RF emulator library + +The RF emulator is a simple network stack terminator on the RF side. +It is meant to run the network stack without real radio and without RF simulation. +It simply drops TX signal and generate noise as an RX signal. +It can be used in `phy-test` mode to simply benchmark the network stack in a determined scenario. +It synchronizes the network stack on the real time clock of the host to guarantee a realistic behavior of the network stack. +It detects and report late samples reads and writes to assess the real time behavior of the network stack. + +## Building + +`librf_emulator.so` is built by default by the `build_oai.sh` script. +It is then located in the root build directory (usually `cmake_targets/ran_build/build`). + +## Usage + +### Enabling `rf_emulator` + +Use the option `--device.name rf_emulator` of the softmodem to use the RF emulator. + +### Options + +Two options are available to configure the RF emulator: +* `--rf_emulator.enable_noise ` Enables (enable = 1) or disables (enable = 0) noise generation on RX. By default noise generation is enabled. +* `--rf_emulator.noise_level_dBFS ` Sets the level of noise generated in dB. If omitted no noise is generated.