From 17a8ec6cddd99c22e3c3efd270234a9a63b82d69 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Wed, 28 Apr 2021 12:55:59 +0100 Subject: [PATCH 01/50] Using estimated COUNT (from NAS overflow counter and RX SQN) for decription of NAS messaages. Should avoid issues decripting messages when the COUNT is larger than 256. --- srsue/src/stack/upper/nas.cc | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/srsue/src/stack/upper/nas.cc b/srsue/src/stack/upper/nas.cc index 8335e6203..204d68893 100644 --- a/srsue/src/stack/upper/nas.cc +++ b/srsue/src/stack/upper/nas.cc @@ -772,8 +772,8 @@ bool nas::integrity_check(byte_buffer_t* pdu) mac[3]); // Updated local count (according to TS 24.301 Sec. 4.4.3.3) - if (pdu->msg[5] != ctxt.rx_count) { - logger.info("Update local count to received value %d", pdu->msg[5]); + if (count_est != ctxt.rx_count) { + logger.info("Update local count to estimated count %d", count_est); ctxt.rx_count = count_est; } return true; @@ -786,12 +786,17 @@ bool nas::integrity_check(byte_buffer_t* pdu) void nas::cipher_encrypt(byte_buffer_t* pdu) { byte_buffer_t pdu_tmp; + + if (ctxt.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { + logger.debug("Encrypting PDU. count=%d", ctxt.tx_count); + } + switch (ctxt.cipher_algo) { case CIPHERING_ALGORITHM_ID_EEA0: break; case CIPHERING_ALGORITHM_ID_128_EEA1: security_128_eea1(&k_nas_enc[16], - pdu->msg[5], + ctxt.tx_count, 0, // Bearer always 0 for NAS SECURITY_DIRECTION_UPLINK, &pdu->msg[6], @@ -801,7 +806,7 @@ void nas::cipher_encrypt(byte_buffer_t* pdu) break; case CIPHERING_ALGORITHM_ID_128_EEA2: security_128_eea2(&k_nas_enc[16], - pdu->msg[5], + ctxt.tx_count, 0, // Bearer always 0 for NAS SECURITY_DIRECTION_UPLINK, &pdu->msg[6], @@ -811,7 +816,7 @@ void nas::cipher_encrypt(byte_buffer_t* pdu) break; case CIPHERING_ALGORITHM_ID_128_EEA3: security_128_eea3(&k_nas_enc[16], - pdu->msg[5], + ctxt.tx_count, 0, // Bearer always 0 for NAS SECURITY_DIRECTION_UPLINK, &pdu->msg[6], @@ -828,12 +833,18 @@ void nas::cipher_encrypt(byte_buffer_t* pdu) void nas::cipher_decrypt(byte_buffer_t* pdu) { byte_buffer_t tmp_pdu; + + uint32_t count_est = (ctxt.rx_count & 0x00FFFF00u) | pdu->msg[5]; + if (ctxt.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { + logger.debug("Decrypting PDU. Local: count=%d, Received: count=%d", ctxt.rx_count, count_est); + } + switch (ctxt.cipher_algo) { case CIPHERING_ALGORITHM_ID_EEA0: break; case CIPHERING_ALGORITHM_ID_128_EEA1: security_128_eea1(&k_nas_enc[16], - pdu->msg[5], + count_est, 0, // Bearer always 0 for NAS SECURITY_DIRECTION_DOWNLINK, &pdu->msg[6], @@ -843,7 +854,7 @@ void nas::cipher_decrypt(byte_buffer_t* pdu) break; case CIPHERING_ALGORITHM_ID_128_EEA2: security_128_eea2(&k_nas_enc[16], - pdu->msg[5], + count_est, 0, // Bearer always 0 for NAS SECURITY_DIRECTION_DOWNLINK, &pdu->msg[6], @@ -854,7 +865,7 @@ void nas::cipher_decrypt(byte_buffer_t* pdu) break; case CIPHERING_ALGORITHM_ID_128_EEA3: security_128_eea3(&k_nas_enc[16], - pdu->msg[5], + count_est, 0, // Bearer always 0 for NAS SECURITY_DIRECTION_DOWNLINK, &pdu->msg[6], From acf098be92be9ff75a010d61768f8e47c34a8ab9 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Fri, 30 Apr 2021 13:51:08 +0200 Subject: [PATCH 02/50] epc,s1ap: remove use of rand() --- srsepc/src/mme/s1ap.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/srsepc/src/mme/s1ap.cc b/srsepc/src/mme/s1ap.cc index ef4725e4a..866202a5f 100644 --- a/srsepc/src/mme/s1ap.cc +++ b/srsepc/src/mme/s1ap.cc @@ -16,6 +16,7 @@ #include "srsran/common/liblte_security.h" #include #include // for printing uint64_t +#include namespace srsepc { @@ -53,7 +54,11 @@ int s1ap::init(const s1ap_args_t& s1ap_args) { m_s1ap_args = s1ap_args; srsran::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &m_plmn); - m_next_m_tmsi = rand(); + + std::random_device rd; + std::mt19937 generator(rd()); + std::uniform_int_distribution distr(0, std::numeric_limits::max()); + m_next_m_tmsi = distr(generator); // Get pointer to the HSS m_hss = hss::get_instance(); From bbea3dd6a147c3e5bad444963b83c714f9cbd033 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Fri, 30 Apr 2021 09:45:12 +0200 Subject: [PATCH 03/50] byte_buffer: fix compilation when buffer pool log is enabled --- lib/include/srsran/common/buffer_pool.h | 2 +- lib/include/srsran/common/byte_buffer.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/include/srsran/common/buffer_pool.h b/lib/include/srsran/common/buffer_pool.h index 9b0ef1ef0..407ca9201 100644 --- a/lib/include/srsran/common/buffer_pool.h +++ b/lib/include/srsran/common/buffer_pool.h @@ -80,7 +80,7 @@ public: std::map buffer_cnt; for (uint32_t i = 0; i < pool.size(); i++) { if (std::find(free_list.cbegin(), free_list.cend(), pool[i]) == free_list.cend()) { - buffer_cnt[strlen(used[i]->debug_name) ? pool[i]->debug_name : "Undefined"]++; + buffer_cnt[strlen(pool[i]->debug_name) ? pool[i]->debug_name : "Undefined"]++; } } std::map::iterator it; diff --git a/lib/include/srsran/common/byte_buffer.h b/lib/include/srsran/common/byte_buffer.h index e33bc3a32..f6d2e6479 100644 --- a/lib/include/srsran/common/byte_buffer.h +++ b/lib/include/srsran/common/byte_buffer.h @@ -19,6 +19,7 @@ #include //#define SRSRAN_BUFFER_POOL_LOG_ENABLED +#define SRSRAN_BUFFER_POOL_LOG_NAME_LEN 128 namespace srsran { From a8e181971c562ed32684a7748413e089c16167cb Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Thu, 22 Apr 2021 18:22:56 +0200 Subject: [PATCH 04/50] Refactored PHY NR procedures for supporting DCI formats 0_1 and 1_1 --- .../interfaces/rrc_nr_interface_types.h | 8 +- lib/include/srsran/phy/phch/dci_nr.h | 11 +- lib/include/srsran/phy/phch/phch_cfg_nr.h | 9 +- lib/include/srsran/phy/phch/ra_dl_nr.h | 29 +++- lib/include/srsran/phy/phch/ra_ul_nr.h | 28 +++- lib/src/phy/phch/dci_nr.c | 116 ++++++++++------ lib/src/phy/phch/ra_dl_nr.c | 131 +++++++++++------- lib/src/phy/phch/ra_helper.h | 44 ++++++ lib/src/phy/phch/ra_nr.c | 77 +++++----- lib/src/phy/phch/ra_ul_nr.c | 131 +++++++++++++++--- lib/src/phy/phch/test/dci_nr_test.c | 8 +- lib/src/phy/phch/test/pdsch_nr_test.c | 14 +- lib/src/phy/phch/test/pusch_nr_test.c | 14 +- lib/src/phy/phch/test/sch_nr_test.c | 7 +- 14 files changed, 418 insertions(+), 209 deletions(-) diff --git a/lib/include/srsran/interfaces/rrc_nr_interface_types.h b/lib/include/srsran/interfaces/rrc_nr_interface_types.h index 6f4c520a0..01eacc809 100644 --- a/lib/include/srsran/interfaces/rrc_nr_interface_types.h +++ b/lib/include/srsran/interfaces/rrc_nr_interface_types.h @@ -93,11 +93,11 @@ struct phy_cfg_nr_t { dci_cfg.enable_transform_precoding = false; dci_cfg.dynamic_dual_harq_ack_codebook = false; dci_cfg.pusch_tx_config_non_codebook = false; - dci_cfg.pusch_dmrs_type2 = false; - dci_cfg.pusch_dmrs_double = false; dci_cfg.pusch_ptrs = false; dci_cfg.pusch_dynamic_betas = false; dci_cfg.pusch_alloc_type = pusch.alloc; + dci_cfg.pusch_dmrs_type = pusch.dmrs_type; + dci_cfg.pusch_dmrs_max_len = pusch.dmrs_max_length; // Format 1_1 specific configuration (for PDSCH only) dci_cfg.nof_dl_bwp = 0; @@ -112,12 +112,12 @@ struct phy_cfg_nr_t { dci_cfg.pdsch_rm_pattern2 = false; dci_cfg.pdsch_2cw = false; dci_cfg.multiple_scell = false; - dci_cfg.pdsch_dmrs_type2 = false; - dci_cfg.pdsch_dmrs_double = false; dci_cfg.pdsch_tci = false; dci_cfg.pdsch_cbg_flush = false; dci_cfg.pdsch_dynamic_bundling = false; dci_cfg.pdsch_alloc_type = pdsch.alloc; + dci_cfg.pdsch_dmrs_type = pdsch.dmrs_type; + dci_cfg.pdsch_dmrs_max_len = pdsch.dmrs_max_length; return dci_cfg; }; diff --git a/lib/include/srsran/phy/phch/dci_nr.h b/lib/include/srsran/phy/phch/dci_nr.h index 0cbaf099e..92986c1a8 100644 --- a/lib/include/srsran/phy/phch/dci_nr.h +++ b/lib/include/srsran/phy/phch/dci_nr.h @@ -57,11 +57,11 @@ typedef struct SRSRAN_API { bool enable_transform_precoding; ///< Set to true if PUSCH transform precoding is enabled bool dynamic_dual_harq_ack_codebook; ///< Set to true if HARQ-ACK codebook is set to dynamic with 2 sub-codebooks bool pusch_tx_config_non_codebook; ///< Set to true if PUSCH txConfig is set to non-codebook - bool pusch_dmrs_type2; ///< Set to true if PUSCH DMRS are type 2 - bool pusch_dmrs_double; ///< Set to true if PUSCH DMRS are 2 symbol long bool pusch_ptrs; ///< Set to true if PT-RS are enabled for PUSCH transmission bool pusch_dynamic_betas; ///< Set to true if beta offsets operation is not semi-static srsran_resource_alloc_t pusch_alloc_type; ///< PUSCH resource allocation type + srsran_dmrs_sch_type_t pusch_dmrs_type; ///< PUSCH DMRS type + srsran_dmrs_sch_len_t pusch_dmrs_max_len; ///< PUSCH DMRS maximum length /// Format 1_1 specific configuration (for PDSCH only) uint32_t nof_dl_bwp; ///< Number of DL BWPs excluding the initial UL BWP, mentioned in the TS as N_BWP_RRC @@ -74,13 +74,12 @@ typedef struct SRSRAN_API { bool pdsch_rm_pattern2; ///< Set to true if rateMatchPatternGroup2 is configured bool pdsch_2cw; ///< Set to true if maxNrofCodeWordsScheduledByDCI is set to 2 in any BWP bool multiple_scell; ///< Set to true if configured with multiple serving cell - bool pdsch_dmrs_type2; ///< Set to true if PDSCH DMRS are type 2 - bool pdsch_dmrs_double; ///< Set to true if PDSCH DMRS are 2 symbol long bool pdsch_tci; ///< Set to true if tci-PresentInDCI is enabled bool pdsch_cbg_flush; ///< Set to true if codeBlockGroupFlushIndicator is true bool pdsch_dynamic_bundling; ///< Set to true if prb-BundlingType is set to dynamicBundling - srsran_resource_alloc_t pdsch_alloc_type; ///< PDSCH resource allocation type, set to 0 for default - + srsran_resource_alloc_t pdsch_alloc_type; ///< PDSCH resource allocation type, set to 0 for default + srsran_dmrs_sch_type_t pdsch_dmrs_type; ///< PDSCH DMRS type + srsran_dmrs_sch_len_t pdsch_dmrs_max_len; ///< PDSCH DMRS maximum length } srsran_dci_cfg_nr_t; /** diff --git a/lib/include/srsran/phy/phch/phch_cfg_nr.h b/lib/include/srsran/phy/phch/phch_cfg_nr.h index 99d028bfe..57a02f21b 100644 --- a/lib/include/srsran/phy/phch/phch_cfg_nr.h +++ b/lib/include/srsran/phy/phch/phch_cfg_nr.h @@ -184,10 +184,10 @@ typedef struct SRSRAN_API { bool scrambling_id_present; uint32_t scambling_id; // Identifier used to initialize data scrambling (0-1023) + srsran_dmrs_sch_type_t dmrs_type; + srsran_dmrs_sch_len_t dmrs_max_length; struct { - srsran_dmrs_sch_type_t type; srsran_dmrs_sch_add_pos_t additional_pos; - srsran_dmrs_sch_len_t length; bool scrambling_id0_present; uint32_t scrambling_id0; bool scrambling_id1_present; @@ -196,9 +196,7 @@ typedef struct SRSRAN_API { } dmrs_typeA; struct { - srsran_dmrs_sch_type_t type; srsran_dmrs_sch_add_pos_t additional_pos; - srsran_dmrs_sch_len_t length; bool scrambling_id0_present; uint32_t scrambling_id0; bool scrambling_id1_present; @@ -224,7 +222,8 @@ typedef struct SRSRAN_API { srsran_csi_rs_nzp_set_t nzp_csi_rs_sets[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS]; /// PUSCH only - srsran_beta_offsets_t beta_offsets; /// Semi-static only. + srsran_beta_offsets_t beta_offsets; /// Semi-static only. + bool enable_transform_precoder; /// Enables transform precoding float scaling; /// Indicates a scaling factor to limit the number of resource elements assigned to UCI on PUSCH. } srsran_sch_hl_cfg_nr_t; diff --git a/lib/include/srsran/phy/phch/ra_dl_nr.h b/lib/include/srsran/phy/phch/ra_dl_nr.h index ecc628f9d..90f6b20cd 100644 --- a/lib/include/srsran/phy/phch/ra_dl_nr.h +++ b/lib/include/srsran/phy/phch/ra_dl_nr.h @@ -62,17 +62,32 @@ SRSRAN_API int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, */ SRSRAN_API int srsran_ra_dl_nr_time_default_A(uint32_t m, srsran_dmrs_sch_typeA_pos_t dmrs_typeA_pos, srsran_sch_grant_nr_t* grant); + +/** + * @brief Calculates the number of front load symbols + * + * @param cfg PDSCH NR configuration by upper layers + * @param dci Provides PDSCH NR DCI + * @param[out] dmrs_duration + * @return SRSRAN_SUCCESS if provided arguments are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ra_dl_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_dl_nr_t* dci, + srsran_dmrs_sch_len_t* dmrs_duration); + /** - * @brief Calculates the number of PDSCH-DMRS CDM groups without data for DCI format 1_0 + * @brief Calculates the number of DMRS CDM groups without data * - * @remark Defined by TS 38.214 V15.10.0 5.1.6.1.3 CSI-RS for mobility + * @remark Defined by TS 38.214 V15.10.0 5.1.6.2 DM-RS reception procedure * - * @param cfg PDSCH-DMRS NR configuration by upper layers - * @param[out] grant Provides grant pointer to fill - * @return Returns SRSRAN_SUCCESS if the provided data is valid, otherwise it returns SRSRAN_ERROR code + * @param cfg PDSCH NR configuration by upper layers + * @param dci Provides PUSCH NR DCI + * @return The number of DMRS CDM groups without data if the provided data is valid, otherwise it returns SRSRAN_ERROR + * code */ -SRSRAN_API int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(const srsran_dmrs_sch_cfg_t* cfg, - srsran_sch_grant_nr_t* grant); +SRSRAN_API int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_dl_nr_t* dci, + uint32_t L); /** * @brief Calculates the PDSCH frequency resource allocation and stores it in the provided PDSCH NR grant. diff --git a/lib/include/srsran/phy/phch/ra_ul_nr.h b/lib/include/srsran/phy/phch/ra_ul_nr.h index 4fb80dce7..958579257 100644 --- a/lib/include/srsran/phy/phch/ra_ul_nr.h +++ b/lib/include/srsran/phy/phch/ra_ul_nr.h @@ -55,16 +55,30 @@ SRSRAN_API int srsran_ra_ul_nr_pusch_time_resource_default_A(uint32_t scs_cfg, uint32_t m, srsran_sch_grant_nr_t* grant); /** - * @brief Calculates the number of PUSCH-DMRS CDM groups without data for DCI format 0_0 + * @brief Calculates the number of front load symbols + * + * @param cfg PUSCH NR configuration by upper layers + * @param dci Provides PUSCH NR DCI + * @param[out] dmrs_duration + * @return SRSRAN_SUCCESS if provided arguments are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ra_ul_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_ul_nr_t* dci, + srsran_dmrs_sch_len_t* dmrs_duration); + +/** + * @brief Calculates the number of DMRS CDM groups without data * * @remark Defined by TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure * * @param cfg PUSCH NR configuration by upper layers - * @param[out] grant Provides grant pointer to fill - * @return Returns SRSRAN_SUCCESS if the provided data is valid, otherwise it returns SRSRAN_ERROR code + * @param dci Provides PUSCH NR DCI + * @return The number of DMRS CDM groups without data if the provided data is valid, otherwise it returns SRSRAN_ERROR + * code */ -SRSRAN_API int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(const srsran_sch_cfg_nr_t* cfg, - srsran_sch_grant_nr_t* grant); +SRSRAN_API int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_ul_nr_t* dci, + uint32_t L); /** * @brief Calculates the ratio of PUSCH EPRE to DM-RS EPRE @@ -87,7 +101,9 @@ SRSRAN_API int srsran_ra_ul_nr_pucch_format_2_3_min_prb(const srsran_pucch_nr_re /** * @brief Calculates the PUSCH frequency resource allocation and stores it in the provided PUSCH NR grant. * - * @remark Defined by TS 38.214 V15.10.0 section 5.1.2.2 + * @remark Defined by TS 38.214 V15.10.0 section 6.1.2.2 Resource allocation in frequency domain (for DCI formats 0_0 + * and 0_1) + * @remark Defined by TS 38.213 V15.10.0 section 8.3 for PUSCH scheduled by RAR UL grant * @param carrier Carrier information * @param cfg PDSCH NR configuration by upper layers * @param dci_dl Unpacked DCI used to schedule the PDSCH grant diff --git a/lib/src/phy/phch/dci_nr.c b/lib/src/phy/phch/dci_nr.c index 89643791e..ad5265b37 100644 --- a/lib/src/phy/phch/dci_nr.c +++ b/lib/src/phy/phch/dci_nr.c @@ -84,6 +84,65 @@ static uint32_t dci_nr_ptrs_size(const srsran_dci_cfg_nr_t* cfg) return 2; } +static uint32_t dci_nr_dl_ports_size(const srsran_dci_cfg_nr_t* cfg) +{ + uint32_t ret = 4; + + if (cfg->pdsch_dmrs_type == srsran_dmrs_sch_type_2) { + ret++; + } + if (cfg->pdsch_dmrs_max_len == srsran_dmrs_sch_len_2) { + ret++; + } + + return ret; +} + +static uint32_t dci_nr_ul_ports_size(const srsran_dci_cfg_nr_t* cfg) +{ + // 2 bits as defined by Tables 7.3.1.1.2-6, if transform precoder is enabled, dmrs-Type=1, and maxLength=1; + if (cfg->enable_transform_precoding == true && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) { + return 2; + } + + // 4 bits as defined by Tables 7.3.1.1.2-7, if transform precoder is enabled, dmrs-Type=1, and maxLength=2; + if (cfg->enable_transform_precoding == true && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_2) { + return 4; + } + + // 3 bits as defined by Tables 7.3.1.1.2-8/9/10/11, if transform precoder is disabled, dmrs-Type=1, and maxLength=1 + if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) { + return 3; + } + + // 4 bits as defined by Tables 7.3.1.1.2-12/13/14/15, if transform precoder is disabled, dmrs-Type=1, and + // maxLength=2 + if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_2) { + return 4; + } + + // 4 bits as defined by Tables 7.3.1.1.2-16/17/18/19, if transform precoder is disabled, dmrs-Type=2, and + // maxLength=1 + if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_2 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) { + return 4; + } + + // 5 bits as defined by Tables 7.3.1.1.2-20/21/22/23, if transform precoder is disabled, dmrs-Type=2, and + // maxLength=2 + if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_2 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_2) { + return 5; + } + + ERROR("Unhandled configuration"); + return 0; +} + static uint32_t dci_nr_srs_id_size(const srsran_dci_cfg_nr_t* cfg) { uint32_t N_srs = SRSRAN_MIN(1, cfg->nof_srs); @@ -393,12 +452,7 @@ static uint32_t dci_nr_format_0_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_ } // Antenna ports - if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { - count += 3; - } else { - ERROR("Not implemented"); - return 0; - } + count += dci_nr_ul_ports_size(cfg); // SRS request - 2 or 3 bits count += cfg->enable_sul ? 3 : 2; @@ -502,12 +556,7 @@ static int dci_nr_format_0_1_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_ } // Antenna ports - if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { - srsran_bit_unpack(dci->ports, &y, 3); - } else { - ERROR("Not implemented"); - return 0; - } + srsran_bit_unpack(dci->ports, &y, dci_nr_ul_ports_size(cfg)); // SRS request - 2 or 3 bits srsran_bit_unpack(dci->srs_request, &y, cfg->enable_sul ? 3 : 2); @@ -619,7 +668,7 @@ static int dci_nr_format_0_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ } // Antenna ports - if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { + if (!cfg->enable_transform_precoding && cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) { dci->ports = srsran_bit_pack(&y, 3); } else { ERROR("Not implemented"); @@ -726,11 +775,8 @@ dci_nr_format_0_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci } // Antenna ports - if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { + if (dci_nr_ul_ports_size(cfg)) { len = srsran_print_check(str, str_len, len, "ports=%d ", dci->ports); - } else { - ERROR("Not implemented"); - return 0; } // SRS request - 2 bits @@ -866,8 +912,8 @@ static int dci_nr_format_1_0_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_ srsran_rnti_type_t rnti_type = msg->ctx.rnti_type; srsran_search_space_type_t ss_type = dci->ctx.ss_type; uint32_t N_DL_BWP_RB = SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type) - ? (q->cfg.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw - : q->cfg.bwp_dl_active_bw; + ? (q->cfg.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw + : q->cfg.bwp_dl_active_bw; // Identifier for DCI formats – 1 bits if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { @@ -972,8 +1018,8 @@ static int dci_nr_format_1_0_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ srsran_rnti_type_t rnti_type = msg->ctx.rnti_type; srsran_search_space_type_t ss_type = msg->ctx.ss_type; uint32_t N_DL_BWP_RB = SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type) - ? (q->cfg.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw - : q->cfg.bwp_dl_active_bw; + ? (q->cfg.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw + : q->cfg.bwp_dl_active_bw; uint32_t nof_bits = srsran_dci_nr_size(q, ss_type, srsran_dci_format_nr_1_0); if (msg->nof_bits != nof_bits) { @@ -1240,13 +1286,7 @@ static uint32_t dci_nr_format_1_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_ count += (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack); // Antenna port(s) – 4, 5, or 6 bits - count += 4; - if (cfg->pdsch_dmrs_type2) { - count++; - } - if (cfg->pdsch_dmrs_double) { - count++; - } + count += dci_nr_dl_ports_size(cfg); // Transmission configuration indication – 0 or 3 bits if (cfg->pdsch_tci) { @@ -1362,13 +1402,7 @@ static int dci_nr_format_1_1_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_ srsran_bit_unpack(dci->harq_feedback, &y, (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack)); // Antenna port(s) – 4, 5, or 6 bits - srsran_bit_unpack(dci->ports, &y, 4); - if (cfg->pdsch_dmrs_type2) { - y++; - } - if (cfg->pdsch_dmrs_double) { - y++; - } + srsran_bit_unpack(dci->ports, &y, dci_nr_dl_ports_size(cfg)); // Transmission configuration indication – 0 or 3 bits if (cfg->pdsch_tci) { @@ -1498,13 +1532,7 @@ static int dci_nr_format_1_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ dci->harq_feedback = srsran_bit_pack(&y, (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack)); // Antenna port(s) – 4, 5, or 6 bits - dci->ports = srsran_bit_pack(&y, 4); - if (cfg->pdsch_dmrs_type2) { - y++; - } - if (cfg->pdsch_dmrs_double) { - y++; - } + dci->ports = srsran_bit_pack(&y, dci_nr_dl_ports_size(cfg)); // Transmission configuration indication – 0 or 3 bits if (cfg->pdsch_tci) { @@ -1619,7 +1647,9 @@ dci_nr_format_1_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci } // Antenna port(s) – 4, 5, or 6 bits - len = srsran_print_check(str, str_len, len, "ports=%d ", dci->ports); + if (dci_nr_dl_ports_size(cfg) > 0) { + len = srsran_print_check(str, str_len, len, "ports=%d ", dci->ports); + } // Transmission configuration indication – 0 or 3 bits if (cfg->pdsch_tci) { diff --git a/lib/src/phy/phch/ra_dl_nr.c b/lib/src/phy/phch/ra_dl_nr.c index cbc52d656..987afd591 100644 --- a/lib/src/phy/phch/ra_dl_nr.c +++ b/lib/src/phy/phch/ra_dl_nr.c @@ -189,69 +189,98 @@ int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, return SRSRAN_SUCCESS; } -int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(const srsran_dmrs_sch_cfg_t* cfg, - srsran_sch_grant_nr_t* grant) +int srsran_ra_dl_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_dl_nr_t* dci, + srsran_dmrs_sch_len_t* dmrs_duration) { - if (cfg == NULL || grant == NULL) { - return SRSRAN_ERROR_INVALID_INPUTS; + // Table 7.3.1.2.2-1: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=1 + // Table 7.3.1.2.2-3: Antenna port(s) (1000 + DMRS port), dmrs-Type=2, maxLength=1 + if (cfg->dmrs_max_length == srsran_dmrs_sch_len_1) { + *dmrs_duration = srsran_dmrs_sch_len_1; + return SRSRAN_SUCCESS; } - /* According to TS 38.214 V15.10.0 5.1.6.1.3 CSI-RS for mobility: - * When receiving PDSCH scheduled by DCI format 1_0, the UE shall assume the number of DM-RS CDM groups without data - * is 1 which corresponds to CDM group 0 for the case of PDSCH with allocation duration of 2 symbols, and the UE - * shall assume that the number of DM-RS CDM groups without data is 2 which corresponds to CDM group {0,1} for all - * other cases. - */ - if (cfg->length == srsran_dmrs_sch_len_2) { - grant->nof_dmrs_cdm_groups_without_data = 1; - } else { - grant->nof_dmrs_cdm_groups_without_data = 2; + // Table 7.3.1.2.2-2: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=2 + if (cfg->dmrs_type == srsran_dmrs_sch_type_1 && cfg->dmrs_max_length == srsran_dmrs_sch_len_2) { + // Only one codeword supported! + if (dci->ports < 12) { + *dmrs_duration = srsran_dmrs_sch_len_1; + return SRSRAN_SUCCESS; + } + + if (dci->ports < 31) { + *dmrs_duration = srsran_dmrs_sch_len_2; + return SRSRAN_SUCCESS; + } + + ERROR("reserved value for ports (%d)", dci->ports); + return SRSRAN_ERROR; } - return SRSRAN_SUCCESS; -} + // Table 7.3.1.2.2-4: Antenna port(s) (1000 + DMRS port), dmrs-Type=2, maxLength=2 + if (cfg->dmrs_type == srsran_dmrs_sch_type_2 && cfg->dmrs_max_length == srsran_dmrs_sch_len_2) { + // Only one codeword supported! + if (dci->ports < 3) { + *dmrs_duration = srsran_dmrs_sch_len_1; + return SRSRAN_SUCCESS; + } -/* RBG size for type0 scheduling as in table 5.1.2.2.1-1 of 36.214 */ -uint32_t srsran_ra_dl_nr_type0_P(uint32_t bwp_size, bool config_is_1) -{ - if (bwp_size <= 36) { - return config_is_1 ? 2 : 4; - } else if (bwp_size <= 72) { - return config_is_1 ? 4 : 8; - } else if (bwp_size <= 144) { - return config_is_1 ? 8 : 16; - } else { - return 16; + if (dci->ports < 24) { + *dmrs_duration = srsran_dmrs_sch_len_2; + return SRSRAN_SUCCESS; + } + + ERROR("reserved value for ports (%d)", dci->ports); + return SRSRAN_ERROR; } + + ERROR("Unhandled case"); + return SRSRAN_ERROR; } -static int ra_freq_type0(const srsran_carrier_nr_t* carrier, - const srsran_sch_hl_cfg_nr_t* cfg, - const srsran_dci_dl_nr_t* dci_dl, - srsran_sch_grant_nr_t* grant) +int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_dl_nr_t* dci, + uint32_t L) { - uint32_t P = srsran_ra_dl_nr_type0_P(carrier->nof_prb, cfg->rbg_size_cfg_1); - - uint32_t N_rbg = (int)ceilf((float)(carrier->nof_prb + (carrier->start % P)) / P); - uint32_t rbg_offset = 0; - for (uint32_t i = 0; i < N_rbg; i++) { - uint32_t rbg_size = P; - if (i == 0) { - rbg_size -= (carrier->start % P); - } else if ((i == N_rbg - 1) && ((carrier->nof_prb + carrier->start) % P) > 0) { - rbg_size = (carrier->nof_prb + carrier->start) % P; - } - if (dci_dl->freq_domain_assigment & (1 << (N_rbg - i - 1))) { - for (uint32_t j = 0; j < rbg_size; j++) { - if (rbg_offset + j < carrier->nof_prb) { - grant->prb_idx[rbg_offset + j] = true; - grant->nof_prb++; + // According to TS 38.214 5.1.6.2 DM-RS reception procedure + switch (dci->ctx.format) { + case srsran_dci_format_nr_1_0: + // When receiving PDSCH scheduled by DCI format 1_0, the UE shall assume the number of DM-RS CDM groups + // without data is 1 which corresponds to CDM group 0 for the case of PDSCH with allocation duration of 2 symbols, + // and the UE shall assume that the number of DM-RS CDM groups without data is 2 which corresponds to CDM group + // {0,1} for all other cases. + if (L == 2) { + return 1; + } else { + return 2; + } + return SRSRAN_SUCCESS; + case srsran_dci_format_nr_0_1: + // When receiving PDSCH scheduled by DCI format 1_1, the UE shall assume that the CDM groups indicated in the + // configured index from Tables 7.3.1.2.2-1, 7.3.1.2.2-2, 7.3.1.2.2-3, 7.3.1.2.2-4 of [5, TS. 38.212] contain + // potential co- scheduled downlink DM-RS and are not used for data transmission, where "1", "2" and "3" for the + // number of DM-RS CDM group(s) in Tables 7.3.1.2.2-1, 7.3.1.2.2-2, 7.3.1.2.2-3, 7.3.1.2.2-4 of [5, TS. 38.212] + // correspond to CDM group 0, {0,1}, {0,1,2}, respectively. + + // Table 7.3.1.2.2-1: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=1 + if (cfg->dmrs_type == srsran_dmrs_sch_type_1 && cfg->dmrs_max_length == srsran_dmrs_sch_len_1) { + if (dci->ports < 3) { + return 1; } + if (dci->ports < 12) { + return 2; + } + ERROR("Invalid ports=%d;", dci->ports); + return SRSRAN_ERROR; } - } - rbg_offset += rbg_size; + + ERROR("Unhandled case (%d, %d)", cfg->dmrs_type, cfg->dmrs_max_length); + return SRSRAN_ERROR; + default: + ERROR("Invalid UL DCI format %s", srsran_dci_format_nr_string(dci->ctx.format)); } - return 0; + + return SRSRAN_ERROR; } int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier, @@ -269,7 +298,7 @@ int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier, return ra_helper_freq_type1(carrier->nof_prb, dci_dl->freq_domain_assigment, grant); } - ra_freq_type0(carrier, cfg, dci_dl, grant); + ra_helper_freq_type0(carrier, cfg, dci_dl->freq_domain_assigment, grant); ERROR("Only DCI Format 1_0 is supported"); return SRSRAN_ERROR; } diff --git a/lib/src/phy/phch/ra_helper.h b/lib/src/phy/phch/ra_helper.h index bdb0c45a0..029bb6ad6 100644 --- a/lib/src/phy/phch/ra_helper.h +++ b/lib/src/phy/phch/ra_helper.h @@ -14,8 +14,52 @@ #define SRSRAN_RA_HELPER_H #include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" #include +/* RBG size for type0 scheduling as in table 5.1.2.2.1-1 of 36.214 */ +static uint32_t ra_helper_type0_P(uint32_t bwp_size, bool config_is_1) +{ + if (bwp_size <= 36) { + return config_is_1 ? 2 : 4; + } else if (bwp_size <= 72) { + return config_is_1 ? 4 : 8; + } else if (bwp_size <= 144) { + return config_is_1 ? 8 : 16; + } else { + return 16; + } +} + +static int ra_helper_freq_type0(const srsran_carrier_nr_t* carrier, + const srsran_sch_hl_cfg_nr_t* cfg, + uint32_t riv, + srsran_sch_grant_nr_t* grant) +{ + uint32_t P = ra_helper_type0_P(carrier->nof_prb, cfg->rbg_size_cfg_1); + + uint32_t N_rbg = (int)ceilf((float)(carrier->nof_prb + (carrier->start % P)) / P); + uint32_t rbg_offset = 0; + for (uint32_t i = 0; i < N_rbg; i++) { + uint32_t rbg_size = P; + if (i == 0) { + rbg_size -= (carrier->start % P); + } else if ((i == N_rbg - 1) && ((carrier->nof_prb + carrier->start) % P) > 0) { + rbg_size = (carrier->nof_prb + carrier->start) % P; + } + if (riv & (1 << (N_rbg - i - 1))) { + for (uint32_t j = 0; j < rbg_size; j++) { + if (rbg_offset + j < carrier->nof_prb) { + grant->prb_idx[rbg_offset + j] = true; + grant->nof_prb++; + } + } + } + rbg_offset += rbg_size; + } + return 0; +} + static inline void ra_helper_compute_s_and_l(uint32_t N, uint32_t v, uint32_t* S, uint32_t* L) { uint32_t low = v % N; diff --git a/lib/src/phy/phch/ra_nr.c b/lib/src/phy/phch/ra_nr.c index 8a34ab4f7..0f4a87f04 100644 --- a/lib/src/phy/phch/ra_nr.c +++ b/lib/src/phy/phch/ra_nr.c @@ -528,13 +528,13 @@ int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg, return SRSRAN_SUCCESS; } -static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* hl_cfg, srsran_sch_grant_nr_t* grant, srsran_sch_cfg_nr_t* cfg) +static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* hl_cfg, const srsran_dci_dl_nr_t* dci, srsran_sch_cfg_nr_t* cfg) { const bool dedicated_dmrs_present = - (grant->mapping == srsran_sch_mapping_type_A) ? hl_cfg->dmrs_typeA.present : hl_cfg->dmrs_typeB.present; + (cfg->grant.mapping == srsran_sch_mapping_type_A) ? hl_cfg->dmrs_typeA.present : hl_cfg->dmrs_typeB.present; - if (grant->dci_format == srsran_dci_format_nr_1_0 || !dedicated_dmrs_present) { - if (grant->mapping == srsran_sch_mapping_type_A) { + if (dci->ctx.format == srsran_dci_format_nr_1_0 || !dedicated_dmrs_present) { + if (cfg->grant.mapping == srsran_sch_mapping_type_A) { // Absent default values are defined is TS 38.331 - DMRS-DownlinkConfig cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2; cfg->dmrs.type = srsran_dmrs_sch_type_1; @@ -546,34 +546,37 @@ static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* hl_cfg, srsran_sch_grant_nr_ return SRSRAN_ERROR; } } else { - if (grant->mapping == srsran_sch_mapping_type_A) { + // Load DMRS duration + if (srsran_ra_dl_nr_nof_front_load_symbols(hl_cfg, dci, &cfg->dmrs.length) < SRSRAN_SUCCESS) { + ERROR("Loading number of front-load symbols"); + return SRSRAN_ERROR; + } + + // DMRS Type + cfg->dmrs.type = hl_cfg->dmrs_type; + + // Other DMRS configuration + if (cfg->grant.mapping == srsran_sch_mapping_type_A) { cfg->dmrs.additional_pos = hl_cfg->dmrs_typeA.additional_pos; - cfg->dmrs.type = hl_cfg->dmrs_typeA.type; - cfg->dmrs.length = hl_cfg->dmrs_typeA.length; cfg->dmrs.scrambling_id0_present = false; cfg->dmrs.scrambling_id1_present = false; } else { cfg->dmrs.additional_pos = hl_cfg->dmrs_typeB.additional_pos; - cfg->dmrs.type = hl_cfg->dmrs_typeB.type; - cfg->dmrs.length = hl_cfg->dmrs_typeB.length; cfg->dmrs.scrambling_id0_present = false; cfg->dmrs.scrambling_id1_present = false; } } // Set number of DMRS CDM groups without data - if (grant->dci_format == srsran_dci_format_nr_1_0) { - if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&cfg->dmrs, grant) < SRSRAN_SUCCESS) { - ERROR("Error loading number of DMRS CDM groups"); - return SRSRAN_ERROR; - } - } else { - ERROR("Invalid case"); + int n = srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(hl_cfg, dci, cfg->grant.L); + if (n < SRSRAN_SUCCESS) { + ERROR("Error loading number of DMRS CDM groups"); return SRSRAN_ERROR; } + cfg->grant.nof_dmrs_cdm_groups_without_data = (uint32_t)n; // Set DMRS power offset Table 6.2.2-1: The ratio of PUSCH EPRE to DM-RS EPRE - if (ra_nr_dmrs_power_offset(grant) < SRSRAN_SUCCESS) { + if (ra_nr_dmrs_power_offset(&cfg->grant) < SRSRAN_SUCCESS) { ERROR("Error setting DMRS power offset"); return SRSRAN_ERROR; } @@ -682,7 +685,7 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, } // 5.1.6.2 DM-RS reception procedure - if (ra_dl_dmrs(pdsch_hl_cfg, pdsch_grant, pdsch_cfg) < SRSRAN_SUCCESS) { + if (ra_dl_dmrs(pdsch_hl_cfg, dci_dl, pdsch_cfg) < SRSRAN_SUCCESS) { ERROR("Error selecting DMRS configuration"); return SRSRAN_ERROR; } @@ -697,15 +700,15 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, } static int -ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, srsran_sch_grant_nr_t* pusch_grant, srsran_sch_cfg_nr_t* cfg) +ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, const srsran_dci_ul_nr_t* dci, srsran_sch_cfg_nr_t* cfg) { - const bool dedicated_dmrs_present = (pusch_grant->mapping == srsran_sch_mapping_type_A) + const bool dedicated_dmrs_present = (cfg->grant.mapping == srsran_sch_mapping_type_A) ? pusch_hl_cfg->dmrs_typeA.present : pusch_hl_cfg->dmrs_typeB.present; - if (pusch_grant->dci_format == srsran_dci_format_nr_0_0 || pusch_grant->dci_format == srsran_dci_format_nr_rar || + if (dci->ctx.format == srsran_dci_format_nr_0_0 || dci->ctx.format == srsran_dci_format_nr_rar || !dedicated_dmrs_present) { - if (pusch_grant->mapping == srsran_sch_mapping_type_A) { + if (cfg->grant.mapping == srsran_sch_mapping_type_A) { // Absent default values are defined is TS 38.331 - DMRS-DownlinkConfig cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2; cfg->dmrs.type = srsran_dmrs_sch_type_1; @@ -717,34 +720,36 @@ ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, srsran_sch_grant_nr_t* pu return SRSRAN_ERROR; } } else { - if (pusch_grant->mapping == srsran_sch_mapping_type_A) { + // DMRS duration + if (srsran_ra_ul_nr_nof_front_load_symbols(pusch_hl_cfg, dci, &cfg->dmrs.length) < SRSRAN_SUCCESS) { + ERROR("Loading number of front-load symbols"); + return SRSRAN_ERROR; + } + + // DMRS type + cfg->dmrs.type = pusch_hl_cfg->dmrs_type; + + if (cfg->grant.mapping == srsran_sch_mapping_type_A) { cfg->dmrs.additional_pos = pusch_hl_cfg->dmrs_typeA.additional_pos; - cfg->dmrs.type = pusch_hl_cfg->dmrs_typeA.type; - cfg->dmrs.length = pusch_hl_cfg->dmrs_typeA.length; cfg->dmrs.scrambling_id0_present = false; cfg->dmrs.scrambling_id1_present = false; } else { cfg->dmrs.additional_pos = pusch_hl_cfg->dmrs_typeB.additional_pos; - cfg->dmrs.type = pusch_hl_cfg->dmrs_typeB.type; - cfg->dmrs.length = pusch_hl_cfg->dmrs_typeB.length; cfg->dmrs.scrambling_id0_present = false; cfg->dmrs.scrambling_id1_present = false; } } // Set number of DMRS CDM groups without data - if (pusch_grant->dci_format == srsran_dci_format_nr_0_0 || pusch_grant->dci_format == srsran_dci_format_nr_rar) { - if (srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(cfg, pusch_grant) < SRSRAN_SUCCESS) { - ERROR("Error loading number of DMRS CDM groups"); - return SRSRAN_ERROR; - } - } else { - ERROR("DCI format not implemented %s", srsran_dci_format_nr_string(pusch_grant->dci_format)); + int n = srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(pusch_hl_cfg, dci, cfg->grant.L); + if (n < SRSRAN_SUCCESS) { + ERROR("Error getting number of DMRS CDM groups without data"); return SRSRAN_ERROR; } + cfg->grant.nof_dmrs_cdm_groups_without_data = (uint32_t)n; // Set DMRS power offset Table 6.2.2-1: The ratio of PUSCH EPRE to DM-RS EPRE - if (ra_nr_dmrs_power_offset(pusch_grant) < SRSRAN_SUCCESS) { + if (ra_nr_dmrs_power_offset(&cfg->grant) < SRSRAN_SUCCESS) { ERROR("Error setting DMRS power offset"); return SRSRAN_ERROR; } @@ -787,7 +792,7 @@ int srsran_ra_ul_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, pusch_grant->tb[0].ndi = dci_ul->ndi; // 5.1.6.2 DM-RS reception procedure - if (ra_ul_dmrs(pusch_hl_cfg, pusch_grant, pusch_cfg) < SRSRAN_SUCCESS) { + if (ra_ul_dmrs(pusch_hl_cfg, dci_ul, pusch_cfg) < SRSRAN_SUCCESS) { ERROR("Error selecting DMRS configuration"); return SRSRAN_ERROR; } diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index 5be5f7681..ea425ab24 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -196,31 +196,94 @@ int srsran_ra_ul_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, return SRSRAN_SUCCESS; } -int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(const srsran_sch_cfg_nr_t* cfg, - srsran_sch_grant_nr_t* grant) +int srsran_ra_ul_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_ul_nr_t* dci, + srsran_dmrs_sch_len_t* dmrs_duration) { - if (cfg == NULL || grant == NULL) { + if (cfg == NULL || dci == NULL || dmrs_duration == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } - /* According to TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure: - * For PUSCH scheduled by DCI format 0_0 or by activation DCI format 0_0 with CRC scrambled by CS-RNTI, the UE - * shall assume the number of DM-RS CDM groups without data is 1 which corresponds to CDM group 0 for the case of - * PUSCH with allocation duration of 2 or less OFDM symbols with transform precoding disabled, the UE shall assume - * that the number of DM-RS CDM groups without data is 3 which corresponds to CDM group {0,1,2} for the case of PUSCH - * scheduled by activation DCI format 0_0 and the dmrs-Type in cg-DMRS-Configuration equal to 'type2' and the PUSCH - * allocation duration being more than 2 OFDM symbols, and the UE shall assume that the number of DM-RS CDM groups - * without data is 2 which corresponds to CDM group {0,1} for all other cases. - */ - if (grant->L <= 2 && !cfg->enable_transform_precoder) { - grant->nof_dmrs_cdm_groups_without_data = 1; - // } else if (grant->L > 2 && cfg->dmrs_cg.type == srsran_dmrs_sch_type_2){ - // grant->nof_dmrs_cdm_groups_without_data = 3; - } else { - grant->nof_dmrs_cdm_groups_without_data = 2; + // Table 7.3.1.1.2-6: Antenna port(s), transform precoder is enabled, dmrs-Type=1, maxLength=1 + // Table 7.3.1.1.2-8: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=1 + // Table 7.3.1.1.2-9: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=2 + // Table 7.3.1.1.2-10: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=3 + // Table 7.3.1.1.2-11: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=4 + // Table 7.3.1.1.2-16: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=1 + // Table 7.3.1.1.2-17: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=2 + // Table 7.3.1.1.2-18: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=3 + // Table 7.3.1.1.2-19: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=4 + if (cfg->dmrs_max_length == srsran_dmrs_sch_len_1) { + *dmrs_duration = srsran_dmrs_sch_len_1; + return SRSRAN_SUCCESS; } - return SRSRAN_SUCCESS; + // Other tables are not implemented + ERROR("Unhandled case"); + return SRSRAN_ERROR; +} + +int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_ul_nr_t* dci, + uint32_t L) +{ + if (cfg == NULL || dci == NULL || L == 0) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + uint32_t rank = 1; // No other supported + + // According to TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure: + switch (dci->ctx.format) { + case srsran_dci_format_nr_0_0: + // For PUSCH scheduled by DCI format 0_0 or by activation DCI format 0_0 with CRC scrambled by CS-RNTI, the UE + // shall assume the number of DM-RS CDM groups without data is 1 which corresponds to CDM group 0 for the case of + // PUSCH with allocation duration of 2 or less OFDM symbols with transform precoding disabled, the UE shall assume + // that the number of DM-RS CDM groups without data is 3 which corresponds to CDM group {0,1,2} for the case of + // PUSCH scheduled by activation DCI format 0_0 and the dmrs-Type in cg-DMRS-Configuration equal to 'type2' and + // the PUSCH allocation duration being more than 2 OFDM symbols, and the UE shall assume that the number of DM-RS + // CDM groups without data is 2 which corresponds to CDM group {0,1} for all other cases. + if (L <= 2 && !cfg->enable_transform_precoder) { + return 1; + } else if (L > 2 && cfg->dmrs_type == srsran_dmrs_sch_type_2 && dci->ctx.format == srsran_dci_format_nr_cg) { + return 3; + } else { + return 2; + } + return SRSRAN_SUCCESS; + case srsran_dci_format_nr_0_1: + // For PUSCH scheduled by DCI format 0_1, by activation DCI format 0_1 with CRC scrambled by CS-RNTI, or + // configured by configured grant Type 1 configuration, the UE shall assume the DM-RS CDM groups indicated in + // Tables 7.3.1.1.2-6 to 7.3.1.1.2-23 of Clause 7.3.1.1 of [5, TS38.212] are not used for data transmission, where + // "1", "2" and "3" for the number of DM-RS CDM group(s) correspond to CDM group 0, {0,1}, {0,1,2}, respectively. + + // Table 7.3.1.1.2-6: Antenna port(s), transform precoder is enabled, dmrs-Type=1, maxLength=1 + if (cfg->enable_transform_precoder && cfg->dmrs_type == srsran_dmrs_sch_type_1 && + cfg->dmrs_max_length == srsran_dmrs_sch_len_1 && rank == 1) { + return 2; + } + + // Table 7.3.1.1.2-8: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank= 1 + if (!cfg->enable_transform_precoder && cfg->dmrs_type == srsran_dmrs_sch_type_1 && + cfg->dmrs_max_length == srsran_dmrs_sch_len_1 && rank == 1) { + if (dci->ports < 2) { + return 1; + } + + if (dci->ports < 6) { + return 2; + } + + ERROR("Invalid ports (%d)", dci->ports); + return SRSRAN_ERROR; + } + + ERROR("Unhandled configuration"); + return SRSRAN_ERROR; + default: + ERROR("Invalid UL DCI format %s", srsran_dci_format_nr_string(dci->ctx.format)); + } + + return SRSRAN_ERROR; } #define RA_UL_PUCCH_CODE_RATE_N 8 @@ -374,13 +437,35 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier, return SRSRAN_ERROR_INVALID_INPUTS; } - // RA scheme - if (dci_ul->ctx.format == srsran_dci_format_nr_0_0 || dci_ul->ctx.format == srsran_dci_format_nr_rar) { - // when the scheduling grant is received with DCI format 1_0 , then downlink resource allocation type 1 is used. + // TS 38.213 PUSCH scheduled by RAR UL grant + if (dci_ul->ctx.format == srsran_dci_format_nr_rar) { + return ra_helper_freq_type1(carrier->nof_prb, dci_ul->freq_domain_assigment, grant); + } + + // The UE shall assume that when the scheduling PDCCH is received with DCI format 0_0, then uplink resource + // allocation type 1 is used. + if (dci_ul->ctx.format == srsran_dci_format_nr_0_0) { return ra_helper_freq_type1(carrier->nof_prb, dci_ul->freq_domain_assigment, grant); } - ERROR("Unhandled DCI Format %s", srsran_dci_format_nr_string(dci_ul->ctx.format)); + // If the scheduling DCI is configured to indicate the uplink resource allocation type as part of the Frequency domain + // resource assignment field by setting a higher layer parameter resourceAllocation in pusch-Config to 'dynamicSwitch' + if (cfg->alloc == srsran_resource_alloc_dynamic) { + ERROR("Unsupported dynamic resource allocation"); + return SRSRAN_ERROR; + } + + // Otherwise the UE shall use the uplink frequency resource allocation type as defined by the higher layer parameter + // resourceAllocation. + if (cfg->alloc == srsran_resource_alloc_type1) { + return ra_helper_freq_type1(carrier->nof_prb, dci_ul->freq_domain_assigment, grant); + } + + if (cfg->alloc == srsran_resource_alloc_type0) { + return ra_helper_freq_type0(carrier, cfg, dci_ul->freq_domain_assigment, grant); + } + + ERROR("Unhandled case"); return SRSRAN_ERROR; } diff --git a/lib/src/phy/phch/test/dci_nr_test.c b/lib/src/phy/phch/test/dci_nr_test.c index 6283a4e82..d808cf597 100644 --- a/lib/src/phy/phch/test/dci_nr_test.c +++ b/lib/src/phy/phch/test/dci_nr_test.c @@ -55,11 +55,11 @@ static int test_52prb_base() cfg.enable_transform_precoding = false; cfg.dynamic_dual_harq_ack_codebook = false; cfg.pusch_tx_config_non_codebook = false; - cfg.pusch_dmrs_type2 = false; - cfg.pusch_dmrs_double = false; cfg.pusch_ptrs = false; cfg.pusch_dynamic_betas = false; cfg.pusch_alloc_type = srsran_resource_alloc_type1; + cfg.pusch_dmrs_type = srsran_dmrs_sch_type_1; + cfg.pusch_dmrs_max_len = srsran_dmrs_sch_len_1; // DCI 1_1 parameters cfg.nof_dl_bwp = 0; @@ -72,12 +72,12 @@ static int test_52prb_base() cfg.pdsch_rm_pattern2 = false; cfg.pdsch_2cw = false; cfg.multiple_scell = false; - cfg.pdsch_dmrs_type2 = false; - cfg.pdsch_dmrs_double = false; cfg.pdsch_tci = false; cfg.pdsch_cbg_flush = false; cfg.pdsch_dynamic_bundling = false; cfg.pdsch_alloc_type = srsran_resource_alloc_type1; + cfg.pdsch_dmrs_type = srsran_dmrs_sch_type_1; + cfg.pdsch_dmrs_max_len = srsran_dmrs_sch_len_1; // Configure DCI srsran_dci_nr_t dci = {}; diff --git a/lib/src/phy/phch/test/pdsch_nr_test.c b/lib/src/phy/phch/test/pdsch_nr_test.c index 93a6ed094..f353cf3f8 100644 --- a/lib/src/phy/phch/test/pdsch_nr_test.c +++ b/lib/src/phy/phch/test/pdsch_nr_test.c @@ -160,15 +160,11 @@ int main(int argc, char** argv) goto clean_exit; } - // Load number of DMRS CDM groups without data - if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&pdsch_cfg.dmrs, &pdsch_cfg.grant) < SRSRAN_SUCCESS) { - ERROR("Error loading number of DMRS CDM groups without data"); - goto clean_exit; - } - - pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers; - pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; - pdsch_cfg.grant.rnti = rnti; + // Set PDSCH grant without considering any procedure + pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO + pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers; + pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; + pdsch_cfg.grant.rnti = rnti; uint32_t n_prb_start = 1; uint32_t n_prb_end = carrier.nof_prb + 1; diff --git a/lib/src/phy/phch/test/pusch_nr_test.c b/lib/src/phy/phch/test/pusch_nr_test.c index 2ccdb2286..f88e1b938 100644 --- a/lib/src/phy/phch/test/pusch_nr_test.c +++ b/lib/src/phy/phch/test/pusch_nr_test.c @@ -166,15 +166,11 @@ int main(int argc, char** argv) goto clean_exit; } - // Load number of DMRS CDM groups without data - if (srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(&pusch_cfg, &pusch_cfg.grant) < SRSRAN_SUCCESS) { - ERROR("Error loading number of DMRS CDM groups without data"); - goto clean_exit; - } - - pusch_cfg.grant.nof_layers = carrier.max_mimo_layers; - pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; - pusch_cfg.grant.rnti = rnti; + // Set PDSCH grant without considering any procedure + pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO + pusch_cfg.grant.nof_layers = carrier.max_mimo_layers; + pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; + pusch_cfg.grant.rnti = rnti; uint32_t n_prb_start = 1; uint32_t n_prb_end = carrier.nof_prb + 1; diff --git a/lib/src/phy/phch/test/sch_nr_test.c b/lib/src/phy/phch/test/sch_nr_test.c index 5913604c0..2e633febd 100644 --- a/lib/src/phy/phch/test/sch_nr_test.c +++ b/lib/src/phy/phch/test/sch_nr_test.c @@ -179,12 +179,7 @@ int main(int argc, char** argv) for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) { pdsch_cfg.grant.prb_idx[n] = (n < n_prb); } - - if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&pdsch_cfg.dmrs, &pdsch_cfg.grant) < - SRSRAN_SUCCESS) { - ERROR("Error calculating number of DMRS CDM groups"); - goto clean_exit; - } + pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO srsran_sch_tb_t tb = {}; tb.rv = rv; From b8e0164f7f2298fea59063913e9d23a7d34c92a1 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 23 Apr 2021 16:47:29 +0200 Subject: [PATCH 05/50] Multiple fix NR PHY procedures --- lib/src/phy/phch/ra_dl_nr.c | 29 +++++++++++++++++++++++------ lib/src/phy/phch/ra_ul_nr.c | 3 ++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/src/phy/phch/ra_dl_nr.c b/lib/src/phy/phch/ra_dl_nr.c index 987afd591..d3bf524e3 100644 --- a/lib/src/phy/phch/ra_dl_nr.c +++ b/lib/src/phy/phch/ra_dl_nr.c @@ -255,7 +255,7 @@ int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_ return 2; } return SRSRAN_SUCCESS; - case srsran_dci_format_nr_0_1: + case srsran_dci_format_nr_1_1: // When receiving PDSCH scheduled by DCI format 1_1, the UE shall assume that the CDM groups indicated in the // configured index from Tables 7.3.1.2.2-1, 7.3.1.2.2-2, 7.3.1.2.2-3, 7.3.1.2.2-4 of [5, TS. 38.212] contain // potential co- scheduled downlink DM-RS and are not used for data transmission, where "1", "2" and "3" for the @@ -288,17 +288,34 @@ int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier, const srsran_dci_dl_nr_t* dci_dl, srsran_sch_grant_nr_t* grant) { - if (cfg == NULL || grant == NULL || dci_dl == NULL) { + if (carrier == NULL || cfg == NULL || grant == NULL || dci_dl == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } - // RA scheme + // The UE shall assume that when the scheduling grant is received with DCI format 1_0 , then downlink resource + // allocation type 1 is used. if (dci_dl->ctx.format == srsran_dci_format_nr_1_0) { - // when the scheduling grant is received with DCI format 1_0 , then downlink resource allocation type 1 is used. return ra_helper_freq_type1(carrier->nof_prb, dci_dl->freq_domain_assigment, grant); } - ra_helper_freq_type0(carrier, cfg, dci_dl->freq_domain_assigment, grant); - ERROR("Only DCI Format 1_0 is supported"); + // If the scheduling DCI is configured to indicate the downlink resource allocation type as part of the Frequency + // domain resource assignment field by setting a higher layer parameter resourceAllocation in pdsch-Config to + // 'dynamicswitch', the UE shall use downlink resource allocation type 0 or type 1 as defined by this DCI field. + if (cfg->alloc == srsran_resource_alloc_dynamic) { + ERROR("Unsupported dynamic resource allocation"); + return SRSRAN_ERROR; + } + + // Otherwise the UE shall use the downlink frequency resource allocation type as defined by the higher layer parameter + // resourceAllocation. + if (cfg->alloc == srsran_resource_alloc_type1) { + return ra_helper_freq_type1(carrier->nof_prb, dci_dl->freq_domain_assigment, grant); + } + + if (cfg->alloc == srsran_resource_alloc_type0) { + return ra_helper_freq_type0(carrier, cfg, dci_dl->freq_domain_assigment, grant); + } + + ERROR("Unhandled case"); return SRSRAN_ERROR; } diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index ea425ab24..065ef8736 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -235,6 +235,7 @@ int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_ // According to TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure: switch (dci->ctx.format) { case srsran_dci_format_nr_0_0: + case srsran_dci_format_nr_rar: // For PUSCH scheduled by DCI format 0_0 or by activation DCI format 0_0 with CRC scrambled by CS-RNTI, the UE // shall assume the number of DM-RS CDM groups without data is 1 which corresponds to CDM group 0 for the case of // PUSCH with allocation duration of 2 or less OFDM symbols with transform precoding disabled, the UE shall assume @@ -433,7 +434,7 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier, const srsran_dci_ul_nr_t* dci_ul, srsran_sch_grant_nr_t* grant) { - if (cfg == NULL || grant == NULL || dci_ul == NULL) { + if (carrier == NULL || cfg == NULL || grant == NULL || dci_ul == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } From 318f064ff45dfa15cc9ee6a4b5da8203a46c0b87 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 23 Apr 2021 16:48:35 +0200 Subject: [PATCH 06/50] Multiple NR SCH fixes --- lib/src/phy/phch/sch_nr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/phy/phch/sch_nr.c b/lib/src/phy/phch/sch_nr.c index 64f430b90..a6a8e1414 100644 --- a/lib/src/phy/phch/sch_nr.c +++ b/lib/src/phy/phch/sch_nr.c @@ -646,6 +646,8 @@ static int sch_nr_decode(srsran_sch_nr_t* q, input_ptr += E; } + // Set average number of iterations + res->avg_iter = (float)nof_iter_sum / (float)cfg.C; // Set average number of iterations if (cfg.C > 0) { From 01eace26315a8c219247368ae19695185006eb3d Mon Sep 17 00:00:00 2001 From: Francisco Date: Thu, 22 Apr 2021 15:15:16 +0100 Subject: [PATCH 07/50] rrc paging mechanism improvements - moved paging record handling to separate class - parallel access to pending pcch messages by phy workers based on TTI and without common lock - asn1 pcch message packing now takes place in stack thread, to avoid real-time issues --- .../srsran/interfaces/enb_rlc_interfaces.h | 2 - .../srsran/interfaces/enb_rrc_interfaces.h | 14 +- srsenb/hdr/stack/rrc/rrc.h | 26 +-- srsenb/hdr/stack/rrc/rrc_paging.h | 203 ++++++++++++++++++ srsenb/hdr/stack/upper/rlc.h | 1 - srsenb/src/stack/mac/mac.cc | 2 +- srsenb/src/stack/rrc/rrc.cc | 135 ++---------- srsenb/src/stack/upper/rlc.cc | 5 - srsenb/test/mac/sched_test_common.h | 1 + 9 files changed, 248 insertions(+), 141 deletions(-) create mode 100644 srsenb/hdr/stack/rrc/rrc_paging.h diff --git a/lib/include/srsran/interfaces/enb_rlc_interfaces.h b/lib/include/srsran/interfaces/enb_rlc_interfaces.h index 5d7be6b3f..f2f3cddb2 100644 --- a/lib/include/srsran/interfaces/enb_rlc_interfaces.h +++ b/lib/include/srsran/interfaces/enb_rlc_interfaces.h @@ -26,8 +26,6 @@ public: * Segmentation happens in this function. RLC PDU is stored in payload. */ virtual int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0; - virtual void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) = 0; - /* MAC calls RLC to push an RLC PDU. This function is called from an independent MAC thread. * PDU gets placed into the buffer and higher layer thread gets notified. */ virtual void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0; diff --git a/lib/include/srsran/interfaces/enb_rrc_interfaces.h b/lib/include/srsran/interfaces/enb_rrc_interfaces.h index b5133af9d..967426f50 100644 --- a/lib/include/srsran/interfaces/enb_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/enb_rrc_interfaces.h @@ -90,7 +90,6 @@ public: class rrc_interface_rlc { public: - virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0; virtual void max_retx_attempted(uint16_t rnti) = 0; virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; }; @@ -100,12 +99,13 @@ class rrc_interface_mac { public: /* Radio Link failure */ - virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0; - virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0; - virtual void set_activity_user(uint16_t rnti) = 0; - virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0; - virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0; - virtual bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) = 0; + virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0; + virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0; + virtual void set_activity_user(uint16_t rnti) = 0; + virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0; + virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0; + virtual bool is_paging_opportunity(uint32_t tti_tx_dl, uint32_t* payload_len) = 0; + virtual void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t payload_size) = 0; ///< Provide packed SIB to MAC (buffer is managed by RRC) virtual uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) = 0; diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index cf3efaa34..b63fdf90d 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -36,6 +36,8 @@ class rlc_interface_rrc; class mac_interface_rrc; class phy_interface_rrc_lte; +class paging_manager; + static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE", "WAIT FOR CON SETUP COMPLETE", "WAIT FOR SECURITY MODE COMPLETE", @@ -75,7 +77,7 @@ public: uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override; // rrc_interface_rlc - void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) override; + void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t buffer_size) override; void max_retx_attempted(uint16_t rnti) override; // rrc_interface_s1ap @@ -163,9 +165,9 @@ private: std::unique_ptr cell_common_list; // state - std::unique_ptr cell_res_list; - std::map > users; // NOTE: has to have fixed addr - std::map pending_paging; + std::unique_ptr cell_res_list; + std::map > users; // NOTE: has to have fixed addr + std::unique_ptr pending_paging; void process_release_complete(uint16_t rnti); void rem_user(uint16_t rnti); @@ -190,13 +192,13 @@ private: srsran::unique_byte_buffer_t pdu; } rrc_pdu; - const static uint32_t LCID_EXIT = 0xffff0000; - const static uint32_t LCID_REM_USER = 0xffff0001; - const static uint32_t LCID_REL_USER = 0xffff0002; - const static uint32_t LCID_ACT_USER = 0xffff0004; - const static uint32_t LCID_RTX_USER = 0xffff0005; - const static uint32_t LCID_RADLINK_DL = 0xffff0006; - const static uint32_t LCID_RADLINK_UL = 0xffff0007; + const static uint32_t LCID_EXIT = 0xffff0000; + const static uint32_t LCID_REM_USER = 0xffff0001; + const static uint32_t LCID_REL_USER = 0xffff0002; + const static uint32_t LCID_ACT_USER = 0xffff0004; + const static uint32_t LCID_RTX_USER = 0xffff0005; + const static uint32_t LCID_RADLINK_DL = 0xffff0006; + const static uint32_t LCID_RADLINK_UL = 0xffff0007; bool running = false; srsran::dyn_blocking_queue rx_pdu_queue; @@ -208,8 +210,6 @@ private: asn1::rrc::sib_type7_s sib7; void rem_user_thread(uint16_t rnti); - - std::mutex paging_mutex; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/rrc/rrc_paging.h b/srsenb/hdr/stack/rrc/rrc_paging.h new file mode 100644 index 000000000..74e6367c2 --- /dev/null +++ b/srsenb/hdr/stack/rrc/rrc_paging.h @@ -0,0 +1,203 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_RRC_PAGING_H +#define SRSRAN_RRC_PAGING_H + +#include "srsran/adt/span.h" +#include "srsran/asn1/rrc/paging.h" +#include "srsran/common/tti_point.h" + +namespace srsenb { + +/** + * Class that handles the buffering of paging records and encoding of PCCH messages. It's thread-safe + */ +class paging_manager +{ +public: + paging_manager(uint32_t default_paging_cycle_, uint32_t nb_) : + T(default_paging_cycle_), + Nb(T * nb_), + N(std::min(T, Nb)), + Ns(std::max(nb_, 1u)), + pending_paging(T), + logger(srslog::fetch_basic_logger("RRC")) + { + for (auto& sfn_pcch_msgs : pending_paging) { + for (pcch_info& pcch : sfn_pcch_msgs) { + pcch.pcch_msg.msg.set_c1().paging().paging_record_list_present = true; + } + } + } + + bool add_imsi_paging(uint32_t ueid, srsran::const_byte_span imsi); + bool add_tmsi_paging(uint32_t ueid, uint8_t mmec, srsran::const_byte_span m_tmsi); + + size_t pending_pcch_bytes(tti_point tti_tx_dl); + /** + * Invoke "callable" for PCCH indexed by tti_tx_dl in a mutexed context. + * Callable signature is bool(const_byte_span pdu, const pcch_msg& msg, bool is_first_tx) + * - "is_first_tx" tells if the PDU hasn't been transmitted yet. + * - the return should be true if the PDU is being transmitted, and false otherwise + */ + template + bool read_pdu_pcch(tti_point tti_tx_dl, const Callable& callable); + +private: + struct pcch_info { + mutable std::mutex mutex; + tti_point tti_tx_dl; + asn1::rrc::pcch_msg_s pcch_msg; + srsran::unique_byte_buffer_t pdu; + + bool is_tx() const { return tti_tx_dl.is_valid(); } + bool empty() const { return pdu == nullptr; } + void clear() + { + tti_tx_dl = tti_point(); + pcch_msg.msg.c1().paging().paging_record_list.clear(); + pdu.reset(); + } + }; + + bool add_paging_record(uint32_t ueid, const asn1::rrc::paging_record_s& paging_record); + pcch_info& get_pcch_info(tti_point tti_tx_dl) { return pending_paging[tti_tx_dl.sfn() % T][tti_tx_dl.sf_idx()]; } + const pcch_info& get_pcch_info(tti_point tti_tx_dl) const + { + return pending_paging[tti_tx_dl.sfn() % T][tti_tx_dl.sf_idx()]; + } + void clear_pcch_info(tti_point tti); + + // args + uint32_t T; + uint32_t Nb; + uint32_t N; + uint32_t Ns; + srslog::basic_logger& logger; + + std::vector > pending_paging; +}; + +void paging_manager::clear_pcch_info(tti_point tti) +{ + pcch_info& pcch = get_pcch_info(tti); + + std::lock_guard lock(pcch.mutex); + pcch.clear(); +} + +bool paging_manager::add_imsi_paging(uint32_t ueid, srsran::const_byte_span imsi) +{ + asn1::rrc::paging_record_s paging_elem; + paging_elem.ue_id.set_imsi().resize(imsi.size()); + std::copy(imsi.begin(), imsi.end(), paging_elem.ue_id.imsi().begin()); + paging_elem.cn_domain = asn1::rrc::paging_record_s::cn_domain_e_::ps; + return add_paging_record(ueid, paging_elem); +} + +bool paging_manager::add_tmsi_paging(uint32_t ueid, uint8_t mmec, srsran::const_byte_span m_tmsi) +{ + asn1::rrc::paging_record_s paging_elem; + paging_elem.ue_id.set_s_tmsi().mmec.from_number(mmec); + uint32_t m_tmsi_val = 0; + for (uint32_t i = 0; i < m_tmsi.size(); i++) { + m_tmsi_val |= m_tmsi[i] << (8u * (m_tmsi.size() - i - 1u)); + } + paging_elem.ue_id.s_tmsi().m_tmsi.from_number(m_tmsi_val); + paging_elem.cn_domain = asn1::rrc::paging_record_s::cn_domain_e_::ps; + return add_paging_record(ueid, paging_elem); +} + +/// \remark See TS 36.304, Section 7 +bool paging_manager::add_paging_record(uint32_t ueid, const asn1::rrc::paging_record_s& paging_record) +{ + constexpr static const int sf_pattern[4][4] = {{9, 4, -1, 0}, {-1, 9, -1, 4}, {-1, -1, -1, 5}, {-1, -1, -1, 9}}; + + ueid = ((uint32_t)ueid) % 1024; + uint32_t i_s = (ueid / N) % Ns; + + int sf_idx = sf_pattern[i_s % 4][(Ns - 1) % 4]; + if (sf_idx < 0) { + logger.error("SF pattern is N/A for Ns=%d, i_s=%d, imsi_decimal=%d", Ns, i_s, ueid); + return false; + } + + size_t sfn_cycle_idx = (T / N) * (ueid % N); + pcch_info& pending_pcch = pending_paging[sfn_cycle_idx][sf_idx]; + auto& record_list = pending_pcch.pcch_msg.msg.c1().paging().paging_record_list; + + std::lock_guard lock(pending_pcch.mutex); + + if (record_list.size() >= ASN1_RRC_MAX_PAGE_REC) { + logger.warning("Failed to add new paging record for ueid=%d. Cause: no paging record space left.", ueid); + return false; + } + + if (pending_pcch.pdu == nullptr) { + pending_pcch.pdu = srsran::make_byte_buffer(); + if (pending_pcch.pdu == nullptr) { + logger.warning("Failed to add new paging record for ueid=%d. Cause: No buffers available", ueid); + return false; + } + } + + record_list.push_back(paging_record); + + asn1::bit_ref bref(pending_pcch.pdu->msg, pending_pcch.pdu->get_tailroom()); + if (pending_pcch.pcch_msg.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { + logger.error("Failed to pack PCCH message"); + pending_pcch.clear(); + return false; + } + pending_pcch.pdu->N_bytes = (uint32_t)bref.distance_bytes(); + + return true; +} + +size_t paging_manager::pending_pcch_bytes(tti_point tti_tx_dl) +{ + // clear old PCCH that has been transmitted at this point + clear_pcch_info(tti_tx_dl - SRSRAN_NOF_SF_X_FRAME); + + const pcch_info& pending_pcch = get_pcch_info(tti_tx_dl); + + std::lock_guard lock(pending_pcch.mutex); + if (pending_pcch.empty()) { + return 0; + } + return pending_pcch.pdu->size(); +} + +template +bool paging_manager::read_pdu_pcch(tti_point tti_tx_dl, const Callable& callable) +{ + pcch_info& pending_pcch = get_pcch_info(tti_tx_dl); + + std::lock_guard lock(pending_pcch.mutex); + + if (pending_pcch.empty()) { + logger.warning("read_pdu_pdcch(...) called for tti=%d, but there is no pending pcch message", tti_tx_dl.to_uint()); + return false; + } + + // Call callable for existing PCCH pdu + if (callable(*pending_pcch.pdu, pending_pcch.pcch_msg, pending_pcch.tti_tx_dl.is_valid())) { + pending_pcch.tti_tx_dl = tti_tx_dl; + return true; + } + return false; +} + +} // namespace srsenb + +#endif // SRSRAN_RRC_PAGING_H diff --git a/srsenb/hdr/stack/upper/rlc.h b/srsenb/hdr/stack/upper/rlc.h index 8bf8fb840..a4a08ecfe 100644 --- a/srsenb/hdr/stack/upper/rlc.h +++ b/srsenb/hdr/stack/upper/rlc.h @@ -65,7 +65,6 @@ public: // rlc_interface_mac int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); - void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size); private: class user_interface : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index 004ea61bf..d664920e4 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -727,7 +727,7 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list) } else { dl_sched_res->pdsch[n].softbuffer_tx[0] = &common_buffers[enb_cc_idx].pcch_softbuffer_tx; dl_sched_res->pdsch[n].data[0] = common_buffers[enb_cc_idx].pcch_payload_buffer; - rlc_h->read_pdu_pcch(common_buffers[enb_cc_idx].pcch_payload_buffer, pcch_payload_buffer_len); + rrc_h->read_pdu_pcch(tti_tx_dl, common_buffers[enb_cc_idx].pcch_payload_buffer, pcch_payload_buffer_len); if (pcap) { pcap->write_dl_pch(dl_sched_res->pdsch[n].data[0], sched_result.bc[i].tbs, true, tti_tx_dl, enb_cc_idx); diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index c85aec7c9..4fb9b6331 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -13,6 +13,7 @@ #include "srsenb/hdr/stack/rrc/rrc.h" #include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h" #include "srsenb/hdr/stack/rrc/rrc_mobility.h" +#include "srsenb/hdr/stack/rrc/rrc_paging.h" #include "srsran/asn1/asn1_utils.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/bcd_helpers.h" @@ -81,6 +82,9 @@ int32_t rrc::init(const rrc_cfg_t& cfg_, logger.info("Inactivity timeout: %d ms", cfg.inactivity_timeout_ms); logger.info("Max consecutive MAC KOs: %d", cfg.max_mac_dl_kos); + pending_paging.reset(new paging_manager(cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.default_paging_cycle.to_number(), + cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.nb.to_number())); + running = true; return SRSRAN_SUCCESS; @@ -448,132 +452,39 @@ int rrc::modify_erab(uint16_t rnti, void rrc::add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id) { - std::lock_guard lock(paging_mutex); - if (pending_paging.count(ueid) > 0) { - logger.warning("Received Paging for UEID=%d but not yet transmitted", ueid); - return; - } - - paging_record_s paging_elem; if (ue_paging_id.type().value == asn1::s1ap::ue_paging_id_c::types_opts::imsi) { - paging_elem.ue_id.set_imsi(); - paging_elem.ue_id.imsi().resize(ue_paging_id.imsi().size()); - memcpy(paging_elem.ue_id.imsi().data(), ue_paging_id.imsi().data(), ue_paging_id.imsi().size()); - srsran::console("Warning IMSI paging not tested\n"); + pending_paging->add_imsi_paging(ueid, ue_paging_id.imsi()); } else { - paging_elem.ue_id.set_s_tmsi(); - paging_elem.ue_id.s_tmsi().mmec.from_number(ue_paging_id.s_tmsi().mmec[0]); - uint32_t m_tmsi = 0; - uint32_t nof_octets = ue_paging_id.s_tmsi().m_tmsi.size(); - for (uint32_t i = 0; i < nof_octets; i++) { - m_tmsi |= ue_paging_id.s_tmsi().m_tmsi[i] << (8u * (nof_octets - i - 1u)); - } - paging_elem.ue_id.s_tmsi().m_tmsi.from_number(m_tmsi); + pending_paging->add_tmsi_paging(ueid, ue_paging_id.s_tmsi().mmec[0], ue_paging_id.s_tmsi().m_tmsi); } - paging_elem.cn_domain = paging_record_s::cn_domain_e_::ps; - - pending_paging.insert(std::make_pair(ueid, paging_elem)); } -// Described in Section 7 of 36.304 bool rrc::is_paging_opportunity(uint32_t tti, uint32_t* payload_len) { - constexpr static int sf_pattern[4][4] = {{9, 4, -1, 0}, {-1, 9, -1, 4}, {-1, -1, -1, 5}, {-1, -1, -1, 9}}; - - if (tti == paging_tti) { - *payload_len = byte_buf_paging.N_bytes; - logger.debug("Sending paging to extra carriers. Payload len=%d, TTI=%d", *payload_len, tti); - return true; - } else { - paging_tti = INVALID_TTI; - } - - if (pending_paging.empty()) { - return false; - } - - asn1::rrc::pcch_msg_s pcch_msg; - pcch_msg.msg.set_c1(); - paging_s* paging_rec = &pcch_msg.msg.c1().paging(); - - // Default paging cycle, should get DRX from user - uint32_t T = cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.default_paging_cycle.to_number(); - uint32_t Nb = T * cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.nb.to_number(); - - uint32_t N = T < Nb ? T : Nb; - uint32_t Ns = Nb / T > 1 ? Nb / T : 1; - uint32_t sfn = tti / 10; - - std::vector ue_to_remove; - - { - std::lock_guard lock(paging_mutex); - - int n = 0; - for (auto& item : pending_paging) { - if (n >= ASN1_RRC_MAX_PAGE_REC) { - break; - } - const asn1::rrc::paging_record_s& u = item.second; - uint32_t ueid = ((uint32_t)item.first) % 1024; - uint32_t i_s = (ueid / N) % Ns; - - if ((sfn % T) != (T / N) * (ueid % N)) { - continue; - } - - int sf_idx = sf_pattern[i_s % 4][(Ns - 1) % 4]; - if (sf_idx < 0) { - logger.error("SF pattern is N/A for Ns=%d, i_s=%d, imsi_decimal=%d", Ns, i_s, ueid); - continue; - } - - if ((uint32_t)sf_idx == (tti % 10)) { - paging_rec->paging_record_list_present = true; - paging_rec->paging_record_list.push_back(u); - ue_to_remove.push_back(ueid); - n++; - logger.info("Assembled paging for ue_id=%d, tti=%d", ueid, tti); - } - } - - for (unsigned int i : ue_to_remove) { - pending_paging.erase(i); - } - } + *payload_len = pending_paging->pending_pcch_bytes(tti_point(tti)); + return *payload_len > 0; +} - if (paging_rec->paging_record_list.size() > 0) { - byte_buf_paging.clear(); - asn1::bit_ref bref(byte_buf_paging.msg, byte_buf_paging.get_tailroom()); - if (pcch_msg.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { - logger.error("Failed to pack PCCH"); +void rrc::read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t buffer_size) +{ + auto read_func = [this, payload, buffer_size](srsran::const_byte_span pdu, const pcch_msg_s& msg, bool first_tx) { + // copy PCCH pdu to buffer + if (pdu.size() > buffer_size) { + logger.warning("byte buffer with size=%zd is too small to fit pcch msg with size=%zd", buffer_size, pdu.size()); return false; } - byte_buf_paging.N_bytes = (uint32_t)bref.distance_bytes(); - uint32_t N_bits = (uint32_t)bref.distance(); + std::copy(pdu.begin(), pdu.end(), payload); - if (payload_len) { - *payload_len = byte_buf_paging.N_bytes; + if (first_tx) { + logger.info("Assembling PCCH payload with %d UE identities, payload_len=%d bytes", + msg.msg.c1().paging().paging_record_list.size(), + pdu.size()); + log_rrc_message("PCCH-Message", Tx, pdu, msg, msg.msg.c1().type().to_string()); } - logger.info("Assembling PCCH payload with %d UE identities, payload_len=%d bytes, nbits=%d", - paging_rec->paging_record_list.size(), - byte_buf_paging.N_bytes, - N_bits); - log_rrc_message("PCCH-Message", Tx, &byte_buf_paging, pcch_msg, pcch_msg.msg.c1().type().to_string()); - - paging_tti = tti; // Store paging tti for other carriers return true; - } - - return false; -} + }; -void rrc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) -{ - std::lock_guard lock(paging_mutex); - if (byte_buf_paging.N_bytes <= buffer_size) { - memcpy(payload, byte_buf_paging.msg, byte_buf_paging.N_bytes); - } + pending_paging->read_pdu_pcch(tti_point(tti_tx_dl), read_func); } /******************************************************************************* diff --git a/srsenb/src/stack/upper/rlc.cc b/srsenb/src/stack/upper/rlc.cc index 8fe04bede..6c43421aa 100644 --- a/srsenb/src/stack/upper/rlc.cc +++ b/srsenb/src/stack/upper/rlc.cc @@ -179,11 +179,6 @@ void rlc::update_bsr(uint32_t rnti, uint32_t lcid, uint32_t tx_queue, uint32_t r mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); } -void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) -{ - rrc->read_pdu_pcch(payload, buffer_size); -} - int rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { int ret; diff --git a/srsenb/test/mac/sched_test_common.h b/srsenb/test/mac/sched_test_common.h index 591d4656d..7d51c2891 100644 --- a/srsenb/test/mac/sched_test_common.h +++ b/srsenb/test/mac/sched_test_common.h @@ -39,6 +39,7 @@ public: void set_radiolink_dl_state(uint16_t rnti, bool crc_res) {} bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) { return false; } uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) { return nullptr; } + void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t n_bytes) {} }; /************************** From 8487c10956571122b643741112a515e9cb3ef0ba Mon Sep 17 00:00:00 2001 From: Francisco Date: Thu, 22 Apr 2021 15:33:59 +0100 Subject: [PATCH 08/50] removal of unused variables --- srsenb/hdr/stack/rrc/rrc.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index b63fdf90d..c54b93653 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -102,7 +102,7 @@ public: asn1::s1ap::cause_c& cause) override; bool release_erabs(uint32_t rnti) override; int release_erab(uint16_t rnti, uint16_t erab_id) override; - void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& UEPagingID) override; + void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id) override; void ho_preparation_complete(uint16_t rnti, rrc::ho_prep_result result, const asn1::s1ap::ho_cmd_s& msg, @@ -180,11 +180,9 @@ private: void parse_ul_ccch(uint16_t rnti, srsran::unique_byte_buffer_t pdu); void send_rrc_connection_reject(uint16_t rnti); - uint32_t paging_tti = INVALID_TTI; - srsran::byte_buffer_t byte_buf_paging; - const static int mcch_payload_len = 3000; - int current_mcch_length = 0; - uint8_t mcch_payload_buffer[mcch_payload_len] = {}; + const static int mcch_payload_len = 3000; + int current_mcch_length = 0; + uint8_t mcch_payload_buffer[mcch_payload_len] = {}; typedef struct { uint16_t rnti; uint32_t lcid; From b014cbfe0faf4bbf5f082a9d304dc173bc3c118e Mon Sep 17 00:00:00 2001 From: Francisco Date: Thu, 22 Apr 2021 15:47:31 +0100 Subject: [PATCH 09/50] rrc paging - removal of redundant mutexes in paging manager class. Now mutexes are indexed by subframe index instead of paging buffer index --- srsenb/hdr/stack/rrc/rrc_paging.h | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/srsenb/hdr/stack/rrc/rrc_paging.h b/srsenb/hdr/stack/rrc/rrc_paging.h index 74e6367c2..5ae15f66a 100644 --- a/srsenb/hdr/stack/rrc/rrc_paging.h +++ b/srsenb/hdr/stack/rrc/rrc_paging.h @@ -55,7 +55,6 @@ public: private: struct pcch_info { - mutable std::mutex mutex; tti_point tti_tx_dl; asn1::rrc::pcch_msg_s pcch_msg; srsran::unique_byte_buffer_t pdu; @@ -76,7 +75,6 @@ private: { return pending_paging[tti_tx_dl.sfn() % T][tti_tx_dl.sf_idx()]; } - void clear_pcch_info(tti_point tti); // args uint32_t T; @@ -85,17 +83,10 @@ private: uint32_t Ns; srslog::basic_logger& logger; + mutable std::array sf_idx_mutex; std::vector > pending_paging; }; -void paging_manager::clear_pcch_info(tti_point tti) -{ - pcch_info& pcch = get_pcch_info(tti); - - std::lock_guard lock(pcch.mutex); - pcch.clear(); -} - bool paging_manager::add_imsi_paging(uint32_t ueid, srsran::const_byte_span imsi) { asn1::rrc::paging_record_s paging_elem; @@ -136,7 +127,7 @@ bool paging_manager::add_paging_record(uint32_t ueid, const asn1::rrc::paging_re pcch_info& pending_pcch = pending_paging[sfn_cycle_idx][sf_idx]; auto& record_list = pending_pcch.pcch_msg.msg.c1().paging().paging_record_list; - std::lock_guard lock(pending_pcch.mutex); + std::lock_guard lock(sf_idx_mutex[sf_idx]); if (record_list.size() >= ASN1_RRC_MAX_PAGE_REC) { logger.warning("Failed to add new paging record for ueid=%d. Cause: no paging record space left.", ueid); @@ -166,12 +157,15 @@ bool paging_manager::add_paging_record(uint32_t ueid, const asn1::rrc::paging_re size_t paging_manager::pending_pcch_bytes(tti_point tti_tx_dl) { + std::lock_guard lock(sf_idx_mutex[tti_tx_dl.sf_idx()]); + // clear old PCCH that has been transmitted at this point - clear_pcch_info(tti_tx_dl - SRSRAN_NOF_SF_X_FRAME); + pcch_info& old_pcch = get_pcch_info(tti_tx_dl - SRSRAN_NOF_SF_X_FRAME); + if (not old_pcch.empty()) { + old_pcch.clear(); + } const pcch_info& pending_pcch = get_pcch_info(tti_tx_dl); - - std::lock_guard lock(pending_pcch.mutex); if (pending_pcch.empty()) { return 0; } @@ -179,11 +173,11 @@ size_t paging_manager::pending_pcch_bytes(tti_point tti_tx_dl) } template -bool paging_manager::read_pdu_pcch(tti_point tti_tx_dl, const Callable& callable) +bool paging_manager::read_pdu_pcch(tti_point tti_tx_dl, const Callable& func) { pcch_info& pending_pcch = get_pcch_info(tti_tx_dl); - std::lock_guard lock(pending_pcch.mutex); + std::lock_guard lock(sf_idx_mutex[tti_tx_dl.sf_idx()]); if (pending_pcch.empty()) { logger.warning("read_pdu_pdcch(...) called for tti=%d, but there is no pending pcch message", tti_tx_dl.to_uint()); @@ -191,7 +185,7 @@ bool paging_manager::read_pdu_pcch(tti_point tti_tx_dl, const Callable& callable } // Call callable for existing PCCH pdu - if (callable(*pending_pcch.pdu, pending_pcch.pcch_msg, pending_pcch.tti_tx_dl.is_valid())) { + if (func(*pending_pcch.pdu, pending_pcch.pcch_msg, pending_pcch.tti_tx_dl.is_valid())) { pending_pcch.tti_tx_dl = tti_tx_dl; return true; } From cde61a5796e7030ab98e96f4be91074e03fa7add Mon Sep 17 00:00:00 2001 From: Francisco Date: Thu, 22 Apr 2021 16:43:24 +0100 Subject: [PATCH 10/50] rrc paging - reduce the space taken by paging manager class Paging messages can only be transmitted in a subset of subframes - {0, 4, 5, 9} I avoid now creating objects for the subframes that are not part of this subset --- srsenb/hdr/stack/rrc/rrc_paging.h | 53 ++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/srsenb/hdr/stack/rrc/rrc_paging.h b/srsenb/hdr/stack/rrc/rrc_paging.h index 5ae15f66a..022abe38b 100644 --- a/srsenb/hdr/stack/rrc/rrc_paging.h +++ b/srsenb/hdr/stack/rrc/rrc_paging.h @@ -40,10 +40,15 @@ public: } } + /// add new IMSI paging record bool add_imsi_paging(uint32_t ueid, srsran::const_byte_span imsi); + + /// add new TMSI paging record bool add_tmsi_paging(uint32_t ueid, uint8_t mmec, srsran::const_byte_span m_tmsi); + /// Get how many bytes are required to fit the pending PCCH message. size_t pending_pcch_bytes(tti_point tti_tx_dl); + /** * Invoke "callable" for PCCH indexed by tti_tx_dl in a mutexed context. * Callable signature is bool(const_byte_span pdu, const pcch_msg& msg, bool is_first_tx) @@ -68,12 +73,32 @@ private: pdu.reset(); } }; + const static size_t nof_paging_subframes = 4; bool add_paging_record(uint32_t ueid, const asn1::rrc::paging_record_s& paging_record); - pcch_info& get_pcch_info(tti_point tti_tx_dl) { return pending_paging[tti_tx_dl.sfn() % T][tti_tx_dl.sf_idx()]; } + pcch_info& get_pcch_info(tti_point tti_tx_dl) + { + return pending_paging[tti_tx_dl.sfn() % T][get_sf_idx_key(tti_tx_dl.sf_idx())]; + } const pcch_info& get_pcch_info(tti_point tti_tx_dl) const { - return pending_paging[tti_tx_dl.sfn() % T][tti_tx_dl.sf_idx()]; + return pending_paging[tti_tx_dl.sfn() % T][get_sf_idx_key(tti_tx_dl.sf_idx())]; + } + static int get_sf_idx_key(uint32_t sf_idx) + { + switch (sf_idx) { + case 0: + return 0; + case 4: + return 1; + case 5: + return 2; + case 9: + return 3; + default: + break; + } + return -1; } // args @@ -83,8 +108,8 @@ private: uint32_t Ns; srslog::basic_logger& logger; - mutable std::array sf_idx_mutex; - std::vector > pending_paging; + mutable std::array sf_idx_mutex; + std::vector > pending_paging; }; bool paging_manager::add_imsi_paging(uint32_t ueid, srsran::const_byte_span imsi) @@ -124,10 +149,10 @@ bool paging_manager::add_paging_record(uint32_t ueid, const asn1::rrc::paging_re } size_t sfn_cycle_idx = (T / N) * (ueid % N); - pcch_info& pending_pcch = pending_paging[sfn_cycle_idx][sf_idx]; + pcch_info& pending_pcch = pending_paging[sfn_cycle_idx][get_sf_idx_key(sf_idx)]; auto& record_list = pending_pcch.pcch_msg.msg.c1().paging().paging_record_list; - std::lock_guard lock(sf_idx_mutex[sf_idx]); + std::lock_guard lock(sf_idx_mutex[get_sf_idx_key(sf_idx)]); if (record_list.size() >= ASN1_RRC_MAX_PAGE_REC) { logger.warning("Failed to add new paging record for ueid=%d. Cause: no paging record space left.", ueid); @@ -157,7 +182,13 @@ bool paging_manager::add_paging_record(uint32_t ueid, const asn1::rrc::paging_re size_t paging_manager::pending_pcch_bytes(tti_point tti_tx_dl) { - std::lock_guard lock(sf_idx_mutex[tti_tx_dl.sf_idx()]); + int sf_key = get_sf_idx_key(tti_tx_dl.sf_idx()); + if (sf_key < 0) { + // tti_tx_dl is not in a paging subframe + return 0; + } + + std::lock_guard lock(sf_idx_mutex[sf_key]); // clear old PCCH that has been transmitted at this point pcch_info& old_pcch = get_pcch_info(tti_tx_dl - SRSRAN_NOF_SF_X_FRAME); @@ -175,9 +206,15 @@ size_t paging_manager::pending_pcch_bytes(tti_point tti_tx_dl) template bool paging_manager::read_pdu_pcch(tti_point tti_tx_dl, const Callable& func) { + int sf_key = get_sf_idx_key(tti_tx_dl.sf_idx()); + if (sf_key < 0) { + logger.warning("%s was called for tti=%d, which is not a paging subframe index", __FUNCTION__, tti_tx_dl.to_uint()); + return false; + } + pcch_info& pending_pcch = get_pcch_info(tti_tx_dl); - std::lock_guard lock(sf_idx_mutex[tti_tx_dl.sf_idx()]); + std::lock_guard lock(sf_idx_mutex[get_sf_idx_key(tti_tx_dl.sf_idx())]); if (pending_pcch.empty()) { logger.warning("read_pdu_pdcch(...) called for tti=%d, but there is no pending pcch message", tti_tx_dl.to_uint()); From d3c51fdad442cdbc1bf87b6aba9510d46ef1e310 Mon Sep 17 00:00:00 2001 From: Francisco Date: Fri, 30 Apr 2021 14:08:27 +0100 Subject: [PATCH 11/50] limit minimum UL grant size to accommodate both BSR and RLC headers --- srsenb/hdr/stack/mac/sched_ue.h | 12 ++- srsenb/src/stack/mac/sched_grid.cc | 6 +- srsenb/src/stack/mac/sched_ue.cc | 5 ++ .../stack/mac/sched_ue_ctrl/sched_ue_cell.cc | 12 ++- srsenb/test/mac/CMakeLists.txt | 4 + srsenb/test/mac/sched_ue_cell_test.cc | 75 +++++++++++++++++++ 6 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 srsenb/test/mac/sched_ue_cell_test.cc diff --git a/srsenb/hdr/stack/mac/sched_ue.h b/srsenb/hdr/stack/mac/sched_ue.h index 3297a1c98..72d985fa0 100644 --- a/srsenb/hdr/stack/mac/sched_ue.h +++ b/srsenb/hdr/stack/mac/sched_ue.h @@ -81,10 +81,12 @@ public: uint32_t get_required_prb_ul(uint32_t enb_cc_idx, uint32_t req_bytes); - rbg_interval get_required_dl_rbgs(uint32_t enb_cc_idx); - srsran::interval get_requested_dl_bytes(uint32_t enb_cc_idx); - uint32_t get_pending_dl_rlc_data() const; - uint32_t get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs = -1) const; + /// Get total pending bytes to be transmitted in DL. + /// The amount of CEs to transmit depends on whether enb_cc_idx is UE's PCell + uint32_t get_pending_dl_bytes(uint32_t enb_cc_idx); + rbg_interval get_required_dl_rbgs(uint32_t enb_cc_idx); + uint32_t get_pending_dl_rlc_data() const; + uint32_t get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs = -1) const; uint32_t get_pending_ul_data_total(tti_point tti_tx_ul, int this_enb_cc_idx); uint32_t get_pending_ul_new_data(tti_point tti_tx_ul, int this_enb_cc_idx); @@ -138,6 +140,8 @@ public: bool pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_pdcch) const; private: + srsran::interval get_requested_dl_bytes(uint32_t enb_cc_idx); + bool is_sr_triggered(); tbs_info allocate_new_dl_mac_pdu(sched_interface::dl_sched_data_t* data, diff --git a/srsenb/src/stack/mac/sched_grid.cc b/srsenb/src/stack/mac/sched_grid.cc index fcb3b7d24..44fb59d30 100644 --- a/srsenb/src/stack/mac/sched_grid.cc +++ b/srsenb/src/stack/mac/sched_grid.cc @@ -662,7 +662,7 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& continue; } sched_ue* user = ue_it->second.get(); - uint32_t data_before = user->get_requested_dl_bytes(cc_cfg->enb_cc_idx).stop(); + uint32_t data_before = user->get_pending_dl_bytes(cc_cfg->enb_cc_idx); const dl_harq_proc& dl_harq = user->get_dl_harq(data_alloc.pid, cc_cfg->enb_cc_idx); bool is_newtx = dl_harq.is_empty(); @@ -678,7 +678,7 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& data_alloc.pid, data_alloc.user_mask, tbs, - user->get_requested_dl_bytes(cc_cfg->enb_cc_idx).stop()); + user->get_pending_dl_bytes(cc_cfg->enb_cc_idx)); logger.warning("%s", srsran::to_c_str(str_buffer)); continue; } @@ -698,7 +698,7 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& dl_harq.nof_retx(0) + dl_harq.nof_retx(1), tbs, data_before, - user->get_requested_dl_bytes(cc_cfg->enb_cc_idx).stop(), + user->get_pending_dl_bytes(cc_cfg->enb_cc_idx), get_tti_tx_dl()); logger.info("%s", srsran::to_c_str(str_buffer)); } diff --git a/srsenb/src/stack/mac/sched_ue.cc b/srsenb/src/stack/mac/sched_ue.cc index 09e30e91b..86b28d429 100644 --- a/srsenb/src/stack/mac/sched_ue.cc +++ b/srsenb/src/stack/mac/sched_ue.cc @@ -767,6 +767,11 @@ rbg_interval sched_ue::get_required_dl_rbgs(uint32_t enb_cc_idx) return {min_pending_rbg, max_pending_rbg}; } +uint32_t sched_ue::get_pending_dl_bytes(uint32_t enb_cc_idx) +{ + return get_requested_dl_bytes(enb_cc_idx).stop(); +} + /** * Returns the range (min,max) of possible MAC PDU sizes. * - the lower boundary value is set based on the following conditions: diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc index f1ec7c8ef..96a691ed0 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc @@ -298,6 +298,7 @@ int get_required_prb_dl(const sched_ue_cell& cell, uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes) { + const static int MIN_ALLOC_BYTES = 10; /// There should be enough space for RLC header + BSR + some payload if (req_bytes == 0) { return 0; } @@ -308,11 +309,18 @@ uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes) }; // find nof prbs that lead to a tbs just above req_bytes - int target_tbs = static_cast(req_bytes) + 4; + int target_tbs = std::max(static_cast(req_bytes) + 4, MIN_ALLOC_BYTES); uint32_t max_prbs = std::min(cell.tpc_fsm.max_ul_prbs(), cell.cell_cfg->nof_prb()); std::tuple ret = false_position_method(1U, max_prbs, target_tbs, compute_tbs_approx, [](int y) { return y == SRSRAN_ERROR; }); - uint32_t req_prbs = std::get<2>(ret); + uint32_t req_prbs = std::get<2>(ret); + uint32_t final_tbs = std::get<3>(ret); + while (final_tbs < MIN_ALLOC_BYTES and req_prbs < cell.cell_cfg->nof_prb()) { + // Note: If PHR<0 is limiting the max nof PRBs per UL grant, the UL grant may become too small to fit any + // data other than headers + BSR. In this edge-case, force an increase the nof required PRBs. + req_prbs++; + final_tbs = compute_tbs_approx(req_prbs); + } while (!srsran_dft_precoding_valid_prb(req_prbs) && req_prbs < cell.cell_cfg->nof_prb()) { req_prbs++; } diff --git a/srsenb/test/mac/CMakeLists.txt b/srsenb/test/mac/CMakeLists.txt index c7f8e184c..c7e721db2 100644 --- a/srsenb/test/mac/CMakeLists.txt +++ b/srsenb/test/mac/CMakeLists.txt @@ -59,6 +59,10 @@ add_executable(sched_dci_test sched_dci_test.cc) target_link_libraries(sched_dci_test srsran_common srsenb_mac srsran_mac sched_test_common) add_test(sched_dci_test sched_dci_test) +add_executable(sched_ue_cell_test sched_ue_cell_test.cc) +target_link_libraries(sched_ue_cell_test srsran_common srsenb_mac srsran_mac sched_test_common) +add_test(sched_ue_cell_test sched_ue_cell_test) + add_executable(sched_benchmark_test sched_benchmark.cc) target_link_libraries(sched_benchmark_test srsran_common srsenb_mac srsran_mac sched_test_common) add_test(sched_benchmark_test sched_benchmark_test) diff --git a/srsenb/test/mac/sched_ue_cell_test.cc b/srsenb/test/mac/sched_ue_cell_test.cc new file mode 100644 index 000000000..a6a90e81b --- /dev/null +++ b/srsenb/test/mac/sched_ue_cell_test.cc @@ -0,0 +1,75 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "sched_test_common.h" +#include "sched_test_utils.h" +#include "srsenb/hdr/stack/mac/sched_ue.h" +#include "srsran/common/test_common.h" + +using namespace srsenb; +const uint32_t seed = std::chrono::system_clock::now().time_since_epoch().count(); + +/** + * Test scenario where PHR < 0, and the UL grant size is limited. + * - The UL grant size should be the smallest possible that guarantees fitting both a BSR, RLC header, and some RLC + * payload. + */ +void test_neg_phr_scenario() +{ + sched_interface::cell_cfg_t cell_cfg = generate_default_cell_cfg(50); + cell_cfg.target_pucch_ul_sinr = 20; + cell_cfg.target_pusch_ul_sinr = 20; + cell_cfg.enable_phr_handling = true; + sched_interface::sched_args_t sched_cfg = {}; + sched_cell_params_t cell_params; + cell_params.set_cfg(0, cell_cfg, sched_cfg); + sched_interface::ue_cfg_t ue_cfg = generate_default_ue_cfg(); + + sched_ue_cell ue_cc(0x46, cell_params, tti_point(0)); + ue_cc.set_ue_cfg(ue_cfg); + + float snr = 0; + ue_cc.tpc_fsm.set_snr(snr, 0); + ue_cc.tpc_fsm.set_snr(snr, 1); + ue_cc.ul_cqi = srsran_cqi_from_snr(snr); + ue_cc.tpc_fsm.set_phr(-5); + ue_cc.new_tti(tti_point(0)); + + uint32_t req_bytes = 10000; + uint32_t pending_prbs = get_required_prb_ul(ue_cc, req_bytes); + TESTASSERT(pending_prbs < 10); // The PHR<0 is limiting the number of allocated PRBs + + uint32_t N_srs = 0; + uint32_t prb_grant_size = pending_prbs; + uint32_t nof_symb = 2 * (SRSRAN_CP_NSYMB(cell_cfg.cell.cp) - 1) - N_srs; + uint32_t nof_re = nof_symb * prb_grant_size * SRSRAN_NRE; + tbs_info tbinfo = cqi_to_tbs_ul(ue_cc, prb_grant_size, nof_re, req_bytes); + TESTASSERT(tbinfo.tbs_bytes >= 10); +} + +int main() +{ + srsenb::set_randseed(seed); + srsran::console("This is the chosen seed: %u\n", seed); + + auto& test_log = srslog::fetch_basic_logger("TEST", false); + test_log.set_level(srslog::basic_levels::info); + + // Start the log backend. + srslog::init(); + + test_neg_phr_scenario(); + + srslog::flush(); + + srsran::console("Success\n"); +} From c69631462b26db41610c61f04d8c1249064df905 Mon Sep 17 00:00:00 2001 From: Francisco Date: Fri, 30 Apr 2021 14:36:30 +0100 Subject: [PATCH 12/50] sched, fix - limit UL cqi update to PUSCH SNR reports --- srsenb/src/stack/mac/sched_ue.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/srsenb/src/stack/mac/sched_ue.cc b/srsenb/src/stack/mac/sched_ue.cc index 86b28d429..55e7f2504 100644 --- a/srsenb/src/stack/mac/sched_ue.cc +++ b/srsenb/src/stack/mac/sched_ue.cc @@ -302,8 +302,10 @@ void sched_ue::set_ul_snr(tti_point tti_rx, uint32_t enb_cc_idx, float snr, uint { if (cells[enb_cc_idx].cc_state() != cc_st::idle) { cells[enb_cc_idx].tpc_fsm.set_snr(snr, ul_ch_code); - cells[enb_cc_idx].ul_cqi = srsran_cqi_from_snr(snr); - cells[enb_cc_idx].ul_cqi_tti_rx = tti_rx; + if (ul_ch_code == tpc::PUSCH_CODE) { + cells[enb_cc_idx].ul_cqi = srsran_cqi_from_snr(snr); + cells[enb_cc_idx].ul_cqi_tti_rx = tti_rx; + } } else { logger.warning("Received SNR info for invalid cell index %d", enb_cc_idx); } From f7016633d24e3956412b294bebe28410a63f49ee Mon Sep 17 00:00:00 2001 From: Francisco Date: Fri, 30 Apr 2021 15:53:03 +0100 Subject: [PATCH 13/50] sched, fix - avoid tpc commands when target pusch and pucch snr are not specified --- srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h | 14 +++++--------- srsenb/test/mac/sched_tpc_test.cc | 9 ++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h index e65b86149..99dc58270 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h @@ -110,14 +110,14 @@ public: * @remark See TS 36.213 Section 5.1.1 * @return accumulated TPC value {-1, 0, 1, 3} */ - uint8_t encode_pusch_tpc() { return enconde_tpc(PUSCH_CODE); } + uint8_t encode_pusch_tpc() { return encode_tpc(PUSCH_CODE); } /** * Called during DCI format1/2A/A encoding to set PUCCH TPC command * @remark See TS 36.213 Section 5.1.2 * @return accumulated TPC value {-1, 0, 1, 3} */ - uint8_t encode_pucch_tpc() { return enconde_tpc(PUCCH_CODE); } + uint8_t encode_pucch_tpc() { return encode_tpc(PUCCH_CODE); } uint32_t max_ul_prbs() const { return max_prbs_cached; } @@ -138,18 +138,14 @@ private: return 1; } } - uint8_t enconde_tpc(uint32_t cc) + uint8_t encode_tpc(uint32_t cc) { float target_snr_dB = cc == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB; auto& ch_snr = snr_estim_list[cc]; assert(ch_snr.pending_delta == 0); // ensure called once per {cc,tti} if (target_snr_dB < 0) { - // undefined target SINR case. Increase Tx power once per PHR, considering the number of allocable PRBs remains - // unchanged - if (not ch_snr.phr_flag) { - ch_snr.pending_delta = (max_prbs_cached == nof_prb) ? 1 : (last_phr < 0 ? -1 : 0); - ch_snr.phr_flag = true; - } + // undefined target sinr case. + ch_snr.pending_delta = 0; } else { // target SINR is finite and there is power headroom float diff = target_snr_dB - ch_snr.snr_avg.value(); diff --git a/srsenb/test/mac/sched_tpc_test.cc b/srsenb/test/mac/sched_tpc_test.cc index f928fcaa0..acfe07ea4 100644 --- a/srsenb/test/mac/sched_tpc_test.cc +++ b/srsenb/test/mac/sched_tpc_test.cc @@ -100,7 +100,7 @@ int test_undefined_target_snr() TESTASSERT(sum_pusch == 0); TESTASSERT(sum_pucch == 0); - // TEST: If the PHR allows full utilization of available PRBs, the TPC slightly increments UL Tx power + // TEST: Check that high PHR allows full utilization of available PRBs, TPC remains at zero (no target SINR) int phr = 30; tpcfsm.set_phr(phr); TESTASSERT(tpcfsm.max_ul_prbs() == 50); @@ -111,8 +111,7 @@ int test_undefined_target_snr() sum_pusch += decode_tpc(tpcfsm.encode_pusch_tpc()); sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc()); } - TESTASSERT(sum_pusch > 0 and sum_pusch <= 3); - TESTASSERT(sum_pucch > 0 and sum_pucch <= 3); + TESTASSERT(sum_pusch == 0 and sum_pucch == 0); // TEST: PHR is too low to allow all PRBs to be allocated. This event should not affect TPC commands phr = 5; @@ -135,8 +134,8 @@ int test_undefined_target_snr() sum_pusch += decode_tpc(tpcfsm.encode_pusch_tpc()); sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc()); } - TESTASSERT(sum_pusch <= 0 and sum_pusch >= -1); - TESTASSERT(sum_pucch <= 0 and sum_pucch >= -1); + TESTASSERT(sum_pusch == 0); + TESTASSERT(sum_pucch == 0); return SRSRAN_SUCCESS; } From a43dd68d57dab73dd8766de2bedf486945f3ac1e Mon Sep 17 00:00:00 2001 From: Francisco Date: Mon, 3 May 2021 15:48:03 +0100 Subject: [PATCH 14/50] update comment, explaining the reasoning behind setting a lower bound for allocated UL grants --- srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc index 96a691ed0..945373986 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc @@ -298,7 +298,7 @@ int get_required_prb_dl(const sched_ue_cell& cell, uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes) { - const static int MIN_ALLOC_BYTES = 10; /// There should be enough space for RLC header + BSR + some payload + const static int MIN_ALLOC_BYTES = 10; if (req_bytes == 0) { return 0; } @@ -317,7 +317,9 @@ uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes) uint32_t final_tbs = std::get<3>(ret); while (final_tbs < MIN_ALLOC_BYTES and req_prbs < cell.cell_cfg->nof_prb()) { // Note: If PHR<0 is limiting the max nof PRBs per UL grant, the UL grant may become too small to fit any - // data other than headers + BSR. In this edge-case, force an increase the nof required PRBs. + // data other than headers + BSR. Besides, forcing unnecessary segmentation, it may additionally + // forbid the UE from fitting small RRC messages (e.g. RRCReconfComplete) in the UL grants. + // To avoid TBS<10, we force an increase the nof required PRBs. req_prbs++; final_tbs = compute_tbs_approx(req_prbs); } From 051530177e195cf43f75fc30916e59ed8a0596f6 Mon Sep 17 00:00:00 2001 From: Francisco Date: Fri, 30 Apr 2021 18:22:31 +0100 Subject: [PATCH 15/50] feature - support eia0 during S1 Handover --- srsenb/src/stack/rrc/rrc_bearer_cfg.cc | 2 ++ srsenb/src/stack/rrc/rrc_mobility.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc index 625b94453..e0ac3b6b8 100644 --- a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc +++ b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc @@ -105,6 +105,8 @@ bool security_cfg_handler::set_security_capabilities(const asn1::s1ap::ue_securi case srsran::INTEGRITY_ALGORITHM_ID_EIA0: // Null integrity is not supported logger.info("Skipping EIA0 as RRC integrity algorithm. Null integrity is not supported."); + sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_EIA0; + integ_algo_found = true; break; case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1: // “first bit” – 128-EIA1, diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index e0f2409c4..8c7c5a07c 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -106,7 +106,7 @@ uint16_t compute_mac_i(uint16_t crnti, mac_key); break; default: - printf("Unsupported integrity algorithm %d.", integ_algo); + srsran::console_stderr("ERROR: Unsupported integrity algorithm %d.\n", integ_algo); } uint16_t short_mac_i = (((uint16_t)mac_key[2] << 8u) | (uint16_t)mac_key[3]); From d175bb281bd6e1a08e6253f1f176ba95bfdc1b60 Mon Sep 17 00:00:00 2001 From: Francisco Date: Fri, 30 Apr 2021 19:31:05 +0100 Subject: [PATCH 16/50] avoid error logging during handover if EIA0 has been chosen --- srsenb/src/stack/rrc/rrc_mobility.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 8c7c5a07c..95bf9d8ee 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -87,6 +87,8 @@ uint16_t compute_mac_i(uint16_t crnti, // Compute MAC-I switch (integ_algo) { + case srsran::INTEGRITY_ALGORITHM_ID_EIA0: + return 0; case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1: srsran::security_128_eia1(&k_rrc_int[16], 0xffffffff, // 32-bit all to ones From 04ded030eadf653c1f74c19d12295880b1c4eb3c Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 28 Apr 2021 17:36:07 +0100 Subject: [PATCH 17/50] srsenb,bugfix - avoid logging error message when UE sends CRNTI MAC CE for old rnti whose context has already been erased. --- srsenb/hdr/stack/mac/sched.h | 2 +- srsenb/src/stack/mac/sched.cc | 15 +++++++++------ srsenb/src/stack/mac/ue.cc | 6 +++++- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/srsenb/hdr/stack/mac/sched.h b/srsenb/hdr/stack/mac/sched.h index 0014e707c..0a6977f1b 100644 --- a/srsenb/hdr/stack/mac/sched.h +++ b/srsenb/hdr/stack/mac/sched.h @@ -84,7 +84,7 @@ protected: bool is_generated(srsran::tti_point, uint32_t enb_cc_idx) const; // Helper methods template - int ue_db_access_locked(uint16_t rnti, Func&& f, const char* func_name = nullptr); + int ue_db_access_locked(uint16_t rnti, Func&& f, const char* func_name = nullptr, bool log_fail = true); // args rrc_interface_mac* rrc = nullptr; diff --git a/srsenb/src/stack/mac/sched.cc b/srsenb/src/stack/mac/sched.cc index 525b31f54..48e1724a0 100644 --- a/srsenb/src/stack/mac/sched.cc +++ b/srsenb/src/stack/mac/sched.cc @@ -125,7 +125,8 @@ int sched::ue_rem(uint16_t rnti) bool sched::ue_exists(uint16_t rnti) { - return ue_db_access_locked(rnti, [](sched_ue& ue) {}) >= 0; + return ue_db_access_locked( + rnti, [](sched_ue& ue) {}, nullptr, false) >= 0; } void sched::phy_config_enabled(uint16_t rnti, bool enabled) @@ -351,17 +352,19 @@ bool sched::is_generated(srsran::tti_point tti_rx, uint32_t enb_cc_idx) const // Common way to access ue_db elements in a read locking way template -int sched::ue_db_access_locked(uint16_t rnti, Func&& f, const char* func_name) +int sched::ue_db_access_locked(uint16_t rnti, Func&& f, const char* func_name, bool log_fail) { std::lock_guard lock(sched_mutex); auto it = ue_db.find(rnti); if (it != ue_db.end()) { f(*it->second); } else { - if (func_name != nullptr) { - Error("User rnti=0x%x not found. Failed to call %s.", rnti, func_name); - } else { - Error("User rnti=0x%x not found.", rnti); + if (log_fail) { + if (func_name != nullptr) { + Error("SCHED: User rnti=0x%x not found. Failed to call %s.", rnti, func_name); + } else { + Error("SCHED: User rnti=0x%x not found.", rnti); + } } return SRSRAN_ERROR; } diff --git a/srsenb/src/stack/mac/ue.cc b/srsenb/src/stack/mac/ue.cc index 4cbd7d66c..94d32898c 100644 --- a/srsenb/src/stack/mac/ue.cc +++ b/srsenb/src/stack/mac/ue.cc @@ -468,7 +468,11 @@ bool ue::process_ce(srsran::sch_subh* subh) rrc->upd_user(rnti, old_rnti); rnti = old_rnti; } else { - logger.error("Updating user C-RNTI: rnti=0x%x already released", old_rnti); + logger.warning("Updating user C-RNTI: rnti=0x%x already released.", old_rnti); + // Disable scheduling for all bearers. The new rnti will be removed on msg3 timer expiry in the RRC + for (uint32_t lcid = 0; lcid < sched_interface::MAX_LC; ++lcid) { + sched->bearer_ue_rem(rnti, lcid); + } } break; case srsran::ul_sch_lcid::TRUNC_BSR: From aef18f9931f49097b58dc8f8f9b3a049cb865330 Mon Sep 17 00:00:00 2001 From: faluco Date: Wed, 28 Apr 2021 10:21:56 +0200 Subject: [PATCH 18/50] Implement a benchmark for measuring latency in the foreground threads when pushing log entries. This benchmark runs with several threads to test contention. --- .../srsran/srslog/detail/log_backend.h | 3 +- .../srsran/srslog/detail/support/work_queue.h | 61 ++------ lib/include/srsran/srslog/shared_types.h | 11 ++ lib/include/srsran/srslog/srslog.h | 2 +- lib/src/srslog/backend_worker.cpp | 55 ++++++- lib/src/srslog/backend_worker.h | 12 +- lib/src/srslog/log_backend_impl.h | 2 +- lib/src/srslog/srslog.cpp | 4 +- lib/test/srslog/CMakeLists.txt | 3 + .../srslog/benchmarks/frontend_latency.cpp | 137 ++++++++++++++++++ lib/test/srslog/event_trace_test.cpp | 2 +- lib/test/srslog/log_channel_test.cpp | 2 +- lib/test/srslog/test_dummies.h | 3 +- 13 files changed, 229 insertions(+), 68 deletions(-) create mode 100644 lib/test/srslog/benchmarks/frontend_latency.cpp diff --git a/lib/include/srsran/srslog/detail/log_backend.h b/lib/include/srsran/srslog/detail/log_backend.h index d1765b498..5af3ec13a 100644 --- a/lib/include/srsran/srslog/detail/log_backend.h +++ b/lib/include/srsran/srslog/detail/log_backend.h @@ -14,6 +14,7 @@ #define SRSLOG_DETAIL_LOG_BACKEND_H #include "srsran/srslog/bundled/fmt/printf.h" +#include "srsran/srslog/shared_types.h" namespace srslog { @@ -31,7 +32,7 @@ public: /// Starts the processing of incoming log entries. /// NOTE: Calling this function more than once has no side effects. - virtual void start() = 0; + virtual void start(backend_priority priority = backend_priority::normal) = 0; /// Allocates a dyn_arg_store and returns a pointer to it on success, otherwise returns nullptr. virtual fmt::dynamic_format_arg_store* alloc_arg_store() = 0; diff --git a/lib/include/srsran/srslog/detail/support/work_queue.h b/lib/include/srsran/srslog/detail/support/work_queue.h index 00b29721e..ffe32735f 100644 --- a/lib/include/srsran/srslog/detail/support/work_queue.h +++ b/lib/include/srsran/srslog/detail/support/work_queue.h @@ -28,7 +28,7 @@ template class work_queue { srsran::dyn_circular_buffer queue; - mutable condition_variable cond_var; + mutable mutex m; static constexpr size_t threshold = capacity * 0.98; public: @@ -41,15 +41,14 @@ public: /// queue is full, otherwise true. bool push(const T& value) { - cond_var.lock(); + m.lock(); // Discard the new element if we reach the maximum capacity. if (queue.full()) { - cond_var.unlock(); + m.unlock(); return false; } queue.push(value); - cond_var.unlock(); - cond_var.signal(); + m.unlock(); return true; } @@ -58,56 +57,26 @@ public: /// queue is full, otherwise true. bool push(T&& value) { - cond_var.lock(); + m.lock(); // Discard the new element if we reach the maximum capacity. if (queue.full()) { - cond_var.unlock(); + m.unlock(); return false; } queue.push(std::move(value)); - cond_var.unlock(); - cond_var.signal(); + m.unlock(); return true; } - /// Extracts the top most element from the queue. - /// NOTE: This method blocks while the queue is empty. - T pop() + /// Extracts the top most element from the queue if it exists. + /// Returns a pair with a bool indicating if the pop has been successful. + std::pair try_pop() { - cond_var.lock(); + m.lock(); - while (queue.empty()) { - cond_var.wait(); - } - - T elem = std::move(queue.top()); - queue.pop(); - - cond_var.unlock(); - - return elem; - } - - /// Extracts the top most element from the queue. - /// NOTE: This method blocks while the queue is empty or or until the - /// programmed timeout expires. Returns a pair with a bool indicating if the - /// pop has been successful. - std::pair timed_pop(unsigned timeout_ms) - { - // Build an absolute time reference for the expiration time. - timespec ts = condition_variable::build_timeout(timeout_ms); - - cond_var.lock(); - - bool timedout = false; - while (queue.empty() && !timedout) { - timedout = cond_var.wait(ts); - } - - // Did we wake up on timeout? - if (timedout && queue.empty()) { - cond_var.unlock(); + if (queue.empty()) { + m.unlock(); return {false, T()}; } @@ -115,7 +84,7 @@ public: T Item = std::move(queue.top()); queue.pop(); - cond_var.unlock(); + m.unlock(); return {true, std::move(Item)}; } @@ -126,7 +95,7 @@ public: /// Returns true when the queue is almost full, otherwise returns false. bool is_almost_full() const { - cond_var_scoped_lock lock(cond_var); + scoped_lock lock(m); return queue.size() > threshold; } diff --git a/lib/include/srsran/srslog/shared_types.h b/lib/include/srsran/srslog/shared_types.h index 0598c59dd..c508219ef 100644 --- a/lib/include/srsran/srslog/shared_types.h +++ b/lib/include/srsran/srslog/shared_types.h @@ -20,6 +20,17 @@ namespace srslog { /// Generic error handler callback. using error_handler = std::function; +/// Backend priority levels. +enum class backend_priority +{ + /// Default priority of the operating system. + normal, + /// Thread will be given a high priority. + high, + /// Thread will be given a very high priority. + very_high +}; + } // namespace srslog #endif // SRSLOG_SHARED_TYPES_H diff --git a/lib/include/srsran/srslog/srslog.h b/lib/include/srsran/srslog/srslog.h index 9de919ceb..632d38a91 100644 --- a/lib/include/srsran/srslog/srslog.h +++ b/lib/include/srsran/srslog/srslog.h @@ -223,7 +223,7 @@ sink* create_stderr_sink(const std::string& name = "stderr"); /// This function initializes the logging framework. It must be called before /// any log entry is generated. /// NOTE: Calling this function more than once has no side effects. -void init(); +void init(backend_priority priority = backend_priority::normal); /// Flushes the contents of all the registered sinks. The caller thread will /// block until the operation is completed. diff --git a/lib/src/srslog/backend_worker.cpp b/lib/src/srslog/backend_worker.cpp index a564b69fe..31bbc6e59 100644 --- a/lib/src/srslog/backend_worker.cpp +++ b/lib/src/srslog/backend_worker.cpp @@ -24,12 +24,48 @@ void backend_worker::stop() } } -void backend_worker::create_worker() +void backend_worker::set_thread_priority(backend_priority priority) const +{ + switch (priority) { + case backend_priority::normal: + break; + case backend_priority::high: { + int min = ::sched_get_priority_min(SCHED_FIFO); + if (min == -1) { + err_handler("Unable to set the backend thread priority to high, falling back to normal priority."); + return; + } + ::sched_param sch{min}; + if (::pthread_setschedparam(::pthread_self(), SCHED_FIFO, &sch)) { + err_handler("Unable to set the backend thread priority to high, falling back to normal priority."); + return; + } + break; + } + case backend_priority::very_high: { + int max = ::sched_get_priority_max(SCHED_FIFO); + int min = ::sched_get_priority_min(SCHED_FIFO); + if (max == -1 || min == -1) { + err_handler("Unable to set the backend thread priority to real time, falling back to normal priority."); + return; + } + ::sched_param sch{min + ((max - min) / 2)}; + if (::pthread_setschedparam(::pthread_self(), SCHED_FIFO, &sch)) { + err_handler("Unable to set the backend thread priority to real time, falling back to normal priority."); + return; + } + break; + } + } +} + +void backend_worker::create_worker(backend_priority priority) { assert(!running_flag && "Only one worker thread should be created"); - std::thread t([this]() { + std::thread t([this, priority]() { running_flag = true; + set_thread_priority(priority); do_work(); }); @@ -41,21 +77,26 @@ void backend_worker::create_worker() } } -void backend_worker::start() +void backend_worker::start(backend_priority priority) { // Ensure we only create the worker thread once. - std::call_once(start_once_flag, [this]() { create_worker(); }); + std::call_once(start_once_flag, [this, priority]() { create_worker(priority); }); } void backend_worker::do_work() { assert(running_flag && "Thread entry function called without running thread"); + /// This period defines the time the worker will sleep while waiting for new entries. This is required to check the + /// termination variable periodically. + constexpr std::chrono::microseconds sleep_period{100}; + while (running_flag) { - auto item = queue.timed_pop(sleep_period_ms); + auto item = queue.try_pop(); - // Spin again when the timeout expires. + // Spin while there are no new entries to process. if (!item.first) { + std::this_thread::sleep_for(sleep_period); continue; } @@ -113,7 +154,7 @@ void backend_worker::process_outstanding_entries() assert(!running_flag && "Cannot process outstanding entries while thread is running"); while (true) { - auto item = queue.timed_pop(1); + auto item = queue.try_pop(); // Check if the queue is empty. if (!item.first) { diff --git a/lib/src/srslog/backend_worker.h b/lib/src/srslog/backend_worker.h index e45786dab..9c3992331 100644 --- a/lib/src/srslog/backend_worker.h +++ b/lib/src/srslog/backend_worker.h @@ -26,11 +26,6 @@ namespace srslog { /// log entries from a work queue and dispatches them to the selected sinks. class backend_worker { - /// This period defines the maximum time the worker will sleep while waiting - /// for new entries. This is required to check the termination variable - /// periodically. - static constexpr unsigned sleep_period_ms = 500; - public: backend_worker(detail::work_queue& queue, detail::dyn_arg_store_pool& arg_pool) : queue(queue), arg_pool(arg_pool), running_flag(false) @@ -44,7 +39,7 @@ public: /// Starts the backend worker thread. After returning from this function the /// secondary thread is ensured to be running. Calling this function more than /// once has no effect. - void start(); + void start(backend_priority priority); /// Stops the backend worker thread if it is running, otherwise the call has /// no effect. After returning from this function the secondary thread is @@ -78,7 +73,7 @@ public: private: /// Creates the worker thread. /// NOTE: This function should be only called once. - void create_worker(); + void create_worker(backend_priority priority); /// Entry function used by the secondary thread. void do_work(); @@ -103,6 +98,9 @@ private: } } + /// Establishes the specified thread priority for the calling thread. + void set_thread_priority(backend_priority priority) const; + private: detail::work_queue& queue; detail::dyn_arg_store_pool& arg_pool; diff --git a/lib/src/srslog/log_backend_impl.h b/lib/src/srslog/log_backend_impl.h index 23ca4b215..ed959a2ab 100644 --- a/lib/src/srslog/log_backend_impl.h +++ b/lib/src/srslog/log_backend_impl.h @@ -29,7 +29,7 @@ public: log_backend_impl(const log_backend_impl& other) = delete; log_backend_impl& operator=(const log_backend_impl& other) = delete; - void start() override { worker.start(); } + void start(backend_priority priority = backend_priority::normal) override { worker.start(priority); } bool push(detail::log_entry&& entry) override { diff --git a/lib/src/srslog/srslog.cpp b/lib/src/srslog/srslog.cpp index d223b1f0f..b50e6c627 100644 --- a/lib/src/srslog/srslog.cpp +++ b/lib/src/srslog/srslog.cpp @@ -181,9 +181,9 @@ bool srslog::install_custom_sink(const std::string& id, std::unique_ptr s) /// Framework configuration and control function implementations. /// -void srslog::init() +void srslog::init(backend_priority priority) { - srslog_instance::get().get_backend().start(); + srslog_instance::get().get_backend().start(priority); } void srslog::flush() diff --git a/lib/test/srslog/CMakeLists.txt b/lib/test/srslog/CMakeLists.txt index 7859d3d23..46a59303e 100644 --- a/lib/test/srslog/CMakeLists.txt +++ b/lib/test/srslog/CMakeLists.txt @@ -6,6 +6,9 @@ # the distribution. # +add_executable(srslog_frontend_latency benchmarks/frontend_latency.cpp) +target_link_libraries(srslog_frontend_latency srslog) + add_executable(srslog_test srslog_test.cpp) target_link_libraries(srslog_test srslog) add_test(srslog_test srslog_test) diff --git a/lib/test/srslog/benchmarks/frontend_latency.cpp b/lib/test/srslog/benchmarks/frontend_latency.cpp new file mode 100644 index 000000000..2866055bc --- /dev/null +++ b/lib/test/srslog/benchmarks/frontend_latency.cpp @@ -0,0 +1,137 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/srslog/srslog.h" +#include +#include +#include + +using namespace srslog; + +static constexpr unsigned num_iterations = 4000; +static constexpr unsigned num_entries_per_iter = 40; + +namespace { + +/// This helper class checks if there has been context switches between its construction and destruction for the caller +/// thread. +class context_switch_checker +{ +public: + explicit context_switch_checker(std::atomic& counter) : counter(counter) + { + ::getrusage(RUSAGE_THREAD, &before); + } + + ~context_switch_checker() + { + ::rusage after{}; + ::getrusage(RUSAGE_THREAD, &after); + unsigned diff = (after.ru_nvcsw - before.ru_nvcsw) + (after.ru_nivcsw - before.ru_nivcsw); + if (diff) { + counter.fetch_add(diff, std::memory_order_relaxed); + } + } + +private: + ::rusage before{}; + std::atomic& counter; +}; + +} // namespace + +/// Busy waits in the calling thread for the specified amount of time. +static void busy_wait(std::chrono::milliseconds interval) +{ + auto begin = std::chrono::steady_clock::now(); + auto end = begin + interval; + + while (std::chrono::steady_clock::now() < end) { + } +} + +/// Worker function used for each thread of the benchmark to generate and measure the time taken for each log entry. +static void run_thread(log_channel& c, std::vector& results, std::atomic& ctx_counter) +{ + for (unsigned iter = 0; iter != num_iterations; ++iter) { + context_switch_checker ctx_checker(ctx_counter); + + auto begin = std::chrono::steady_clock::now(); + for (unsigned entry_num = 0; entry_num != num_entries_per_iter; ++entry_num) { + double d = entry_num; + c("SRSLOG latency benchmark: int: %u, double: %f, string: %s", iter, d, "test"); + } + auto end = std::chrono::steady_clock::now(); + + results.push_back(std::chrono::duration_cast(end - begin).count() / num_entries_per_iter); + + busy_wait(std::chrono::milliseconds(4)); + } +} + +/// This function runs the latency benchmark generating log entries using the specified number of threads. +static void benchmark(unsigned num_threads) +{ + std::vector > thread_results; + thread_results.resize(num_threads); + for (auto& v : thread_results) { + v.reserve(num_iterations); + } + + auto& s = srslog::fetch_file_sink("srslog_latency_benchmark.txt"); + auto& channel = srslog::fetch_log_channel("bench", s, {}); + + srslog::init(); + + std::vector workers; + workers.reserve(num_threads); + + std::atomic ctx_counter(0); + for (unsigned i = 0; i != num_threads; ++i) { + workers.emplace_back(run_thread, std::ref(channel), std::ref(thread_results[i]), std::ref(ctx_counter)); + } + for (auto& w : workers) { + w.join(); + } + + std::vector results; + results.reserve(num_threads * num_iterations); + for (const auto& v : thread_results) { + results.insert(results.end(), v.begin(), v.end()); + } + std::sort(results.begin(), results.end()); + + fmt::print("SRSLOG Frontend Latency Benchmark - logging with {} thread{}\n" + "All values in nanoseconds\n" + "Percentiles: | 50th | 75th | 90th | 99th | 99.9th | Worst |\n" + " |{:6}|{:6}|{:6}|{:6}|{:8}|{:7}|\n" + "Context switches: {} in {} of generated entries\n\n", + num_threads, + (num_threads > 1) ? "s" : "", + results[static_cast(results.size() * 0.5)], + results[static_cast(results.size() * 0.75)], + results[static_cast(results.size() * 0.9)], + results[static_cast(results.size() * 0.99)], + results[static_cast(results.size() * 0.999)], + results.back(), + ctx_counter, + num_threads * num_iterations * num_entries_per_iter); +} + +int main() +{ + for (auto n : {1, 2, 4}) { + benchmark(n); + } + + return 0; +} diff --git a/lib/test/srslog/event_trace_test.cpp b/lib/test/srslog/event_trace_test.cpp index d55648e08..df5b0c16a 100644 --- a/lib/test/srslog/event_trace_test.cpp +++ b/lib/test/srslog/event_trace_test.cpp @@ -25,7 +25,7 @@ namespace { class backend_spy : public detail::log_backend { public: - void start() override {} + void start(srslog::backend_priority priority) override {} bool push(detail::log_entry&& entry) override { diff --git a/lib/test/srslog/log_channel_test.cpp b/lib/test/srslog/log_channel_test.cpp index af231903b..c5674b4e8 100644 --- a/lib/test/srslog/log_channel_test.cpp +++ b/lib/test/srslog/log_channel_test.cpp @@ -60,7 +60,7 @@ namespace { class backend_spy : public detail::log_backend { public: - void start() override {} + void start(srslog::backend_priority priority) override {} bool push(detail::log_entry&& entry) override { diff --git a/lib/test/srslog/test_dummies.h b/lib/test/srslog/test_dummies.h index 412f1e42b..9f546219b 100644 --- a/lib/test/srslog/test_dummies.h +++ b/lib/test/srslog/test_dummies.h @@ -14,6 +14,7 @@ #define TEST_DUMMIES #include "srsran/srslog/detail/log_backend.h" +#include "srsran/srslog/shared_types.h" #include "srsran/srslog/sink.h" namespace test_dummies { @@ -67,7 +68,7 @@ public: class backend_dummy : public srslog::detail::log_backend { public: - void start() override {} + void start(srslog::backend_priority priority) override {} bool push(srslog::detail::log_entry&& entry) override { return true; } From 243ad05dfa52c23cae9558c31b428ef8d7d4792b Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Wed, 21 Apr 2021 14:06:27 +0200 Subject: [PATCH 19/50] Added on the fly sequence apply in float --- lib/include/srsran/phy/common/sequence.h | 2 + lib/src/phy/common/sequence.c | 105 ++++++++++++----------- 2 files changed, 58 insertions(+), 49 deletions(-) diff --git a/lib/include/srsran/phy/common/sequence.h b/lib/include/srsran/phy/common/sequence.h index ee01f446d..6d8bfeaf9 100644 --- a/lib/include/srsran/phy/common/sequence.h +++ b/lib/include/srsran/phy/common/sequence.h @@ -36,6 +36,8 @@ SRSRAN_API void srsran_sequence_state_init(srsran_sequence_state_t* s, uint32_t SRSRAN_API void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* out, uint32_t length); +SRSRAN_API void srsran_sequence_state_apply_f(srsran_sequence_state_t* s, const float* in, float* out, uint32_t length); + SRSRAN_API void srsran_sequence_state_advance(srsran_sequence_state_t* s, uint32_t length); typedef struct SRSRAN_API { diff --git a/lib/src/phy/common/sequence.c b/lib/src/phy/common/sequence.c index 1216564e0..2ec871398 100644 --- a/lib/src/phy/common/sequence.c +++ b/lib/src/phy/common/sequence.c @@ -264,6 +264,59 @@ void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* } } +void srsran_sequence_state_apply_f(srsran_sequence_state_t* s, const float* in, float* out, uint32_t length) +{ + uint32_t i = 0; + const float xor [2] = {+0.0F, -0.0F}; + + if (length >= SEQUENCE_PAR_BITS) { + for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { + uint32_t c = (uint32_t)(s->x1 ^ s->x2); + + uint32_t j = 0; +#ifdef LV_HAVE_SSE + for (; j < SEQUENCE_PAR_BITS - 3; j += 4) { + // Preloads bits of interest in the 4 LSB + __m128i mask = _mm_set1_epi32(c >> j); + + // Masks each bit + mask = _mm_and_si128(mask, _mm_setr_epi32(1, 2, 4, 8)); + + // Get non zero mask + mask = _mm_cmpgt_epi32(mask, _mm_set1_epi32(0)); + + // And with MSB + mask = _mm_and_si128(mask, (__m128i)_mm_set1_ps(-0.0F)); + + // Load input + __m128 v = _mm_load_ps(in + i + j); + + // Loads input and perform sign XOR + v = _mm_xor_ps((__m128)mask, v); + + _mm_storeu_ps(out + i + j, v); + } +#endif + // Finish the parallel bits with generic code + for (; j < SEQUENCE_PAR_BITS; j++) { + *((uint32_t*)&out[i + j]) = *((uint32_t*)&in[i + j]) ^ *((uint32_t*)&xor[(c >> j) & 1U]); + } + + // Step sequences + s->x1 = sequence_gen_LTE_pr_memless_step_par_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_par_x2(s->x2); + } + } + + for (; i < length; i++) { + *((uint32_t*)&out[i]) = *((uint32_t*)&in[i]) ^ *((uint32_t*)&xor[(s->x1 ^ s->x2) & 1U]); + + // Step sequences + s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_x2(s->x2); + } +} + void srsran_sequence_state_advance(srsran_sequence_state_t* s, uint32_t length) { uint32_t i = 0; @@ -430,56 +483,10 @@ void srsran_sequence_free(srsran_sequence_t* q) void srsran_sequence_apply_f(const float* in, float* out, uint32_t length, uint32_t seed) { - uint32_t x1 = sequence_x1_init; // X1 initial state is fix - uint32_t x2 = sequence_get_x2_init(seed); // loads x2 initial state - - uint32_t i = 0; - - if (length >= SEQUENCE_PAR_BITS) { - for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { - uint32_t c = (uint32_t)(x1 ^ x2); - - uint32_t j = 0; -#ifdef LV_HAVE_SSE - for (; j < SEQUENCE_PAR_BITS - 3; j += 4) { - // Preloads bits of interest in the 4 LSB - __m128i mask = _mm_set1_epi32(c >> j); - - // Masks each bit - mask = _mm_and_si128(mask, _mm_setr_epi32(1, 2, 4, 8)); - - // Get non zero mask - mask = _mm_cmpgt_epi32(mask, _mm_set1_epi32(0)); - - // And with MSB - mask = _mm_and_si128(mask, (__m128i)_mm_set1_ps(-0.0F)); + srsran_sequence_state_t seq = {}; + srsran_sequence_state_init(&seq, seed); - // Load input - __m128 v = _mm_loadu_ps(in + i + j); - - // Loads input and perform sign XOR - v = _mm_xor_ps((__m128)mask, v); - - _mm_storeu_ps(out + i + j, v); - } -#endif - for (; j < SEQUENCE_PAR_BITS; j++) { - ((uint32_t*)out)[i + j] = ((uint32_t*)in)[i] ^ (((c >> j) & 1U) << 31U); - } - - // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_par_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_par_x2(x2); - } - } - - for (; i < length; i++) { - ((uint32_t*)out)[i] = ((uint32_t*)in)[i] ^ (((x1 ^ x2) & 1U) << 31U); - - // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_x2(x2); - } + srsran_sequence_state_apply_f(&seq, in, out, length); } void srsran_sequence_apply_s(const int16_t* in, int16_t* out, uint32_t length, uint32_t seed) From 3b919b4f6ce5c7c8048ee815822e20073f47aa1e Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Wed, 21 Apr 2021 14:07:13 +0200 Subject: [PATCH 20/50] Minor aestheics change --- lib/src/phy/common/phy_common_nr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/phy/common/phy_common_nr.c b/lib/src/phy/common/phy_common_nr.c index 1f88ba9bc..fd10c134d 100644 --- a/lib/src/phy/common/phy_common_nr.c +++ b/lib/src/phy/common/phy_common_nr.c @@ -152,7 +152,7 @@ uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb) return 0; } -float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology) +float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, srsran_subcarrier_spacing_t scs) { // l0 must be smaller than l1 if (l0 >= l1) { @@ -163,7 +163,7 @@ float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology) uint32_t count = l1 - l0; // Compute at what symbol there is a longer CP - uint32_t cp_boundary = 7U << numerology; + uint32_t cp_boundary = 7U << (uint32_t)scs; // Select whether extra CP shall be added uint32_t extra_cp = 0; @@ -175,7 +175,7 @@ float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology) uint32_t N = (2048 + 144) * count + extra_cp; // Return symbol distance in microseconds - return (N << numerology) * SRSRAN_LTE_TS; + return (N << (uint32_t)scs) * SRSRAN_LTE_TS; } bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx) From 751b6858b3cc1af710ece0cbd55e588fd1159789 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Wed, 21 Apr 2021 14:07:50 +0200 Subject: [PATCH 21/50] Added CSI-RS resource set measurements --- lib/include/srsran/phy/ch_estimation/csi_rs.h | 144 ++++- lib/src/phy/ch_estimation/csi_rs.c | 560 ++++++++++++++++-- lib/src/phy/ch_estimation/test/csi_rs_test.c | 335 +++++++++-- 3 files changed, 910 insertions(+), 129 deletions(-) diff --git a/lib/include/srsran/phy/ch_estimation/csi_rs.h b/lib/include/srsran/phy/ch_estimation/csi_rs.h index 7f7e659c9..1dae8fb52 100644 --- a/lib/include/srsran/phy/ch_estimation/csi_rs.h +++ b/lib/include/srsran/phy/ch_estimation/csi_rs.h @@ -40,18 +40,24 @@ #define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_OTHER 6 /** - * @brief Measurement structure + * @brief Describes a measurement for NZP-CSI-RS + * @note Used for fine tracking RSRP, SNR, CFO, SFO, and so on + * @note srsran_csi_measurements_t is used for CSI report generation */ typedef struct SRSRAN_API { - float rsrp; - float rsrp_dB; - float epre; - float epre_dB; - float n0; - float n0_dB; - float snr_dB; - uint32_t nof_re; -} srsran_csi_rs_measure_t; + float rsrp; ///< Linear scale RSRP + float rsrp_dB; ///< Logarithm scale RSRP relative to full-scale + float epre; ///< Linear scale EPRE + float epre_dB; ///< Logarithm scale EPRE relative to full-scale + float n0; ///< Linear noise level + float n0_dB; ///< Logarithm scale noise level relative to full-scale + float snr_dB; ///< Signal to noise ratio in decibels + float cfo_hz; ///< Carrier frequency offset in Hz. Only set if more than 2 symbols are available in a TRS set + float cfo_hz_max; ///< Maximum CFO in Hz that can be measured. It is set to 0 if CFO cannot be estimated + float delay_us; ///< Average measured delay in microseconds + uint32_t nof_re; ///< Number of available RE for the measurement, it can be used for weighting among different + ///< measurements +} srsran_csi_rs_nzp_measure_t; /** * @brief Calculates if the given periodicity implies a CSI-RS transmission in the given slot @@ -67,7 +73,7 @@ SRSRAN_API bool srsran_csi_rs_send(const srsran_csi_rs_period_and_offset_t* peri * @brief Adds to a RE pattern list the RE used in a CSI-RS resource for all CDM grops. This is intended for generating * reserved RE pattern for PDSCH transmission. * @param carrier Provides carrier configuration - * @param resource Provides a CSI-RS resource + * @param resource Provides any CSI-RS resource mapping * @param nof_resources Provides the number of ZP-CSI-RS resources * @param l Symbol index in the slot * @param[out] rvd_mask Provides the reserved mask @@ -77,17 +83,121 @@ SRSRAN_API int srsran_csi_rs_append_resource_to_pattern(const srsran_carrier_nr_ const srsran_csi_rs_resource_mapping_t* resource, srsran_re_pattern_list_t* re_pattern_list); -SRSRAN_API int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_resource_t* resource, - cf_t* grid); +/** + * @brief Puts in the provided resource grid NZP-CSI-RS signals given by a NZP-CSI-RS resource + * + * @note it does not check if the provided slot matches with the periodicity of the provided NZP-CSI-RS resource + * + * @param carrier Provides carrier configuration + * @param slot_cfg Provides current slot configuration + * @param resource Provides a NZP-CSI-RS resource + * @param[out] grid Resource grid + * @return SRSLTE_SUCCESS if the arguments and the resource are valid. SRSLTE_ERROR code otherwise. + */ +SRSRAN_API int srsran_csi_rs_nzp_put_resource(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + cf_t* grid); +/** + * @brief Puts in the provided resource grid NZP-CSI-RS signals given by a NZP-CSI-RS resource set if their periodicity + * configuration matches with the provided slot + * + * @param carrier Provides carrier configuration + * @param slot_cfg Provides current slot configuration + * @param set Provides a NZP-CSI-RS resource set + * @param[out] grid Resource grid + * @return The number of NZP-CSI-RS resources that have been scheduled for this slot if the arguments and the resource + * are valid. SRSLTE_ERROR code otherwise. + */ +SRSRAN_API int srsran_csi_rs_nzp_put_set(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + cf_t* grid); SRSRAN_API int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, const srsran_slot_cfg_t* slot_cfg, const srsran_csi_rs_nzp_resource_t* resource, const cf_t* grid, - srsran_csi_rs_measure_t* measure); + srsran_csi_rs_nzp_measure_t* measure); -SRSRAN_API uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_measure_t* measure, char* str, uint32_t str_len); +/** + * @brief Performs measurements of NZP-CSI-RS resource set flagged as TRS + * + * @attention It expects: + * - The NZP-CSI-RS resource set shall be flagged as TRS; and + * - at least a pair of active NZP-CSR-RS per measurement opportunity with their first transmission symbol in ascending + * order. + * + * @note It performs the following wideband measurements: + * - RSRP (linear and dB), + * - EPRE (linear and dB), + * - Noise (linear and dB), + * - SNR (dB), + * - average delay (microseconds), and + * - CFO (Hz) + * + * @note It is intended for fine tracking of synchronization error (average delay) and carrier frequency error + * + * @param carrier Provides carrier configuration + * @param slot_cfg Provides current slot + * @param set Provides NZP-CSI-RS resource + * @param grid Resource grid + * @param measure Provides measurement + * @return The number of NZP-CSI-RS resources scheduled for this TTI if the configuration is right, SRSLTE_ERROR code if + * the configuration is invalid + */ +SRSRAN_API int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + srsran_csi_rs_nzp_measure_t* measure); + +SRSRAN_API uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_nzp_measure_t* measure, char* str, uint32_t str_len); + +/** + * @brief Performs channel measurements of NZP-CSI-RS resource set for CSI reports + * + * @note It performs the following wideband measurements: + * - RSRP (dB), + * - EPRE (dB), + * - SNR (dB), + * + * @note It is intended for generating CSI wideband measurements that are used for generating CSI reporting + * + * @param carrier Provides carrier configuration + * @param slot_cfg Provides current slot + * @param set Provides NZP-CSI-RS resource + * @param grid Resource grid + * @param measure Provides CSI measurement + * @return The number of NZP-CSI-RS resources scheduled for this slot if the configuration is right, SRSLTE_ERROR code + * if the configuration is invalid + */ +SRSRAN_API int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + srsran_csi_measurements_t* measure); + +/** + * @brief Performs measurements of ZP-CSI-RS resource set for CSI reports + * + * @note It performs the following wideband measurememnts: + * - EPRE (dB) + * + * @note It is intended for measuring interference + * + * @param carrier Provides carrier configuration + * @param slot_cfg Provides current slot + * @param set Provides ZP-CSI-RS resource + * @param grid Resource grid + * @param measure Provides CSI measurement + * @return The number of ZP-CSI-RS resources scheduled for this slot if the configuration is right, SRSLTE_ERROR code if + * the configuration is invalid + */ +SRSRAN_API int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_zp_set_t* set, + const cf_t* grid, + srsran_csi_measurements_t* measure); #endif // SRSRAN_CSI_RS_H_ diff --git a/lib/src/phy/ch_estimation/csi_rs.c b/lib/src/phy/ch_estimation/csi_rs.c index 767c6a799..df6cdc166 100644 --- a/lib/src/phy/ch_estimation/csi_rs.c +++ b/lib/src/phy/ch_estimation/csi_rs.c @@ -56,7 +56,7 @@ static int csi_rs_location_f(const srsran_csi_rs_resource_mapping_t* resource, u } if (count == i) { - return j * mul; + return (int)(j * mul); } } @@ -177,7 +177,7 @@ static uint32_t csi_rs_cinit(const srsran_carrier_nr_t* carrier, uint32_t n = SRSRAN_SLOT_NR_MOD(carrier->scs, slot_cfg->idx); uint32_t n_id = resource->scrambling_id; - return ((SRSRAN_NSYMB_PER_SLOT_NR * n + l + 1UL) * (2UL * n_id) << 10UL) + n_id; + return SRSRAN_SEQUENCE_MOD(((SRSRAN_NSYMB_PER_SLOT_NR * n + l + 1UL) * (2UL * n_id) << 10UL) + n_id); } bool srsran_csi_rs_send(const srsran_csi_rs_period_and_offset_t* periodicity, const srsran_slot_cfg_t* slot_cfg) @@ -238,7 +238,6 @@ uint32_t csi_rs_count(srsran_csi_rs_density_t density, uint32_t nprb) case srsran_csi_rs_resource_mapping_density_three: return nprb * 3; case srsran_csi_rs_resource_mapping_density_dot5_even: - return nprb / 2; case srsran_csi_rs_resource_mapping_density_dot5_odd: return nprb / 2; case srsran_csi_rs_resource_mapping_density_one: @@ -339,12 +338,13 @@ int srsran_csi_rs_append_resource_to_pattern(const srsran_carrier_nr_t* return SRSRAN_SUCCESS; } -int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_resource_t* resource, - cf_t* grid) +int srsran_csi_rs_nzp_put_resource(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + cf_t* grid) { - if (carrier == NULL || resource == NULL || grid == NULL) { + // Verify inputs + if (carrier == NULL || slot_cfg == NULL || resource == NULL || grid == NULL) { return SRSRAN_ERROR; } @@ -412,25 +412,76 @@ int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, return SRSRAN_SUCCESS; } -int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_resource_t* resource, - const cf_t* grid, - srsran_csi_rs_measure_t* measure) +int srsran_csi_rs_nzp_put_set(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + cf_t* grid) { - if (carrier == NULL || resource == NULL || grid == NULL) { + // Verify inputs + if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL) { return SRSRAN_ERROR; } + uint32_t count = 0; + + // Iterate all resources in set + for (uint32_t i = 0; i < set->count; i++) { + // Skip resource + if (!srsran_csi_rs_send(&set->data[i].periodicity, slot_cfg)) { + continue; + } + + // Put resource + if (srsran_csi_rs_nzp_put_resource(carrier, slot_cfg, &set->data[i], grid) < SRSRAN_SUCCESS) { + ERROR("Error putting NZP-CSI-RS resource"); + return SRSRAN_ERROR; + } + count++; + } + + return (int)count; +} + +/** + * @brief Internal NZP-CSI-RS measurement structure + */ +typedef struct { + uint32_t cri; ///< CSI-RS resource identifier + uint32_t l0; ///< First OFDM symbol carrying CSI-RS + float epre; ///< Linear EPRE + cf_t corr; ///< Correlation + float delay_us; ///< Estimated average delay + uint32_t nof_re; ///< Total number of resource elements +} csi_rs_nzp_resource_measure_t; + +static int csi_rs_nzp_measure_resource(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + const cf_t* grid, + csi_rs_nzp_resource_measure_t* measure) +{ // Force CDM group to 0 uint32_t j = 0; + // Get subcarrier indexes uint32_t k_list[CSI_RS_MAX_SUBC_PRB]; int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, j, k_list); if (nof_k <= 0) { return SRSRAN_ERROR; } + // Calculate average CSI-RS RE stride + float avg_k_stride = (float)((k_list[0] + SRSRAN_NRE) - k_list[nof_k - 1]); + for (uint32_t i = 1; i < (uint32_t)nof_k; i++) { + avg_k_stride += (float)(k_list[i] - k_list[i - 1]); + } + avg_k_stride /= (float)nof_k; + if (!isnormal(avg_k_stride)) { + ERROR("Invalid avg_k_stride"); + return SRSRAN_ERROR; + } + + // Get symbol indexes uint32_t l_list[CSI_RS_MAX_SYMBOLS_SLOT]; int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, j, l_list); if (nof_l <= 0) { @@ -442,11 +493,18 @@ int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, uint32_t rb_end = csi_rs_rb_end(carrier, &resource->resource_mapping); uint32_t rb_stride = csi_rs_rb_stride(&resource->resource_mapping); + // Calculate ideal number of RE per symbol + uint32_t nof_re = csi_rs_count(resource->resource_mapping.density, rb_end - rb_begin); + // Accumulators - float epre_acc = 0.0f; - cf_t rsrp_acc = 0.0f; - uint32_t count = 0; + float epre_acc = 0.0f; + cf_t corr_acc = 0.0f; + float delay_acc = 0.0f; + // Initialise measurement + SRSRAN_MEM_ZERO(measure, csi_rs_nzp_resource_measure_t, 1); + + // Iterate time symbols for (int l_idx = 0; l_idx < nof_l; l_idx++) { // Get symbol index uint32_t l = l_list[l_idx]; @@ -459,61 +517,459 @@ int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, // Skip unallocated RB srsran_sequence_state_advance(&sequence_state, 2 * csi_rs_count(resource->resource_mapping.density, rb_begin)); - // Temporal R sequence - cf_t r[64]; - uint32_t r_idx = 64; + // Temporal Least Square Estimates + cf_t lse[CSI_RS_MAX_SUBC_PRB * SRSRAN_MAX_PRB_NR]; + uint32_t count_re = 0; - // Iterate over frequency domain + // Extract RE for (uint32_t n = rb_begin; n < rb_end; n += rb_stride) { for (uint32_t k_idx = 0; k_idx < nof_k; k_idx++) { // Calculate sub-carrier index k uint32_t k = SRSRAN_NRE * n + k_list[k_idx]; - // Do we need more r? - if (r_idx >= 64) { - // ... Generate a bunch of it! - srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)r, 64 * 2); - r_idx = 0; - } - - // Take CSI-RS from grid and measure - cf_t tmp = grid[l * SRSRAN_NRE * carrier->nof_prb + k] * conjf(r[r_idx++]); - rsrp_acc += tmp; - epre_acc += __real__ tmp * __real__ tmp + __imag__ tmp * __imag__ tmp; - count++; + lse[count_re++] = grid[l * SRSRAN_NRE * carrier->nof_prb + k]; } } + + // Verify RE count matches the expected number of RE + if (count_re == 0 || count_re != nof_re) { + ERROR("Unmatched number of RE (%d != %d)", count_re, nof_re); + return SRSRAN_ERROR; + } + + // Compute LSE + srsran_sequence_state_apply_f(&sequence_state, (float*)lse, (float*)lse, 2 * count_re); + + // Compute EPRE + epre_acc += srsran_vec_avg_power_cf(lse, count_re); + + // Compute correlation + corr_acc += srsran_vec_acc_cc(lse, count_re) / (float)count_re; + + // Compute average delay + delay_acc += srsran_vec_estimate_frequency(lse, count_re); } - if (count) { - measure->epre = epre_acc / (float)count; - rsrp_acc /= (float)count; - measure->rsrp = (__real__ rsrp_acc * __real__ rsrp_acc + __imag__ rsrp_acc * __imag__ rsrp_acc); - if (measure->epre > measure->rsrp) { - measure->n0 = measure->epre - measure->rsrp; - } else { - measure->n0 = 0.0f; + // Set measure fields + measure->cri = resource->id; + measure->l0 = l_list[0]; + measure->epre = epre_acc / (float)nof_l; + measure->corr = corr_acc / (float)nof_l; + measure->delay_us = 1e6f * delay_acc / ((float)nof_l * SRSRAN_SUBC_SPACING_NR(carrier->scs)); + measure->nof_re = nof_l * nof_re; + + return SRSRAN_SUCCESS; +} + +static int csi_rs_nzp_measure_set(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + csi_rs_nzp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]) +{ + uint32_t count = 0; + + // Iterate all resources in set + for (uint32_t i = 0; i < set->count; i++) { + // Skip resource + if (!srsran_csi_rs_send(&set->data[i].periodicity, slot_cfg)) { + continue; + } + + // Perform measurement + if (csi_rs_nzp_measure_resource(carrier, slot_cfg, &set->data[i], grid, &measurements[count]) < SRSRAN_SUCCESS) { + ERROR("Error measuring NZP-CSI-RS resource"); + return SRSRAN_ERROR; } + count++; } + return count; +} + +int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + const cf_t* grid, + srsran_csi_rs_nzp_measure_t* measure) +{ + if (carrier == NULL || slot_cfg == NULL || resource == NULL || grid == NULL || measure == NULL) { + return SRSRAN_ERROR; + } + + csi_rs_nzp_resource_measure_t m = {}; + if (csi_rs_nzp_measure_resource(carrier, slot_cfg, resource, grid, &m) < SRSRAN_SUCCESS) { + ERROR("Error measuring NZP-CSI-RS resource"); + return SRSRAN_ERROR; + } + + // Copy measurements + measure->epre = m.epre; + measure->rsrp = (__real__ m.corr * __real__ m.corr + __imag__ m.corr * __imag__ m.corr); + measure->delay_us = m.delay_us; + measure->nof_re = m.nof_re; + + // Estimate noise from EPRE and RSPR + if (measure->epre > measure->rsrp) { + measure->n0 = measure->epre - measure->rsrp; + } else { + measure->n0 = 0.0f; + } + + // CFo cannot be estimated with a single resource + measure->cfo_hz = 0.0f; + measure->cfo_hz_max = 0.0f; + + // Calculate logarithmic measurements measure->rsrp_dB = srsran_convert_power_to_dB(measure->rsrp); measure->epre_dB = srsran_convert_power_to_dB(measure->epre); measure->n0_dB = srsran_convert_power_to_dB(measure->n0); measure->snr_dB = measure->rsrp_dB - measure->n0_dB; - measure->nof_re = count; return SRSRAN_SUCCESS; } -uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_measure_t* measure, char* str, uint32_t str_len) +int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + srsran_csi_rs_nzp_measure_t* measure) +{ + // Verify inputs + if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL || measure == NULL) { + return SRSRAN_ERROR; + } + + // Verify it is a TRS set + if (!set->trs_info) { + ERROR("The set is not configured as TRS"); + return SRSRAN_ERROR; + } + + // Perform Measurements + csi_rs_nzp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]; + int ret = csi_rs_nzp_measure_set(carrier, slot_cfg, set, grid, measurements); + if (ret < SRSRAN_SUCCESS) { + ERROR("Error performing measurements"); + } + uint32_t count = (uint32_t)ret; + + // No NZP-CSI-RS has been scheduled for this slot + if (count == 0) { + return 0; + } + + // Make sure at least 2 measurements are scheduled + if (count < 2) { + ERROR("Not enough NZP-CSI-RS (%d) have been scheduled for this slot", count); + return SRSRAN_ERROR; + } + + // Make sure initial simbols are in ascending order + for (uint32_t i = 1; i < count; i++) { + if (measurements[i].l0 <= measurements[i - 1].l0) { + ERROR("NZP-CSI-RS are not in ascending order (%d <= %d)", measurements[i].l0, measurements[i - 1].l0); + return SRSRAN_ERROR; + } + } + + // Average measurements + float epre_sum = 0.0f; + float rsrp_sum = 0.0f; + float delay_sum = 0.0f; + uint32_t nof_re = 0; + for (uint32_t i = 0; i < count; i++) { + epre_sum += measurements[i].epre / (float)count; + rsrp_sum += (__real__ measurements[i].corr * __real__ measurements[i].corr + + __imag__ measurements[i].corr * __imag__ measurements[i].corr) / + (float)count; + delay_sum += measurements[i].delay_us / (float)count; + nof_re += measurements[i].nof_re; + } + + // Compute CFO + float cfo_sum = 0.0f; + float cfo_max = 0.0f; + for (uint32_t i = 1; i < count; i++) { + float time_diff = srsran_symbol_distance_s(measurements[i - 1].l0, measurements[i].l0, carrier->scs); + float phase_diff = cargf(measurements[i].corr * conjf(measurements[i - 1].corr)); + float cfo_max_temp = 0.0f; + + // Avoid zero division + if (isnormal(time_diff)) { + // Calculate maximum CFO from this pair of symbols + cfo_max_temp = 1.0f / time_diff; + + // Calculate the actual CFO of this pair of symbols + cfo_sum += phase_diff / (2.0f * M_PI * time_diff * (count - 1)); + } + + // Select the lowest CFO + cfo_max = SRSRAN_MIN(cfo_max_temp, cfo_max); + } + + // Copy measurements + measure->epre = epre_sum; + measure->rsrp = rsrp_sum; + measure->delay_us = delay_sum; + measure->cfo_hz = cfo_sum; + measure->cfo_hz_max = cfo_max; + measure->nof_re = nof_re; + + // Estimate noise from EPRE and RSPR + if (measure->epre > measure->rsrp) { + measure->n0 = measure->epre - measure->rsrp; + } else { + measure->n0 = 0.0f; + } + + // Calculate logarithmic measurements + measure->rsrp_dB = srsran_convert_power_to_dB(measure->rsrp); + measure->epre_dB = srsran_convert_power_to_dB(measure->epre); + measure->n0_dB = srsran_convert_power_to_dB(measure->n0); + measure->snr_dB = measure->rsrp_dB - measure->n0_dB; + + return count; +} + +int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + srsran_csi_measurements_t* measure) { - return srsran_print_check(str, - str_len, - 0, - "rsrp=%+.1f, epre=%+.1f, n0=%+.1f, snr=%+.1f, nof_re=%d", - measure->rsrp_dB, - measure->epre_dB, - measure->n0_dB, - measure->snr_dB, - measure->nof_re); + // Verify inputs + if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL || measure == NULL) { + return SRSRAN_ERROR; + } + + // Perform Measurements + csi_rs_nzp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]; + int ret = csi_rs_nzp_measure_set(carrier, slot_cfg, set, grid, measurements); + if (ret < SRSRAN_SUCCESS) { + ERROR("Error performing measurements"); + } + uint32_t count = (uint32_t)ret; + + // No NZP-CSI-RS has been scheduled for this slot + if (count == 0) { + return 0; + } + + // Average measurements + float epre_sum = 0.0f; + float rsrp_sum = 0.0f; + for (uint32_t i = 0; i < count; i++) { + epre_sum += measurements[i].epre / (float)count; + rsrp_sum += (__real__ measurements[i].corr * __real__ measurements[i].corr + + __imag__ measurements[i].corr * __imag__ measurements[i].corr) / + (float)count; + } + + // Estimate noise from EPRE and RSPR + float n0 = 0.0f; + if (epre_sum > rsrp_sum) { + n0 = epre_sum - rsrp_sum; + } + float n0_db = srsran_convert_power_to_dB(n0); + + // Set measurements + measure->cri = measurements[0].cri; + measure->wideband_rsrp_dBm = srsran_convert_power_to_dB(rsrp_sum); + measure->wideband_epre_dBm = srsran_convert_power_to_dB(epre_sum); + measure->wideband_snr_db = measure->wideband_rsrp_dBm - n0_db; + + // Set other parameters + measure->K_csi_rs = count; + measure->nof_ports = 1; // No other value is currently supported + + // Return the number of active resources for this slot + return count; +} + +/** + * @brief Internal ZP-CSI-RS measurement structure + */ +typedef struct { + uint32_t cri; ///< CSI-RS resource identifier + uint32_t l0; ///< First OFDM symbol carrying CSI-RS + float epre; ///< Linear EPRE + uint32_t nof_re; ///< Total number of resource elements +} csi_rs_zp_resource_measure_t; + +static int csi_rs_zp_measure_resource(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_zp_resource_t* resource, + const cf_t* grid, + csi_rs_zp_resource_measure_t* measure) +{ + // Force CDM group to 0 + uint32_t j = 0; + + // Get subcarrier indexes + uint32_t k_list[CSI_RS_MAX_SUBC_PRB]; + int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, j, k_list); + if (nof_k <= 0) { + return SRSRAN_ERROR; + } + + // Calculate average CSI-RS RE stride + float avg_k_stride = (float)((k_list[0] + SRSRAN_NRE) - k_list[nof_k - 1]); + for (uint32_t i = 1; i < (uint32_t)nof_k; i++) { + avg_k_stride += (float)(k_list[i] - k_list[i - 1]); + } + avg_k_stride /= (float)nof_k; + if (!isnormal(avg_k_stride)) { + ERROR("Invalid avg_k_stride"); + return SRSRAN_ERROR; + } + + // Get symbol indexes + uint32_t l_list[CSI_RS_MAX_SYMBOLS_SLOT]; + int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, j, l_list); + if (nof_l <= 0) { + return SRSRAN_ERROR; + } + + // Calculate Resource Block boundaries + uint32_t rb_begin = csi_rs_rb_begin(carrier, &resource->resource_mapping); + uint32_t rb_end = csi_rs_rb_end(carrier, &resource->resource_mapping); + uint32_t rb_stride = csi_rs_rb_stride(&resource->resource_mapping); + + // Calculate ideal number of RE per symbol + uint32_t nof_re = csi_rs_count(resource->resource_mapping.density, rb_end - rb_begin); + + // Accumulators + float epre_acc = 0.0f; + + // Initialise measurement + SRSRAN_MEM_ZERO(measure, csi_rs_zp_resource_measure_t, 1); + + // Iterate time symbols + for (int l_idx = 0; l_idx < nof_l; l_idx++) { + // Get symbol index + uint32_t l = l_list[l_idx]; + + // Temporal Least Square Estimates + cf_t temp[CSI_RS_MAX_SUBC_PRB * SRSRAN_MAX_PRB_NR]; + uint32_t count_re = 0; + + // Extract RE + for (uint32_t n = rb_begin; n < rb_end; n += rb_stride) { + for (uint32_t k_idx = 0; k_idx < nof_k; k_idx++) { + // Calculate sub-carrier index k + uint32_t k = SRSRAN_NRE * n + k_list[k_idx]; + + temp[count_re++] = grid[l * SRSRAN_NRE * carrier->nof_prb + k]; + } + } + + // Verify RE count matches the expected number of RE + if (count_re == 0 || count_re != nof_re) { + ERROR("Unmatched number of RE (%d != %d)", count_re, nof_re); + return SRSRAN_ERROR; + } + + // Compute EPRE + epre_acc += srsran_vec_avg_power_cf(temp, count_re); + } + + // Set measure fields + measure->cri = resource->id; + measure->l0 = l_list[0]; + measure->epre = epre_acc / (float)nof_l; + measure->nof_re = nof_l * nof_re; + + return SRSRAN_SUCCESS; +} + +static int csi_rs_zp_measure_set(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_zp_set_t* set, + const cf_t* grid, + csi_rs_zp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]) +{ + uint32_t count = 0; + + // Iterate all resources in set + for (uint32_t i = 0; i < set->count; i++) { + // Skip resource + if (!srsran_csi_rs_send(&set->data[i].periodicity, slot_cfg)) { + continue; + } + + // Perform measurement + if (csi_rs_zp_measure_resource(carrier, slot_cfg, &set->data[i], grid, &measurements[count]) < SRSRAN_SUCCESS) { + ERROR("Error measuring NZP-CSI-RS resource"); + return SRSRAN_ERROR; + } + count++; + } + + return count; +} + +int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_zp_set_t* set, + const cf_t* grid, + srsran_csi_measurements_t* measure) +{ + // Verify inputs + if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL || measure == NULL) { + return SRSRAN_ERROR; + } + + // Perform Measurements + csi_rs_zp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]; + int ret = csi_rs_zp_measure_set(carrier, slot_cfg, set, grid, measurements); + if (ret < SRSRAN_SUCCESS) { + ERROR("Error performing measurements"); + } + uint32_t count = (uint32_t)ret; + + // No NZP-CSI-RS has been scheduled for this slot + if (count == 0) { + return 0; + } + + // Average measurements + float epre_sum = 0.0f; + for (uint32_t i = 0; i < count; i++) { + epre_sum += measurements[i].epre / (float)count; + } + + // Set measurements + measure->cri = measurements[0].cri; + measure->wideband_rsrp_dBm = NAN; + measure->wideband_epre_dBm = srsran_convert_power_to_dB(epre_sum); + measure->wideband_snr_db = NAN; + + // Set other parameters + measure->K_csi_rs = count; + measure->nof_ports = 1; // No other value is currently supported + + // Return the number of active resources for this slot + return count; +} + +uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_nzp_measure_t* measure, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + len = srsran_print_check(str, + str_len, + len, + "rsrp=%+.1f epre=%+.1f n0=%+.1f snr=%+.1f delay_us=%+.1f ", + measure->rsrp_dB, + measure->epre_dB, + measure->n0_dB, + measure->snr_dB); + + // Append measured CFO and the maximum CFO that can be measured + if (isnormal(measure->cfo_hz_max)) { + len = srsran_print_check(str, str_len, len, "cfo_hz=%+.1f cfo_hz_max=%+.1f", measure->cfo_hz, measure->cfo_hz_max); + } + + return len; } \ No newline at end of file diff --git a/lib/src/phy/ch_estimation/test/csi_rs_test.c b/lib/src/phy/ch_estimation/test/csi_rs_test.c index 4552f32a1..095b0774e 100644 --- a/lib/src/phy/ch_estimation/test/csi_rs_test.c +++ b/lib/src/phy/ch_estimation/test/csi_rs_test.c @@ -34,15 +34,15 @@ static uint32_t start_rb = UINT32_MAX; static uint32_t nof_rb = UINT32_MAX; static uint32_t first_symbol = UINT32_MAX; -static int test(const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_resource_t* resource, - srsran_channel_awgn_t* awgn, - cf_t* grid) +static int nzp_test_case(const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + srsran_channel_awgn_t* awgn, + cf_t* grid) { - srsran_csi_rs_measure_t measure = {}; + srsran_csi_rs_nzp_measure_t measure = {}; // Put NZP-CSI-RS - TESTASSERT(srsran_csi_rs_nzp_put(&carrier, slot_cfg, resource, grid) == SRSRAN_SUCCESS); + TESTASSERT(srsran_csi_rs_nzp_put_resource(&carrier, slot_cfg, resource, grid) == SRSRAN_SUCCESS); // Configure N0 and add Noise TESTASSERT(srsran_channel_awgn_set_n0(awgn, (float)resource->power_control_offset - snr_dB) == SRSRAN_SUCCESS); @@ -69,6 +69,267 @@ static int test(const srsran_slot_cfg_t* slot_cfg, return SRSRAN_SUCCESS; } +static int nzp_test_brute(srsran_channel_awgn_t* awgn, cf_t* grid) +{ + // Slot configuration + srsran_slot_cfg_t slot_cfg = {}; + + // Initialise NZP-CSI-RS fix parameters, other params are not implemented + srsran_csi_rs_nzp_resource_t resource = {}; + resource.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; + resource.resource_mapping.nof_ports = 1; + + // Row 1 supported only! + uint32_t nof_freq_dom_alloc = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1; + + uint32_t first_symbol_begin = (first_symbol != UINT32_MAX) ? first_symbol : 0; + uint32_t first_symbol_end = (first_symbol != UINT32_MAX) ? first_symbol : 13; + for (resource.resource_mapping.first_symbol_idx = first_symbol_begin; + resource.resource_mapping.first_symbol_idx <= first_symbol_end; + resource.resource_mapping.first_symbol_idx++) { + // Iterate over possible power control offset + float power_control_offset_begin = isnormal(power_control_offset) ? power_control_offset : -8.0f; + float power_control_offset_end = isnormal(power_control_offset) ? power_control_offset : 15.0f; + for (resource.power_control_offset = power_control_offset_begin; + resource.power_control_offset <= power_control_offset_end; + resource.power_control_offset += 1.0f) { + // Iterate over all possible starting number of PRB + uint32_t start_rb_begin = (start_rb != UINT32_MAX) ? start_rb : 0; + uint32_t start_rb_end = (start_rb != UINT32_MAX) ? start_rb : carrier.nof_prb - 24; + for (resource.resource_mapping.freq_band.start_rb = start_rb_begin; + resource.resource_mapping.freq_band.start_rb <= start_rb_end; + resource.resource_mapping.freq_band.start_rb += 4) { + // Iterate over all possible number of PRB + uint32_t nof_rb_begin = (nof_rb != UINT32_MAX) ? nof_rb : 24; + uint32_t nof_rb_end = + (nof_rb != UINT32_MAX) ? nof_rb : (carrier.nof_prb - resource.resource_mapping.freq_band.start_rb); + for (resource.resource_mapping.freq_band.nof_rb = nof_rb_begin; + resource.resource_mapping.freq_band.nof_rb <= nof_rb_end; + resource.resource_mapping.freq_band.nof_rb += 4) { + // Iterate for all slot numbers + for (slot_cfg.idx = 0; slot_cfg.idx < SRSRAN_NSLOTS_PER_FRAME_NR(carrier.scs); slot_cfg.idx++) { + // Steer Frequency allocation + for (uint32_t freq_dom_alloc = 0; freq_dom_alloc < nof_freq_dom_alloc; freq_dom_alloc++) { + for (uint32_t i = 0; i < nof_freq_dom_alloc; i++) { + resource.resource_mapping.frequency_domain_alloc[i] = i == freq_dom_alloc; + } + + // Call actual test + TESTASSERT(nzp_test_case(&slot_cfg, &resource, awgn, grid) == SRSRAN_SUCCESS); + } + } + } + } + } + } + + return SRSRAN_SUCCESS; +} + +static int nzp_test_trs(srsran_channel_awgn_t* awgn, cf_t* grid) +{ + // Slot configuration + srsran_slot_cfg_t slot_cfg = {}; + + // Item 1 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 1 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 4 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 11 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t resource1 = {}; + resource1.id = 1; + resource1.resource_mapping.frequency_domain_alloc[0] = 0; + resource1.resource_mapping.frequency_domain_alloc[1] = 0; + resource1.resource_mapping.frequency_domain_alloc[2] = 0; + resource1.resource_mapping.frequency_domain_alloc[3] = 1; + resource1.resource_mapping.nof_ports = 1; + resource1.resource_mapping.first_symbol_idx = 4; + resource1.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource1.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource1.resource_mapping.freq_band.start_rb = 0; + resource1.resource_mapping.freq_band.nof_rb = carrier.nof_prb; + resource1.power_control_offset = 0; + resource1.power_control_offset_ss = 0; + resource1.periodicity.period = 40; + resource1.periodicity.offset = 11; + + // Item 2 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 2 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 8 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 11 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t resource2 = {}; + resource2.id = 1; + resource2.resource_mapping.frequency_domain_alloc[0] = 0; + resource2.resource_mapping.frequency_domain_alloc[1] = 0; + resource2.resource_mapping.frequency_domain_alloc[2] = 0; + resource2.resource_mapping.frequency_domain_alloc[3] = 1; + resource2.resource_mapping.nof_ports = 1; + resource2.resource_mapping.first_symbol_idx = 8; + resource2.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource2.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource2.resource_mapping.freq_band.start_rb = 0; + resource2.resource_mapping.freq_band.nof_rb = carrier.nof_prb; + resource2.power_control_offset = 0; + resource2.power_control_offset_ss = 0; + resource2.periodicity.period = 40; + resource2.periodicity.offset = 11; + + // Item 3 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 3 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 4 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 12 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t resource3 = {}; + resource3.id = 1; + resource3.resource_mapping.frequency_domain_alloc[0] = 0; + resource3.resource_mapping.frequency_domain_alloc[1] = 0; + resource3.resource_mapping.frequency_domain_alloc[2] = 0; + resource3.resource_mapping.frequency_domain_alloc[3] = 1; + resource3.resource_mapping.nof_ports = 1; + resource3.resource_mapping.first_symbol_idx = 4; + resource3.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource3.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource3.resource_mapping.freq_band.start_rb = 0; + resource3.resource_mapping.freq_band.nof_rb = carrier.nof_prb; + resource3.power_control_offset = 0; + resource3.power_control_offset_ss = 0; + resource3.periodicity.period = 40; + resource3.periodicity.offset = 12; + + // Item 4 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 4 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 8 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 12 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t resource4 = {}; + resource4.id = 1; + resource4.resource_mapping.frequency_domain_alloc[0] = 0; + resource4.resource_mapping.frequency_domain_alloc[1] = 0; + resource4.resource_mapping.frequency_domain_alloc[2] = 0; + resource4.resource_mapping.frequency_domain_alloc[3] = 1; + resource4.resource_mapping.nof_ports = 1; + resource4.resource_mapping.first_symbol_idx = 8; + resource4.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource4.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource4.resource_mapping.freq_band.start_rb = 0; + resource4.resource_mapping.freq_band.nof_rb = carrier.nof_prb; + resource4.power_control_offset = 0; + resource4.power_control_offset_ss = 0; + resource4.periodicity.period = 40; + resource4.periodicity.offset = 12; + + // NZP-CSI-RS-ResourceSet + // nzp-CSI-ResourceSetId: 1 + // nzp-CSI-RS-Resources: 4 items + // Item 0 + // NZP-CSI-RS-ResourceId: 1 + // Item 1 + // NZP-CSI-RS-ResourceId: 2 + // Item 2 + // NZP-CSI-RS-ResourceId: 3 + // Item 3 + // NZP-CSI-RS-ResourceId: 4 + // trs-Info: true (0) + srsran_csi_rs_nzp_set_t set = {}; + set.data[set.count++] = resource1; + set.data[set.count++] = resource2; + set.data[set.count++] = resource3; + set.data[set.count++] = resource4; + set.trs_info = true; + + for (slot_cfg.idx = 0; slot_cfg.idx < resource1.periodicity.period; slot_cfg.idx++) { + // Put NZP-CSI-RS TRS signals + int ret = srsran_csi_rs_nzp_put_set(&carrier, &slot_cfg, &set, grid); + + // Check return + if (slot_cfg.idx == 11 || slot_cfg.idx == 12) { + TESTASSERT(ret == 2); + } else { + TESTASSERT(ret == 0); + } + + // Configure N0 and add Noise + TESTASSERT(srsran_channel_awgn_set_n0(awgn, (float)set.data[0].power_control_offset - snr_dB) == SRSRAN_SUCCESS); + srsran_channel_awgn_run_c(awgn, grid, grid, SRSRAN_SLOT_LEN_RE_NR(carrier.nof_prb)); + + // Measure + srsran_csi_rs_nzp_measure_t measure = {}; + ret = srsran_csi_rs_nzp_measure_trs(&carrier, &slot_cfg, &set, grid, &measure); + + // Check return and assert measurement + if (slot_cfg.idx == 11 || slot_cfg.idx == 12) { + TESTASSERT(ret == 2); + } else { + TESTASSERT(ret == 0); + } + } + + return SRSRAN_SUCCESS; +} + static void usage(char* prog) { printf("Usage: %s [recov]\n", prog); @@ -120,10 +381,8 @@ static void parse_args(int argc, char** argv) int main(int argc, char** argv) { - int ret = SRSRAN_ERROR; - srsran_slot_cfg_t slot_cfg = {}; - srsran_csi_rs_nzp_resource_t resource = {}; - srsran_channel_awgn_t awgn = {}; + int ret = SRSRAN_ERROR; + srsran_channel_awgn_t awgn = {}; parse_args(argc, argv); @@ -138,56 +397,12 @@ int main(int argc, char** argv) goto clean_exit; } - // Fixed parameters, other params are not implemented - resource.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; - resource.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; - resource.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; - resource.resource_mapping.nof_ports = 1; - - // Row 1 supported only! - uint32_t nof_freq_dom_alloc = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1; - - uint32_t first_symbol_begin = (first_symbol != UINT32_MAX) ? first_symbol : 0; - uint32_t first_symbol_end = (first_symbol != UINT32_MAX) ? first_symbol : 13; - for (resource.resource_mapping.first_symbol_idx = first_symbol_begin; - resource.resource_mapping.first_symbol_idx <= first_symbol_end; - resource.resource_mapping.first_symbol_idx++) { - // Iterate over possible power control offset - float power_control_offset_begin = isnormal(power_control_offset) ? power_control_offset : -8.0f; - float power_control_offset_end = isnormal(power_control_offset) ? power_control_offset : 15.0f; - for (resource.power_control_offset = power_control_offset_begin; - resource.power_control_offset <= power_control_offset_end; - resource.power_control_offset += 1.0f) { - // Iterate over all possible starting number of PRB - uint32_t start_rb_begin = (start_rb != UINT32_MAX) ? start_rb : 0; - uint32_t start_rb_end = (start_rb != UINT32_MAX) ? start_rb : carrier.nof_prb - 24; - for (resource.resource_mapping.freq_band.start_rb = start_rb_begin; - resource.resource_mapping.freq_band.start_rb <= start_rb_end; - resource.resource_mapping.freq_band.start_rb += 4) { - // Iterate over all possible number of PRB - uint32_t nof_rb_begin = (nof_rb != UINT32_MAX) ? nof_rb : 24; - uint32_t nof_rb_end = - (nof_rb != UINT32_MAX) ? nof_rb : (carrier.nof_prb - resource.resource_mapping.freq_band.start_rb); - for (resource.resource_mapping.freq_band.nof_rb = nof_rb_begin; - resource.resource_mapping.freq_band.nof_rb <= nof_rb_end; - resource.resource_mapping.freq_band.nof_rb += 4) { - // Iterate for all slot numbers - for (slot_cfg.idx = 0; slot_cfg.idx < SRSRAN_NSLOTS_PER_FRAME_NR(carrier.scs); slot_cfg.idx++) { - // Steer Frequency allocation - for (uint32_t freq_dom_alloc = 0; freq_dom_alloc < nof_freq_dom_alloc; freq_dom_alloc++) { - for (uint32_t i = 0; i < nof_freq_dom_alloc; i++) { - resource.resource_mapping.frequency_domain_alloc[i] = i == freq_dom_alloc; - } + if (nzp_test_brute(&awgn, grid) < SRSRAN_SUCCESS) { + goto clean_exit; + } - // Call actual test - if (test(&slot_cfg, &resource, &awgn, grid) < SRSRAN_SUCCESS) { - goto clean_exit; - } - } - } - } - } - } + if (nzp_test_trs(&awgn, grid) < SRSRAN_SUCCESS) { + goto clean_exit; } ret = SRSRAN_SUCCESS; @@ -206,4 +421,4 @@ clean_exit: } return ret; -} \ No newline at end of file +} From eef3fac8636341a6cbf88d4310dda654d48d6628 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Wed, 28 Apr 2021 12:44:53 +0200 Subject: [PATCH 22/50] Improved float XOR readability --- lib/src/phy/common/sequence.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/src/phy/common/sequence.c b/lib/src/phy/common/sequence.c index 2ec871398..c0a47667a 100644 --- a/lib/src/phy/common/sequence.c +++ b/lib/src/phy/common/sequence.c @@ -211,10 +211,17 @@ void srsran_sequence_state_init(srsran_sequence_state_t* s, uint32_t seed) s->x2 = sequence_get_x2_init(seed); } +#define FLOAT_U32_XOR(DST, SRC, U32_MASK) \ + do { \ + uint32_t temp_u32; \ + memcpy(&temp_u32, &(SRC), 4); \ + temp_u32 ^= (U32_MASK); \ + memcpy(&(DST), &temp_u32, 4); \ + } while (false) + void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* out, uint32_t length) { - uint32_t i = 0; - const float xor [2] = {+0.0F, -0.0F}; + uint32_t i = 0; if (length >= SEQUENCE_PAR_BITS) { for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { @@ -246,7 +253,7 @@ void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* #endif // Finish the parallel bits with generic code for (; j < SEQUENCE_PAR_BITS; j++) { - *((uint32_t*)&out[i + j]) = *((uint32_t*)&value) ^ *((uint32_t*)&xor[(c >> j) & 1U]); + FLOAT_U32_XOR(out[i + j], value, (c << (31U - j)) & 0x80000000); } // Step sequences @@ -256,7 +263,7 @@ void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* } for (; i < length; i++) { - *((uint32_t*)&out[i]) = *((uint32_t*)&value) ^ *((uint32_t*)&xor[(s->x1 ^ s->x2) & 1U]); + FLOAT_U32_XOR(out[i], value, (s->x1 ^ s->x2) << 31U); // Step sequences s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1); @@ -266,8 +273,7 @@ void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* void srsran_sequence_state_apply_f(srsran_sequence_state_t* s, const float* in, float* out, uint32_t length) { - uint32_t i = 0; - const float xor [2] = {+0.0F, -0.0F}; + uint32_t i = 0; if (length >= SEQUENCE_PAR_BITS) { for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { @@ -289,17 +295,17 @@ void srsran_sequence_state_apply_f(srsran_sequence_state_t* s, const float* in, mask = _mm_and_si128(mask, (__m128i)_mm_set1_ps(-0.0F)); // Load input - __m128 v = _mm_load_ps(in + i + j); + __m128 v = _mm_loadu_ps(in + i + j); // Loads input and perform sign XOR v = _mm_xor_ps((__m128)mask, v); _mm_storeu_ps(out + i + j, v); } -#endif +#endif // LV_HAVE_SSE // Finish the parallel bits with generic code for (; j < SEQUENCE_PAR_BITS; j++) { - *((uint32_t*)&out[i + j]) = *((uint32_t*)&in[i + j]) ^ *((uint32_t*)&xor[(c >> j) & 1U]); + FLOAT_U32_XOR(out[i + j], in[i + j], (c << (31U - j)) & 0x80000000); } // Step sequences @@ -309,7 +315,7 @@ void srsran_sequence_state_apply_f(srsran_sequence_state_t* s, const float* in, } for (; i < length; i++) { - *((uint32_t*)&out[i]) = *((uint32_t*)&in[i]) ^ *((uint32_t*)&xor[(s->x1 ^ s->x2) & 1U]); + FLOAT_U32_XOR(out[i], in[i], (s->x1 ^ s->x2) << 31U); // Step sequences s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1); @@ -744,7 +750,7 @@ void srsran_sequence_apply_packed(const uint8_t* in, uint8_t* out, uint32_t leng out[i] = in[i] ^ reverse_lut[buffer & ((1U << rem8) - 1U) & 255U]; } -#else // SEQUENCE_PAR_BITS % 8 == 0 +#else // SEQUENCE_PAR_BITS % 8 == 0 while (i < (length / 8 - (SEQUENCE_PAR_BITS - 1) / 8)) { uint32_t c = (uint32_t)(x1 ^ x2); From de9158eeaf606828a016352d73ecdd811a1ae495 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Mon, 3 May 2021 18:13:55 +0200 Subject: [PATCH 23/50] Enable NR RRC MCS table selection --- lib/include/srsran/phy/phch/phch_cfg_nr.h | 1 + lib/src/phy/phch/ra_nr.c | 30 ++++++++++---------- srsue/src/stack/rrc/rrc_nr.cc | 34 +++++++++++++++++++++++ 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/lib/include/srsran/phy/phch/phch_cfg_nr.h b/lib/include/srsran/phy/phch/phch_cfg_nr.h index 57a02f21b..a8e8d337c 100644 --- a/lib/include/srsran/phy/phch/phch_cfg_nr.h +++ b/lib/include/srsran/phy/phch/phch_cfg_nr.h @@ -184,6 +184,7 @@ typedef struct SRSRAN_API { bool scrambling_id_present; uint32_t scambling_id; // Identifier used to initialize data scrambling (0-1023) + srsran_mcs_table_t mcs_table; srsran_dmrs_sch_type_t dmrs_type; srsran_dmrs_sch_len_t dmrs_max_length; struct { diff --git a/lib/src/phy/phch/ra_nr.c b/lib/src/phy/phch/ra_nr.c index 0f4a87f04..5581c03ec 100644 --- a/lib/src/phy/phch/ra_nr.c +++ b/lib/src/phy/phch/ra_nr.c @@ -670,13 +670,14 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, // 5.1.2.3 Physical resource block (PRB) bundling // ... - pdsch_grant->nof_layers = 1; - pdsch_grant->dci_format = dci_dl->ctx.format; - pdsch_grant->rnti = dci_dl->ctx.rnti; - pdsch_grant->rnti_type = dci_dl->ctx.rnti_type; - pdsch_grant->tb[0].rv = dci_dl->rv; - pdsch_grant->tb[0].mcs = dci_dl->mcs; - pdsch_grant->tb[0].ndi = dci_dl->ndi; + pdsch_grant->nof_layers = 1; + pdsch_grant->dci_format = dci_dl->ctx.format; + pdsch_grant->rnti = dci_dl->ctx.rnti; + pdsch_grant->rnti_type = dci_dl->ctx.rnti_type; + pdsch_grant->tb[0].rv = dci_dl->rv; + pdsch_grant->tb[0].mcs = dci_dl->mcs; + pdsch_grant->tb[0].ndi = dci_dl->ndi; + pdsch_cfg->sch_cfg.mcs_table = pdsch_hl_cfg->mcs_table; // 5.1.4 PDSCH resource mapping if (ra_dl_resource_mapping(carrier, slot, pdsch_hl_cfg, pdsch_cfg) < SRSRAN_SUCCESS) { @@ -783,13 +784,14 @@ int srsran_ra_ul_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, // 5.1.2.3 Physical resource block (PRB) bundling // ... - pusch_grant->nof_layers = 1; - pusch_grant->dci_format = dci_ul->ctx.format; - pusch_grant->rnti = dci_ul->ctx.rnti; - pusch_grant->rnti_type = dci_ul->ctx.rnti_type; - pusch_grant->tb[0].rv = dci_ul->rv; - pusch_grant->tb[0].mcs = dci_ul->mcs; - pusch_grant->tb[0].ndi = dci_ul->ndi; + pusch_grant->nof_layers = 1; + pusch_grant->dci_format = dci_ul->ctx.format; + pusch_grant->rnti = dci_ul->ctx.rnti; + pusch_grant->rnti_type = dci_ul->ctx.rnti_type; + pusch_grant->tb[0].rv = dci_ul->rv; + pusch_grant->tb[0].mcs = dci_ul->mcs; + pusch_grant->tb[0].ndi = dci_ul->ndi; + pusch_cfg->sch_cfg.mcs_table = pusch_hl_cfg->mcs_table; // 5.1.6.2 DM-RS reception procedure if (ra_ul_dmrs(pusch_hl_cfg, dci_ul, pusch_cfg) < SRSRAN_SUCCESS) { diff --git a/srsue/src/stack/rrc/rrc_nr.cc b/srsue/src/stack/rrc/rrc_nr.cc index cf8443c35..8c518a64c 100644 --- a/srsue/src/stack/rrc/rrc_nr.cc +++ b/srsue/src/stack/rrc/rrc_nr.cc @@ -606,6 +606,23 @@ bool rrc_nr::apply_sp_cell_init_dl_pdcch(const asn1::rrc_nr::pdcch_cfg_s& pdcch_ bool rrc_nr::apply_sp_cell_init_dl_pdsch(const asn1::rrc_nr::pdsch_cfg_s& pdsch_cfg) { + if (pdsch_cfg.mcs_table_present) { + switch (pdsch_cfg.mcs_table) { + case pdsch_cfg_s::mcs_table_opts::qam256: + phy_cfg.pdsch.mcs_table = srsran_mcs_table_256qam; + break; + case pdsch_cfg_s::mcs_table_opts::qam64_low_se: + phy_cfg.pdsch.mcs_table = srsran_mcs_table_qam64LowSE; + break; + case pdsch_cfg_s::mcs_table_opts::nulltype: + logger.warning("Warning while selecting pdsch mcs_table"); + return false; + } + } else { + // If the field is absent the UE applies the value 64QAM. + phy_cfg.pdsch.mcs_table = srsran_mcs_table_64qam; + } + if (pdsch_cfg.dmrs_dl_for_pdsch_map_type_a_present) { if (pdsch_cfg.dmrs_dl_for_pdsch_map_type_a.type() == setup_release_c::types_opts::setup) { srsran_dmrs_sch_add_pos_t srsran_dmrs_sch_add_pos; @@ -1016,6 +1033,23 @@ bool rrc_nr::apply_sp_cell_ded_ul_pucch(const asn1::rrc_nr::pucch_cfg_s& pucch_c bool rrc_nr::apply_sp_cell_ded_ul_pusch(const asn1::rrc_nr::pusch_cfg_s& pusch_cfg) { + if (pusch_cfg.mcs_table_present) { + switch (pusch_cfg.mcs_table) { + case pusch_cfg_s::mcs_table_opts::qam256: + phy_cfg.pusch.mcs_table = srsran_mcs_table_256qam; + break; + case pusch_cfg_s::mcs_table_opts::qam64_low_se: + phy_cfg.pusch.mcs_table = srsran_mcs_table_qam64LowSE; + break; + case pusch_cfg_s::mcs_table_opts::nulltype: + logger.warning("Warning while selecting pdsch mcs_table"); + return false; + } + } else { + // If the field is absent the UE applies the value 64QAM. + phy_cfg.pusch.mcs_table = srsran_mcs_table_64qam; + } + srsran_resource_alloc_t resource_alloc; if (make_phy_pusch_alloc_type(pusch_cfg, &resource_alloc) == true) { phy_cfg.pusch.alloc = resource_alloc; From 9fb091262aedb0f32be272968ffbe04e604886eb Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Mon, 3 May 2021 18:14:32 +0200 Subject: [PATCH 24/50] Duplicate NR PDSCH transmits ACK and add ACK Tx TTI to trace --- srsue/src/phy/nr/cc_worker.cc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 9d7eb9503..454a24378 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -229,9 +229,14 @@ bool cc_worker::work_dl() mac_dl_grant.tti = dl_slot_cfg.idx; phy->stack->new_grant_dl(0, mac_dl_grant, &dl_action); - // Early stop if MAC says it doesn't need the TB + // Abort if MAC says it doesn't need the TB if (not dl_action.tb.enabled) { - logger.info("Decoding not required. Skipping PDSCH"); + // Force positive ACK + if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) { + phy->set_pending_ack(dl_slot_cfg.idx, ack_resource, true); + } + + logger.info("Decoding not required. Skipping PDSCH. ack_tti_tx=%d", TTI_ADD(dl_slot_cfg.idx, ack_resource.k1)); return true; } @@ -270,8 +275,13 @@ bool cc_worker::work_dl() str.data(), str_extra.data()); } else { - logger.info( - pdsch_res.tb[0].payload, pdsch_cfg.grant.tb[0].tbs / 8, "PDSCH: cc=%d pid=%d %s", cc_idx, pid, str.data()); + logger.info(pdsch_res.tb[0].payload, + pdsch_cfg.grant.tb[0].tbs / 8, + "PDSCH: cc=%d pid=%d %s ack_tti_tx=%d", + cc_idx, + pid, + str.data(), + TTI_ADD(dl_slot_cfg.idx, ack_resource.k1)); } } From 9739a9de5fdbbf3960312664c5d84d6cc688fdf7 Mon Sep 17 00:00:00 2001 From: Francisco Date: Tue, 4 May 2021 11:20:26 +0100 Subject: [PATCH 25/50] add missing srsenb expert parameter to enb.conf.example --- srsenb/enb.conf.example | 1 + 1 file changed, 1 insertion(+) diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index d902f6767..5f8d9ebae 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -327,5 +327,6 @@ enable = false #rrc_inactivity_timer = 30000 #max_nof_kos = 100 #max_prach_offset_us = 30 +#max_nof_ues = 8 #eea_pref_list = EEA0, EEA2, EEA1 #eia_pref_list = EIA2, EIA1, EIA0 From 5818488b27201f5b07d7b3697b40075a50cb4428 Mon Sep 17 00:00:00 2001 From: Francisco Date: Tue, 4 May 2021 11:37:02 +0100 Subject: [PATCH 26/50] change enb max_nof_ues config parameter to nof_prealloc_ues --- lib/include/srsran/interfaces/enb_mac_interfaces.h | 2 +- srsenb/enb.conf.example | 2 +- srsenb/src/enb_cfg_parser.cc | 6 +++--- srsenb/src/main.cc | 2 +- srsenb/src/stack/enb_stack_lte.cc | 4 ++-- srsenb/src/stack/mac/mac.cc | 6 +++--- srsenb/test/upper/test_helpers.cc | 10 +++++----- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/include/srsran/interfaces/enb_mac_interfaces.h b/lib/include/srsran/interfaces/enb_mac_interfaces.h index 3d229b9cc..591cb204c 100644 --- a/lib/include/srsran/interfaces/enb_mac_interfaces.h +++ b/lib/include/srsran/interfaces/enb_mac_interfaces.h @@ -22,7 +22,7 @@ struct mac_args_t { uint32_t nof_prb; ///< Needed to dimension MAC softbuffers for all cells sched_interface::sched_args_t sched; int nr_tb_size = -1; - uint32_t max_nof_ues; + uint32_t nof_prealloc_ues; uint32_t max_nof_kos; }; diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 5f8d9ebae..b14572635 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -327,6 +327,6 @@ enable = false #rrc_inactivity_timer = 30000 #max_nof_kos = 100 #max_prach_offset_us = 30 -#max_nof_ues = 8 +#nof_prealloc_ues = 8 #eea_pref_list = EEA0, EEA2, EEA1 #eia_pref_list = EIA2, EIA1, EIA0 diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 98d8127ef..4cdf72269 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -905,9 +905,9 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ { // Sanity checks ASSERT_VALID_CFG(not rrc_cfg_->cell_list.empty(), "No cell specified in rr.conf."); - ASSERT_VALID_CFG(args_->stack.mac.max_nof_ues <= SRSENB_MAX_UES and args_->stack.mac.max_nof_ues > 0, - "mac.max_nof_ues=%d must be within [0, %d]", - args_->stack.mac.max_nof_ues, + ASSERT_VALID_CFG(args_->stack.mac.nof_prealloc_ues <= SRSENB_MAX_UES, + "mac.nof_prealloc_ues=%d must be within [0, %d]", + args_->stack.mac.nof_prealloc_ues, SRSENB_MAX_UES); // Check for a forced DL EARFCN or frequency (only valid for a single cell config (Xico's favorite feature)) diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 66b9c0aa5..eb7f8198a 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -214,7 +214,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.print_buffer_state", bpo::value(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds") ("expert.eea_pref_list", bpo::value(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).") ("expert.eia_pref_list", bpo::value(&args->general.eia_pref_list)->default_value("EIA2, EIA1, EIA0"), "Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).") - ("expert.max_nof_ues", bpo::value(&args->stack.mac.max_nof_ues)->default_value(8), "Maximum number of connected UEs") + ("expert.nof_prealloc_ues", bpo::value(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate") ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release") ("expert.max_mac_ul_kos", bpo::value(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release") diff --git a/srsenb/src/stack/enb_stack_lte.cc b/srsenb/src/stack/enb_stack_lte.cc index 59d44c5c9..7a193f208 100644 --- a/srsenb/src/stack/enb_stack_lte.cc +++ b/srsenb/src/stack/enb_stack_lte.cc @@ -73,9 +73,9 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) rrc_cfg = rrc_cfg_; // Init RNTI and bearer memory pools - reserve_rnti_memblocks(args.mac.max_nof_ues); + reserve_rnti_memblocks(args.mac.nof_prealloc_ues); uint32_t min_nof_bearers_per_ue = 4; - reserve_rlc_memblocks(args.mac.max_nof_ues * min_nof_bearers_per_ue); + reserve_rlc_memblocks(args.mac.nof_prealloc_ues * min_nof_bearers_per_ue); // setup logging for each layer mac_logger.set_level(srslog::str_to_basic_level(args.log.mac_level)); diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index d664920e4..6b4a271c4 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -84,7 +84,7 @@ bool mac::init(const mac_args_t& args_, }; auto recycle_softbuffers = [](ue_cc_softbuffers& softbuffers) { softbuffers.clear(); }; softbuffer_pool.reset(new srsran::background_obj_pool( - 8, 8, args.max_nof_ues, init_softbuffers, recycle_softbuffers)); + 8, 8, args.nof_prealloc_ues, init_softbuffers, recycle_softbuffers)); // Pre-alloc UE objects for first attaching users prealloc_ue(10); @@ -469,8 +469,8 @@ uint16_t mac::allocate_ue() logger.info("RACH ignored as eNB is being shutdown"); return SRSRAN_INVALID_RNTI; } - if (ue_db.size() >= args.max_nof_ues) { - logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", args.max_nof_ues); + if (ue_db.size() >= SRSENB_MAX_UES) { + logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", SRSENB_MAX_UES); return SRSRAN_INVALID_RNTI; } auto ret = ue_db.insert(rnti, std::move(ue_ptr)); diff --git a/srsenb/test/upper/test_helpers.cc b/srsenb/test/upper/test_helpers.cc index a35873b1e..10b69e6bd 100644 --- a/srsenb/test/upper/test_helpers.cc +++ b/srsenb/test/upper/test_helpers.cc @@ -56,11 +56,11 @@ int parse_default_cfg(rrc_cfg_t* rrc_cfg, srsenb::all_args_t& args) args.enb.n_prb = 50; TESTASSERT(srsran::string_to_mcc("001", &args.stack.s1ap.mcc)); TESTASSERT(srsran::string_to_mnc("01", &args.stack.s1ap.mnc)); - args.enb.transmission_mode = 1; - args.enb.nof_ports = 1; - args.general.eia_pref_list = "EIA2, EIA1, EIA0"; - args.general.eea_pref_list = "EEA0, EEA2, EEA1"; - args.stack.mac.max_nof_ues = 2; + args.enb.transmission_mode = 1; + args.enb.nof_ports = 1; + args.general.eia_pref_list = "EIA2, EIA1, EIA0"; + args.general.eea_pref_list = "EEA0, EEA2, EEA1"; + args.stack.mac.nof_prealloc_ues = 2; args.general.rrc_inactivity_timer = 60000; From 7bee75e1265aca85a723ff615c53c2f8441a3694 Mon Sep 17 00:00:00 2001 From: Francisco Date: Tue, 4 May 2021 16:26:44 +0100 Subject: [PATCH 27/50] update description of srsenb --expert.nof_prealloc_ues config parameter, and added a comment in header file explaining the use of such parameter. --- lib/include/srsran/interfaces/enb_mac_interfaces.h | 2 +- srsenb/enb.conf.example | 1 + srsenb/src/main.cc | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/include/srsran/interfaces/enb_mac_interfaces.h b/lib/include/srsran/interfaces/enb_mac_interfaces.h index 591cb204c..f3cac8286 100644 --- a/lib/include/srsran/interfaces/enb_mac_interfaces.h +++ b/lib/include/srsran/interfaces/enb_mac_interfaces.h @@ -22,7 +22,7 @@ struct mac_args_t { uint32_t nof_prb; ///< Needed to dimension MAC softbuffers for all cells sched_interface::sched_args_t sched; int nr_tb_size = -1; - uint32_t nof_prealloc_ues; + uint32_t nof_prealloc_ues; ///< Number of UE resources to pre-allocate at eNB startup uint32_t max_nof_kos; }; diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index b14572635..19716f130 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -304,6 +304,7 @@ enable = false # tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR) # rrc_inactivity_timer Inactivity timeout used to remove UE context from RRC (in milliseconds). # max_prach_offset_us: Maximum allowed RACH offset (in us) +# nof_prealloc_ues: Number of UE memory resources to preallocate during eNB initialization for faster UE creation (Default 8) # eea_pref_list: Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1). # eia_pref_list: Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0). # diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index eb7f8198a..e828ba721 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -214,7 +214,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.print_buffer_state", bpo::value(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds") ("expert.eea_pref_list", bpo::value(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).") ("expert.eia_pref_list", bpo::value(&args->general.eia_pref_list)->default_value("EIA2, EIA1, EIA0"), "Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).") - ("expert.nof_prealloc_ues", bpo::value(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate") + ("expert.nof_prealloc_ues", bpo::value(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate during eNB initialization") ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release") ("expert.max_mac_ul_kos", bpo::value(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release") From 617fb2ba7eae33b2c39c06ac95fd034518cd4429 Mon Sep 17 00:00:00 2001 From: David Rupprecht Date: Tue, 4 May 2021 17:36:36 +0200 Subject: [PATCH 28/50] Added default destructor for args --- srsue/hdr/stack/upper/nas_config.h | 2 +- srsue/hdr/stack/upper/usim_base.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/srsue/hdr/stack/upper/nas_config.h b/srsue/hdr/stack/upper/nas_config.h index b185c75a5..1836b7fce 100644 --- a/srsue/hdr/stack/upper/nas_config.h +++ b/srsue/hdr/stack/upper/nas_config.h @@ -26,7 +26,7 @@ class nas_args_t { public: nas_args_t() : force_imsi_attach(false) {} - + ~nas_args_t() = default; std::string apn_name; std::string apn_protocol; std::string apn_user; diff --git a/srsue/hdr/stack/upper/usim_base.h b/srsue/hdr/stack/upper/usim_base.h index cdb3019aa..54f7175a0 100644 --- a/srsue/hdr/stack/upper/usim_base.h +++ b/srsue/hdr/stack/upper/usim_base.h @@ -42,6 +42,7 @@ class usim_args_t { public: usim_args_t() : using_op(false) {} + ~usim_args_t() = default; std::string mode; std::string algo; bool using_op; From 8c194b887b68a267fef9bfc9984040b01de3f968 Mon Sep 17 00:00:00 2001 From: David Rupprecht Date: Tue, 4 May 2021 17:43:34 +0200 Subject: [PATCH 29/50] Allow NAS init function to fail --- srsue/hdr/stack/upper/nas.h | 2 +- srsue/src/stack/upper/nas.cc | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/srsue/hdr/stack/upper/nas.h b/srsue/hdr/stack/upper/nas.h index 37186d57d..e2d33fe33 100644 --- a/srsue/hdr/stack/upper/nas.h +++ b/srsue/hdr/stack/upper/nas.h @@ -39,7 +39,7 @@ class nas : public nas_interface_rrc, public srsran::timer_callback public: explicit nas(srsran::task_sched_handle task_sched_); virtual ~nas(); - void init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& args_); + int init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& args_); void stop(); void run_tti(); diff --git a/srsue/src/stack/upper/nas.cc b/srsue/src/stack/upper/nas.cc index 204d68893..3633e70e6 100644 --- a/srsue/src/stack/upper/nas.cc +++ b/srsue/src/stack/upper/nas.cc @@ -46,7 +46,7 @@ nas::nas(srsran::task_sched_handle task_sched_) : logger(srslog::fetch_basic_logger("NAS")) {} -void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& cfg_) +int nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& cfg_) { usim = usim_; rrc = rrc_; @@ -105,6 +105,7 @@ void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_ } running = true; + return SRSRAN_SUCCESS; } nas::~nas() {} From 3284143b3990888f010d6c46fb57216987aa0439 Mon Sep 17 00:00:00 2001 From: David Rupprecht Date: Tue, 4 May 2021 17:49:13 +0200 Subject: [PATCH 30/50] Capture false encoded messages before sending --- srsenb/src/stack/rrc/rrc.cc | 7 +++++-- srsenb/src/stack/rrc/rrc_nr.cc | 10 ++++++++-- srsenb/src/stack/rrc/rrc_ue.cc | 5 ++++- srsenb/src/stack/upper/s1ap.cc | 5 ++++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index 4fb9b6331..8c8fd5e85 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -735,7 +735,7 @@ uint32_t rrc::generate_sibs() return SRSRAN_ERROR; } asn1::bit_ref bref(sib_buffer->msg, sib_buffer->get_tailroom()); - if (msg[msg_index].pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { + if (msg[msg_index].pack(bref) != asn1::SRSASN_SUCCESS) { logger.error("Failed to pack SIB message %d", msg_index); return SRSRAN_ERROR; } @@ -889,7 +889,10 @@ int rrc::pack_mcch() const int rlc_header_len = 1; asn1::bit_ref bref(&mcch_payload_buffer[rlc_header_len], sizeof(mcch_payload_buffer) - rlc_header_len); - mcch.pack(bref); + if (mcch.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack MCCH message"); + } + current_mcch_length = bref.distance_bytes(&mcch_payload_buffer[1]); current_mcch_length = current_mcch_length + rlc_header_len; return current_mcch_length; diff --git a/srsenb/src/stack/rrc/rrc_nr.cc b/srsenb/src/stack/rrc/rrc_nr.cc index 2f66c6ae5..e8b107603 100644 --- a/srsenb/src/stack/rrc/rrc_nr.cc +++ b/srsenb/src/stack/rrc/rrc_nr.cc @@ -215,7 +215,10 @@ int32_t rrc_nr::generate_sibs() return SRSRAN_ERROR; } asn1::bit_ref bref(mib_buf->msg, mib_buf->get_tailroom()); - mib_msg.pack(bref); + if (mib_msg.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Couldn't pack mib msg"); + return SRSRAN_ERROR; + } mib_buf->N_bytes = bref.distance_bytes(); logger.debug(mib_buf->msg, mib_buf->N_bytes, "MIB payload (%d B)", mib_buf->N_bytes); mib_buffer = std::move(mib_buf); @@ -253,7 +256,10 @@ int32_t rrc_nr::generate_sibs() return SRSRAN_ERROR; } asn1::bit_ref bref(sib->msg, sib->get_tailroom()); - msg[msg_index].pack(bref); + if (msg[msg_index].pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack SIB message %d", msg_index); + return SRSRAN_ERROR; + } sib->N_bytes = bref.distance_bytes(); sib_buffer.push_back(std::move(sib)); diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index 78255d958..12192230e 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -911,7 +911,10 @@ bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) dest.resize(bref2.distance_bytes()); memcpy(dest.data(), pdu->msg, bref2.distance_bytes()); bref2 = asn1::bit_ref{pdu->msg, pdu->get_tailroom()}; - ue_rat_caps.pack(bref2); + if (ue_rat_caps.pack(bref2) != asn1::SRSASN_SUCCESS) { + parent->logger.error("Couldn't pack ue rat caps"); + return false; + } pdu->N_bytes = bref2.distance_bytes(); parent->s1ap->send_ue_cap_info_indication(rnti, std::move(pdu)); } diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index a828911d7..7b78d27df 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -1806,7 +1806,10 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt return false; } asn1::bit_ref bref(buf->msg, buf->get_tailroom()); - tx_pdu.pack(bref); + if (tx_pdu.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack TX PDU %s", procedure_name); + return false; + } buf->N_bytes = bref.distance_bytes(); // Save message to PCAP From c117b563c94f045133c070998c6449ef0ec29265 Mon Sep 17 00:00:00 2001 From: David Rupprecht Date: Tue, 4 May 2021 19:08:46 +0200 Subject: [PATCH 31/50] Initial commit for ngap in enb --- lib/include/srsran/asn1/ngap_utils.h | 31 ++ .../srsran/interfaces/gnb_interfaces.h | 12 - .../srsran/interfaces/gnb_ngap_interfaces.h | 60 +++ .../srsran/interfaces/gnb_rrc_nr_interfaces.h | 24 ++ srsenb/hdr/stack/rrc/rrc_nr.h | 2 + srsenb/hdr/stack/upper/ngap.h | 134 ++++++ srsenb/src/stack/upper/CMakeLists.txt | 2 +- srsenb/src/stack/upper/ngap.cc | 405 ++++++++++++++++++ 8 files changed, 657 insertions(+), 13 deletions(-) create mode 100644 lib/include/srsran/asn1/ngap_utils.h create mode 100644 lib/include/srsran/interfaces/gnb_ngap_interfaces.h create mode 100644 lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h create mode 100644 srsenb/hdr/stack/upper/ngap.h create mode 100644 srsenb/src/stack/upper/ngap.cc diff --git a/lib/include/srsran/asn1/ngap_utils.h b/lib/include/srsran/asn1/ngap_utils.h new file mode 100644 index 000000000..59d16c487 --- /dev/null +++ b/lib/include/srsran/asn1/ngap_utils.h @@ -0,0 +1,31 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_NGAP_UTILS_H +#define SRSRAN_NGAP_UTILS_H + +#include "asn1_utils.h" +#include "ngap.h" +/************************ + * Forward declarations + ***********************/ + +namespace asn1 { +namespace ngap_nr { +struct rrcestablishment_cause_opts; +struct cause_radio_network_opts; +using rrcestablishment_cause_e = enumerated; +using cause_radio_network_e = enumerated; +} // namespace ngap +} // namespace asn1 + +#endif // SRSRAN_NGAP_UTILS_H \ No newline at end of file diff --git a/lib/include/srsran/interfaces/gnb_interfaces.h b/lib/include/srsran/interfaces/gnb_interfaces.h index 31681a9be..35f19d651 100644 --- a/lib/include/srsran/interfaces/gnb_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_interfaces.h @@ -159,18 +159,6 @@ class rrc_interface_pdcp_nr public: virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; }; -class rrc_interface_ngap_nr -{ -public: -}; - -/***************************** - * NGAP INTERFACES - ****************************/ -class ngap_interface_rrc_nr -{ -public: -}; class phy_interface_stack_nr { diff --git a/lib/include/srsran/interfaces/gnb_ngap_interfaces.h b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h new file mode 100644 index 000000000..ff39c7ffe --- /dev/null +++ b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h @@ -0,0 +1,60 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_GNB_NGAP_INTERFACES_H +#define SRSRAN_GNB_NGAP_INTERFACES_H + +#include "srsran/asn1/ngap_utils.h" + +namespace srsenb { + +struct ngap_args_t { + uint32_t gnb_id; // 20-bit id (lsb bits) + uint8_t cell_id; // 8-bit cell id + uint16_t tac; // 16-bit tac + uint16_t mcc; // BCD-coded with 0xF filler + uint16_t mnc; // BCD-coded with 0xF filler + std::string amf_addr; + std::string gtp_bind_addr; + std::string gtp_advertise_addr; + std::string ngc_bind_addr; + std::string gnb_name; +}; + +// NGAP interface for RRC +class ngap_interface_rrc_nr +{ +public: + virtual void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap_nr::rrcestablishment_cause_e cause, + srsran::unique_byte_buffer_t pdu) = 0; + virtual void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap_nr::rrcestablishment_cause_e cause, + srsran::unique_byte_buffer_t pdu, + uint32_t m_tmsi, + uint8_t mmec) = 0; + + virtual void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu) = 0; + virtual bool user_exists(uint16_t rnti) = 0; + virtual void user_mod(uint16_t old_rnti, uint16_t new_rnti) = 0; + virtual bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) = 0; + virtual bool is_amf_connected() = 0; + + /// TS 36.413, 8.3.1 - Initial Context Setup + virtual void ue_ctxt_setup_complete(uint16_t rnti) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_GNB_NGAP_INTERFACES_H \ No newline at end of file diff --git a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h new file mode 100644 index 000000000..af7ec46a3 --- /dev/null +++ b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h @@ -0,0 +1,24 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#ifndef SRSRAN_GNB_RRC_NR_INTERFACES_H +#define SRSRAN_GNB_RRC_NR_INTERFACES_H + +namespace srsenb { + +class rrc_interface_ngap_nr +{ +public: +}; + +} // namespace srsenb + +#endif // SRSRAN_GNB_RRC_NR_INTERFACES_H \ No newline at end of file diff --git a/srsenb/hdr/stack/rrc/rrc_nr.h b/srsenb/hdr/stack/rrc/rrc_nr.h index 0e2f3b944..b8fbf1622 100644 --- a/srsenb/hdr/stack/rrc/rrc_nr.h +++ b/srsenb/hdr/stack/rrc/rrc_nr.h @@ -24,6 +24,8 @@ #include "srsran/common/threads.h" #include "srsran/common/timeout.h" #include "srsran/interfaces/gnb_interfaces.h" +#include "srsran/interfaces/gnb_ngap_interfaces.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" #include #include diff --git a/srsenb/hdr/stack/upper/ngap.h b/srsenb/hdr/stack/upper/ngap.h new file mode 100644 index 000000000..3962b88a7 --- /dev/null +++ b/srsenb/hdr/stack/upper/ngap.h @@ -0,0 +1,134 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#ifndef SRSENB_NGAP_H +#define SRSENB_NGAP_H + +#include "srsenb/hdr/common/common_enb.h" +#include "srsran/adt/circular_map.h" +#include "srsran/asn1/asn1_utils.h" +#include "srsran/asn1/ngap.h" +#include "srsran/common/bcd_helpers.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/network_utils.h" +#include "srsran/common/stack_procedure.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/task_scheduler.h" +#include "srsran/common/threads.h" +#include "srsran/interfaces/gnb_ngap_interfaces.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" +#include "srsran/srslog/srslog.h" + +namespace srsenb { +class ngap : public ngap_interface_rrc_nr +{ +public: + ngap(srsran::task_sched_handle task_sched_, + srslog::basic_logger& logger, + srsran::socket_manager_itf* rx_socket_handler); + int init(ngap_args_t args_, rrc_interface_ngap_nr* rrc_); + void stop(); + + // RRC NR interface + void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap_nr::rrcestablishment_cause_e cause, + srsran::unique_byte_buffer_t pdu){}; + void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap_nr::rrcestablishment_cause_e cause, + srsran::unique_byte_buffer_t pdu, + uint32_t m_tmsi, + uint8_t mmec){}; + void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu){}; + bool user_exists(uint16_t rnti) { return true; }; + void user_mod(uint16_t old_rnti, uint16_t new_rnti){}; + bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) { return true; }; + bool is_amf_connected(); + + /// TS 36.413, 8.3.1 - Initial Context Setup + void ue_ctxt_setup_complete(uint16_t rnti){}; + + // Stack interface + bool + handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); + +private: + static const int AMF_PORT = 38412; + static const int ADDR_FAMILY = AF_INET; + static const int SOCK_TYPE = SOCK_STREAM; + static const int PROTO = IPPROTO_SCTP; + static const int PPID = 60; + static const int NONUE_STREAM_ID = 0; + + // args + rrc_interface_ngap_nr* rrc = nullptr; + ngap_args_t args; + srslog::basic_logger& logger; + srsran::task_sched_handle task_sched; + srsran::task_queue_handle amf_task_queue; + srsran::socket_manager_itf* rx_socket_handler; + + srsran::unique_socket amf_socket; + struct sockaddr_in amf_addr = {}; // AMF address + bool amf_connected = false; + bool running = false; + uint32_t next_enb_ue_ngap_id = 1; // Next ENB-side UE identifier + uint16_t next_ue_stream_id = 1; // Next UE SCTP stream identifier + srsran::unique_timer amf_connect_timer, ngsetup_timeout; + + // Protocol IEs sent with every UL NGAP message + asn1::ngap_nr::tai_s tai; + asn1::ngap_nr::nr_cgi_s nr_cgi; + + asn1::ngap_nr::ng_setup_resp_s ngsetupresponse; + + // procedures + class ng_setup_proc_t + { + public: + struct ngsetupresult { + bool success = false; + enum class cause_t { timeout, failure } cause; + }; + + explicit ng_setup_proc_t(ngap* ngap_) : ngap_ptr(ngap_) {} + srsran::proc_outcome_t init(); + srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } + srsran::proc_outcome_t react(const ngsetupresult& event); + void then(const srsran::proc_state_t& result) const; + const char* name() const { return "AMF Connection"; } + + private: + srsran::proc_outcome_t start_amf_connection(); + + ngap* ngap_ptr = nullptr; + }; + + void build_tai_cgi(); + bool connect_amf(); + bool setup_ng(); + bool sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name); + + bool handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu); + bool handle_successfuloutcome(const asn1::ngap_nr::successful_outcome_s& msg); + + bool handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg); + + srsran::proc_t ngsetup_proc; + + std::string get_cause(const asn1::ngap_nr::cause_c& c); + void log_ngap_msg(const asn1::ngap_nr::ngap_pdu_c& msg, srsran::const_span sdu, bool is_rx); +}; + +} // namespace srsenb +#endif diff --git a/srsenb/src/stack/upper/CMakeLists.txt b/srsenb/src/stack/upper/CMakeLists.txt index 226bd4eb1..9ec6d3bbe 100644 --- a/srsenb/src/stack/upper/CMakeLists.txt +++ b/srsenb/src/stack/upper/CMakeLists.txt @@ -9,5 +9,5 @@ set(SOURCES gtpu.cc pdcp.cc rlc.cc s1ap.cc) add_library(srsenb_upper STATIC ${SOURCES}) -set(SOURCES pdcp_nr.cc rlc_nr.cc sdap.cc) +set(SOURCES pdcp_nr.cc rlc_nr.cc sdap.cc ngap.cc) add_library(srsgnb_upper STATIC ${SOURCES}) diff --git a/srsenb/src/stack/upper/ngap.cc b/srsenb/src/stack/upper/ngap.cc new file mode 100644 index 000000000..6aba77892 --- /dev/null +++ b/srsenb/src/stack/upper/ngap.cc @@ -0,0 +1,405 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/stack/upper/ngap.h" + +#define procError(fmt, ...) ngap_ptr->logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procWarning(fmt, ...) ngap_ptr->logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procInfo(fmt, ...) ngap_ptr->logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) + +using namespace asn1::ngap_nr; + +namespace srsenb { +/********************************************************* + * AMF Connection + *********************************************************/ + +srsran::proc_outcome_t ngap::ng_setup_proc_t::init() +{ + procInfo("Starting new AMF connection."); + return start_amf_connection(); +} + +srsran::proc_outcome_t ngap::ng_setup_proc_t::start_amf_connection() +{ + if (not ngap_ptr->running) { + procInfo("NGAP is not running anymore."); + return srsran::proc_outcome_t::error; + } + if (ngap_ptr->amf_connected) { + procInfo("gNB NGAP is already connected to AMF"); + return srsran::proc_outcome_t::success; + } + + if (not ngap_ptr->connect_amf()) { + procInfo("Could not connect to AMF"); + return srsran::proc_outcome_t::error; + } + + if (not ngap_ptr->setup_ng()) { + procError("NG setup failed. Exiting..."); + srsran::console("NG setup failed\n"); + ngap_ptr->running = false; + return srsran::proc_outcome_t::error; + } + + ngap_ptr->ngsetup_timeout.run(); + procInfo("NGSetupRequest sent. Waiting for response..."); + return srsran::proc_outcome_t::yield; +} + +srsran::proc_outcome_t ngap::ng_setup_proc_t::react(const srsenb::ngap::ng_setup_proc_t::ngsetupresult& event) +{ + if (ngap_ptr->ngsetup_timeout.is_running()) { + ngap_ptr->ngsetup_timeout.stop(); + } + if (event.success) { + procInfo("NGSetup procedure completed successfully"); + return srsran::proc_outcome_t::success; + } + procError("NGSetup failed."); + srsran::console("NGsetup failed\n"); + return srsran::proc_outcome_t::error; +} + +void ngap::ng_setup_proc_t::then(const srsran::proc_state_t& result) const +{ + if (result.is_error()) { + procInfo("Failed to initiate NG connection. Attempting reconnection in %d seconds", + ngap_ptr->amf_connect_timer.duration() / 1000); + srsran::console("Failed to initiate NG connection. Attempting reconnection in %d seconds\n", + ngap_ptr->amf_connect_timer.duration() / 1000); + ngap_ptr->rx_socket_handler->remove_socket(ngap_ptr->amf_socket.get_socket()); + ngap_ptr->amf_socket.close(); + procInfo("NGAP socket closed."); + ngap_ptr->amf_connect_timer.run(); + // Try again with in 10 seconds + } +} + +/********************************************************* + * NGAP class + *********************************************************/ + +ngap::ngap(srsran::task_sched_handle task_sched_, + srslog::basic_logger& logger, + srsran::socket_manager_itf* rx_socket_handler_) : + ngsetup_proc(this), logger(logger), task_sched(task_sched_), rx_socket_handler(rx_socket_handler_) +{ + amf_task_queue = task_sched.make_task_queue(); +} + +int ngap::init(ngap_args_t args_, rrc_interface_ngap_nr* rrc_) +{ + rrc = rrc_; + args = args_; + + build_tai_cgi(); + + // Setup AMF reconnection timer + amf_connect_timer = task_sched.get_unique_timer(); + auto amf_connect_run = [this](uint32_t tid) { + if (ngsetup_proc.is_busy()) { + logger.error("Failed to initiate NGSetup procedure."); + } + ngsetup_proc.launch(); + }; + amf_connect_timer.set(10000, amf_connect_run); + // Setup NGSetup timeout + ngsetup_timeout = task_sched.get_unique_timer(); + uint32_t ngsetup_timeout_val = 5000; + ngsetup_timeout.set(ngsetup_timeout_val, [this](uint32_t tid) { + ng_setup_proc_t::ngsetupresult res; + res.success = false; + res.cause = ng_setup_proc_t::ngsetupresult::cause_t::timeout; + ngsetup_proc.trigger(res); + }); + + running = true; + // starting AMF connection + if (not ngsetup_proc.launch()) { + logger.error("Failed to initiate NGSetup procedure."); + } + + return SRSRAN_SUCCESS; +} + +void ngap::stop() +{ + running = false; + amf_socket.close(); +} + +bool ngap::is_amf_connected() +{ + return amf_connected; +} + +// Generate common NGAP protocol IEs from config args +void ngap::build_tai_cgi() +{ + uint32_t plmn; + + // TAI + srsran::s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + tai.plmn_id.from_number(plmn); + + tai.tac.from_number(args.tac); + + // nr_cgi + nr_cgi.plmn_id.from_number(plmn); + // TODO Check how to build nr cell id + nr_cgi.nrcell_id.from_number((uint32_t)(args.gnb_id << 8) | args.cell_id); +} + +/******************************************************************************* +/* NGAP message handlers +********************************************************************************/ +bool ngap::handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, + const sockaddr_in& from, + const sctp_sndrcvinfo& sri, + int flags) +{ + // Handle Notification Case + if (flags & MSG_NOTIFICATION) { + // Received notification + union sctp_notification* notification = (union sctp_notification*)pdu->msg; + logger.debug("SCTP Notification %d", notification->sn_header.sn_type); + if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) { + logger.info("SCTP Association Shutdown. Association: %d", sri.sinfo_assoc_id); + srsran::console("SCTP Association Shutdown. Association: %d\n", sri.sinfo_assoc_id); + rx_socket_handler->remove_socket(amf_socket.get_socket()); + amf_socket.close(); + } else if (notification->sn_header.sn_type == SCTP_PEER_ADDR_CHANGE && + notification->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) { + logger.info("SCTP peer addres unreachable. Association: %d", sri.sinfo_assoc_id); + srsran::console("SCTP peer address unreachable. Association: %d\n", sri.sinfo_assoc_id); + rx_socket_handler->remove_socket(amf_socket.get_socket()); + amf_socket.close(); + } + } else if (pdu->N_bytes == 0) { + logger.error("SCTP return 0 bytes. Closing socket"); + amf_socket.close(); + } + + // Restart MME connection procedure if we lost connection + if (not amf_socket.is_open()) { + amf_connected = false; + if (ngsetup_proc.is_busy()) { + logger.error("Failed to initiate MME connection procedure, as it is already running."); + return false; + } + ngsetup_proc.launch(); + return false; + } + + handle_ngap_rx_pdu(pdu.get()); + return true; +} + +bool ngap::handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu) +{ + // Save message to PCAP + // if (pcap != nullptr) { + // pcap->write_ngap(pdu->msg, pdu->N_bytes); + // } + + ngap_pdu_c rx_pdu; + asn1::cbit_ref bref(pdu->msg, pdu->N_bytes); + + if (rx_pdu.unpack(bref) != asn1::SRSASN_SUCCESS) { + logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack received PDU"); + cause_c cause; + cause.set_protocol().value = cause_protocol_opts::transfer_syntax_error; + // send_error_indication(cause); + return false; + } + // log_ngap_msg(rx_pdu, srsran::make_span(*pdu), true); + + switch (rx_pdu.type().value) { + // case ngap_pdu_c::types_opts::init_msg: + // return handle_initiatingmessage(rx_pdu.init_msg()); + case ngap_pdu_c::types_opts::successful_outcome: + return handle_successfuloutcome(rx_pdu.successful_outcome()); + // case ngap_pdu_c::types_opts::unsuccessful_outcome: + // return handle_unsuccessfuloutcome(rx_pdu.unsuccessful_outcome()); + default: + logger.error("Unhandled PDU type %d", rx_pdu.type().value); + return false; + } + + return true; +} + +bool ngap::handle_successfuloutcome(const successful_outcome_s& msg) +{ + switch (msg.value.type().value) { + case ngap_elem_procs_o::successful_outcome_c::types_opts::ng_setup_resp: + return handle_ngsetupresponse(msg.value.ng_setup_resp()); + default: + logger.error("Unhandled successful outcome message: %s", msg.value.type().to_string()); + } + return true; +} + +bool ngap::handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg) +{ + ngsetupresponse = msg; + amf_connected = true; + ng_setup_proc_t::ngsetupresult res; + res.success = true; + ngsetup_proc.trigger(res); + return true; +} + +/******************************************************************************* +/* NGAP connection helpers +********************************************************************************/ + +bool ngap::connect_amf() +{ + using namespace srsran::net_utils; + logger.info("Connecting to AMF %s:%d", args.amf_addr.c_str(), int(AMF_PORT)); + + // Init SCTP socket and bind it + if (not sctp_init_client(&amf_socket, socket_type::seqpacket, args.ngc_bind_addr.c_str())) { + return false; + } + logger.info("SCTP socket opened. fd=%d", amf_socket.fd()); + + // Connect to the AMF address + if (not amf_socket.connect_to(args.amf_addr.c_str(), AMF_PORT, &amf_addr)) { + return false; + } + logger.info("SCTP socket connected with AMF. fd=%d", amf_socket.fd()); + + // Assign a handler to rx AMF packets + auto rx_callback = + [this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags) { + // Defer the handling of AMF packet to eNB stack main thread + handle_amf_rx_msg(std::move(pdu), from, sri, flags); + }; + rx_socket_handler->add_socket_handler(amf_socket.fd(), + srsran::make_sctp_sdu_handler(logger, amf_task_queue, rx_callback)); + + logger.info("SCTP socket established with AMF"); + return true; +} + +bool ngap::setup_ng() +{ + uint32_t tmp32; + uint16_t tmp16; + + uint32_t plmn; + srsran::s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + plmn = htonl(plmn); + + ngap_pdu_c pdu; + pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_NG_SETUP); + ng_setup_request_ies_container& container = pdu.init_msg().value.ng_setup_request().protocol_ies; + global_gnb_id_s& global_gnb_id = container.global_ran_node_id.value.set_global_gnb_id(); + global_gnb_id.plmn_id = tai.plmn_id; + // TODO: when ASN1 is fixed + // global_gnb_id.gnb_id.set_gnb_id().from_number(args.gnb_id); + + // container.ran_node_name_present = true; + // container.ran_node_name.value.from_string(args.gnb_name); + + asn1::bounded_bitstring<22, 32, false, true>& gnb_str = global_gnb_id.gnb_id.set_gnb_id(); + gnb_str.resize(32); + uint8_t buffer[4]; + asn1::bit_ref bref(&buffer[0], sizeof(buffer)); + bref.pack(args.gnb_id, 8); + memcpy(gnb_str.data(), &buffer[0], bref.distance_bytes()); + + // .from_number(args.gnb_id); + + container.ran_node_name_present = true; + if (args.gnb_name.length() >= 150) { + args.gnb_name.resize(150); + } + // container.ran_node_name.value.from_string(args.enb_name); + container.ran_node_name.value.resize(args.gnb_name.size()); + memcpy(&container.ran_node_name.value[0], &args.gnb_name[0], args.gnb_name.size()); + + container.supported_ta_list.value.resize(1); + container.supported_ta_list.value[0].tac = tai.tac; + container.supported_ta_list.value[0].broadcast_plmn_list.resize(1); + container.supported_ta_list.value[0].broadcast_plmn_list[0].plmn_id = tai.plmn_id; + container.supported_ta_list.value[0].broadcast_plmn_list[0].tai_slice_support_list.resize(1); + container.supported_ta_list.value[0].broadcast_plmn_list[0].tai_slice_support_list[0].s_nssai.sst.from_number(1); + + container.default_paging_drx.value.value = asn1::ngap_nr::paging_drx_opts::v256; // Todo: add to args, config file + + return sctp_send_ngap_pdu(pdu, 0, "ngSetupRequest"); +} + +/******************************************************************************* +/* General helpers +********************************************************************************/ + +bool ngap::sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name) +{ + if (not amf_connected and rnti != SRSRAN_INVALID_RNTI) { + logger.error("Aborting %s for rnti=0x%x. Cause: AMF is not connected.", procedure_name, rnti); + return false; + } + + srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer(); + if (buf == nullptr) { + logger.error("Fatal Error: Couldn't allocate buffer for %s.", procedure_name); + return false; + } + asn1::bit_ref bref(buf->msg, buf->get_tailroom()); + if (tx_pdu.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack TX PDU %s", procedure_name); + return false; + } + buf->N_bytes = bref.distance_bytes(); + + // TODO: when we got pcap support + // Save message to PCAP + // if (pcap != nullptr) { + // pcap->write_s1ap(buf->msg, buf->N_bytes); + // } + + if (rnti != SRSRAN_INVALID_RNTI) { + logger.info(buf->msg, buf->N_bytes, "Tx S1AP SDU, %s, rnti=0x%x", procedure_name, rnti); + } else { + logger.info(buf->msg, buf->N_bytes, "Tx S1AP SDU, %s", procedure_name); + } + // TODO: when user list is ready + // uint16_t streamid = rnti == SRSRAN_INVALID_RNTI ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id; + uint16_t streamid = 0; + ssize_t n_sent = sctp_sendmsg(amf_socket.fd(), + buf->msg, + buf->N_bytes, + (struct sockaddr*)&amf_addr, + sizeof(struct sockaddr_in), + htonl(PPID), + 0, + streamid, + 0, + 0); + if (n_sent == -1) { + if (rnti != SRSRAN_INVALID_RNTI) { + logger.error("Error: Failure at Tx S1AP SDU, %s, rnti=0x%x", procedure_name, rnti); + } else { + logger.error("Error: Failure at Tx S1AP SDU, %s", procedure_name); + } + return false; + } + return true; +} + +} // namespace srsenb From f0d2a22cb6bf506e7133db28ac80c264454af3da Mon Sep 17 00:00:00 2001 From: David Rupprecht Date: Wed, 5 May 2021 11:02:08 +0200 Subject: [PATCH 32/50] Fixed band index in rrc --- srsue/src/stack/rrc/rrc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index 98e9e84d6..693c23dea 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -1895,7 +1895,7 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry) ca_mimo_params_ul.supported_mimo_cap_ul_r10_present = false; band_params_r10_s band_params; - band_params.band_eutra_r10 = args.supported_bands[i]; + band_params.band_eutra_r10 = args.supported_bands[k]; band_params.band_params_dl_r10_present = true; band_params.band_params_dl_r10.push_back(ca_mimo_params_dl); band_params.band_params_ul_r10_present = true; From 28847badcf43b4e64d3a012073e2b34f629e5e63 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 5 May 2021 20:52:05 +0100 Subject: [PATCH 33/50] implementation of custom allocator for memory caching. This allocator may be used for unbounded queues --- lib/include/srsran/adt/pool/cached_alloc.h | 109 +++++++++++++++++++++ lib/test/adt/CMakeLists.txt | 4 + lib/test/adt/cached_alloc_test.cc | 103 +++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 lib/include/srsran/adt/pool/cached_alloc.h create mode 100644 lib/test/adt/cached_alloc_test.cc diff --git a/lib/include/srsran/adt/pool/cached_alloc.h b/lib/include/srsran/adt/pool/cached_alloc.h new file mode 100644 index 000000000..aa4e24fb9 --- /dev/null +++ b/lib/include/srsran/adt/pool/cached_alloc.h @@ -0,0 +1,109 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_CACHED_ALLOC_H +#define SRSRAN_CACHED_ALLOC_H + +#include "../intrusive_list.h" +#include "memblock_cache.h" +#include +#include + +namespace srsran { + +template +class cached_alloc : public std::allocator +{ + struct memblock_t : public intrusive_double_linked_list_element<> { + memblock_t(size_t sz) : block_size(sz) {} + size_t block_size; + }; + const size_t min_n = (sizeof(memblock_t) + sizeof(T) - 1) / sizeof(T); + +public: + using value_type = T; + + ~cached_alloc() + { + while (not free_list.empty()) { + memblock_t& b = free_list.front(); + free_list.pop_front(); + size_t n = b.block_size; + b.~memblock_t(); + std::allocator::deallocate(reinterpret_cast(&b), n); + } + } + cached_alloc() = default; + cached_alloc(cached_alloc&& other) noexcept = default; + cached_alloc(const cached_alloc& other) noexcept : cached_alloc() {} + template + explicit cached_alloc(const cached_alloc& other) noexcept : cached_alloc() + { + // start empty, as cached blocks cannot be copied + } + cached_alloc& operator=(const cached_alloc& other) noexcept { return *this; } + cached_alloc& operator=(cached_alloc&& other) noexcept = default; + + T* allocate(size_t n, const void* ptr = nullptr) + { + size_t req_n = std::max(n, min_n); + for (memblock_t& b : free_list) { + if (b.block_size == req_n) { + free_list.pop(&b); + b.~memblock_t(); + return reinterpret_cast(&b); + } + } + return std::allocator::allocate(req_n, ptr); + } + void deallocate(T* p, size_t n) noexcept + { + size_t req_n = std::max(n, min_n); + auto* block = reinterpret_cast(p); + new (block) memblock_t(req_n); + free_list.push_front(block); + } + + template + struct rebind { + using other = cached_alloc; + }; + +private: + intrusive_double_linked_list free_list; +}; + +} // namespace srsran + +template +bool operator==(const srsran::cached_alloc& lhs, const srsran::cached_alloc& rhs) noexcept +{ + return &lhs == &rhs; +} + +template +bool operator!=(const srsran::cached_alloc& lhs, const srsran::cached_alloc& rhs) noexcept +{ + return not(lhs == rhs); +} + +namespace srsran { + +template +using deque = std::deque >; + +template +using queue = std::queue >; + +} // namespace srsran + +#endif // SRSRAN_CACHED_ALLOC_H diff --git a/lib/test/adt/CMakeLists.txt b/lib/test/adt/CMakeLists.txt index 18e4ac1c3..62a5c152e 100644 --- a/lib/test/adt/CMakeLists.txt +++ b/lib/test/adt/CMakeLists.txt @@ -57,3 +57,7 @@ add_test(fsm_test fsm_test) add_executable(optional_test optional_test.cc) target_link_libraries(optional_test srsran_common) add_test(optional_test optional_test) + +add_executable(cached_alloc_test cached_alloc_test.cc) +target_link_libraries(cached_alloc_test srsran_common) +add_test(cached_alloc_test cached_alloc_test) diff --git a/lib/test/adt/cached_alloc_test.cc b/lib/test/adt/cached_alloc_test.cc new file mode 100644 index 000000000..737bd368f --- /dev/null +++ b/lib/test/adt/cached_alloc_test.cc @@ -0,0 +1,103 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/adt/pool/cached_alloc.h" +#include "srsran/common/test_common.h" +#include + +void test_cached_deque_basic_operations() +{ + srsran::deque my_deque; + TESTASSERT(my_deque.empty() and my_deque.size() == 0); + my_deque.push_front(0); + my_deque.push_back(1); + TESTASSERT(my_deque.front() == 0 and my_deque.back() == 1); + TESTASSERT(my_deque.size() == 2); + + srsran::deque my_deque2(my_deque); + TESTASSERT(my_deque == my_deque2); + my_deque.clear(); + TESTASSERT(my_deque != my_deque2); + TESTASSERT(my_deque2.size() == 2 and my_deque2.back() == 1); + TESTASSERT(my_deque.empty()); + + my_deque = my_deque2; + TESTASSERT(my_deque == my_deque2); + my_deque2.clear(); + TESTASSERT(my_deque2.empty()); + + my_deque2 = std::move(my_deque); + TESTASSERT(my_deque.empty() and my_deque2.size() == 2); +} + +struct C { + C() = default; + C(C&&) noexcept = default; + C(const C&) = delete; + C& operator=(C&&) noexcept = default; + C& operator=(const C&) = delete; + + bool operator==(const C& other) { return true; } +}; + +void test_cached_queue_basic_operations() +{ + srsran::queue my_queue; + TESTASSERT(my_queue.empty()); + my_queue.push(C{}); + TESTASSERT(my_queue.size() == 1); + + srsran::queue my_queue2(std::move(my_queue)); + TESTASSERT(my_queue2.size() == 1); +} + +void cached_deque_benchmark() +{ + using std::chrono::high_resolution_clock; + using std::chrono::microseconds; + + srsran::deque my_deque; + std::deque std_deque; + high_resolution_clock::time_point tp; + + size_t N = 10000000, n_elems = 10; + + for (size_t i = 0; i < n_elems; ++i) { + my_deque.push_back(i); + std_deque.push_back(i); + } + + // NOTE: this benchmark doesnt account for when memory is fragmented + tp = high_resolution_clock::now(); + for (size_t i = n_elems; i < N; ++i) { + std_deque.push_back(i); + std_deque.pop_front(); + } + microseconds t_std = std::chrono::duration_cast(high_resolution_clock::now() - tp); + + tp = high_resolution_clock::now(); + for (size_t i = n_elems; i < N; ++i) { + my_deque.push_back(i); + my_deque.pop_front(); + } + microseconds t_cached = std::chrono::duration_cast(high_resolution_clock::now() - tp); + + fmt::print("Time elapsed: cached alloc={} usec, std alloc={} usec", t_cached.count(), t_std.count()); +} + +int main() +{ + test_cached_deque_basic_operations(); + test_cached_queue_basic_operations(); + cached_deque_benchmark(); + return 0; +} \ No newline at end of file From b619a2b649659c391ffc1eb7c0e53d86ce4b13b8 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 5 May 2021 20:57:52 +0100 Subject: [PATCH 34/50] application of new cache allocator to deques in scheduler --- srsenb/hdr/stack/mac/sched_carrier.h | 7 ++++--- srsenb/hdr/stack/mac/sched_ue_ctrl/sched_lch.h | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/srsenb/hdr/stack/mac/sched_carrier.h b/srsenb/hdr/stack/mac/sched_carrier.h index 229892ca9..6fada4517 100644 --- a/srsenb/hdr/stack/mac/sched_carrier.h +++ b/srsenb/hdr/stack/mac/sched_carrier.h @@ -15,6 +15,7 @@ #include "sched.h" #include "schedulers/sched_base.h" +#include "srsran/adt/pool/cached_alloc.h" #include "srsran/srslog/srslog.h" namespace srsenb { @@ -122,9 +123,9 @@ private: const sched_cell_params_t* cc_cfg = nullptr; sched_ue_list* ue_db = nullptr; - std::deque pending_rars; - uint32_t rar_aggr_level = 2; - static const uint32_t PRACH_RAR_OFFSET = 3; // TS 36.321 Sec. 5.1.4 + srsran::deque pending_rars; + uint32_t rar_aggr_level = 2; + static const uint32_t PRACH_RAR_OFFSET = 3; // TS 36.321 Sec. 5.1.4 }; } // namespace srsenb diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_lch.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_lch.h index 2eff87c9f..90b79fd93 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_lch.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_lch.h @@ -13,10 +13,10 @@ #ifndef SRSRAN_SCHED_LCH_H #define SRSRAN_SCHED_LCH_H +#include "srsran/adt/pool/cached_alloc.h" #include "srsran/interfaces/sched_interface.h" #include "srsran/mac/pdu.h" #include "srsran/srslog/srslog.h" -#include namespace srsenb { @@ -59,7 +59,7 @@ public: // Control Element Command queue using ce_cmd = srsran::dl_sch_lcid; - std::deque pending_ces; + srsran::deque pending_ces; private: struct ue_bearer_t { From cd51537234a47ffcd4526ca6881e3fbb34f1c0bc Mon Sep 17 00:00:00 2001 From: Francisco Date: Thu, 6 May 2021 13:47:43 +0100 Subject: [PATCH 35/50] add comment explaining the use case of cached_alloc --- lib/include/srsran/adt/pool/cached_alloc.h | 9 ++++++++- lib/test/adt/cached_alloc_test.cc | 19 ++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/include/srsran/adt/pool/cached_alloc.h b/lib/include/srsran/adt/pool/cached_alloc.h index aa4e24fb9..64ac1f531 100644 --- a/lib/include/srsran/adt/pool/cached_alloc.h +++ b/lib/include/srsran/adt/pool/cached_alloc.h @@ -20,11 +20,18 @@ namespace srsran { +/** + * Custom Allocator that caches deallocated memory blocks in a stack to be reused in future allocations. + * This minimizes the number of new/delete calls, when the rate of insertions/removals match (e.g. a queue) + * This allocator is not thread-safe. It assumes the container is being used in a single-threaded environment, + * or being mutexed when altered, which is a reasonable assumption + * @tparam T object type + */ template class cached_alloc : public std::allocator { struct memblock_t : public intrusive_double_linked_list_element<> { - memblock_t(size_t sz) : block_size(sz) {} + explicit memblock_t(size_t sz) : block_size(sz) {} size_t block_size; }; const size_t min_n = (sizeof(memblock_t) + sizeof(T) - 1) / sizeof(T); diff --git a/lib/test/adt/cached_alloc_test.cc b/lib/test/adt/cached_alloc_test.cc index 737bd368f..4ef63b4a8 100644 --- a/lib/test/adt/cached_alloc_test.cc +++ b/lib/test/adt/cached_alloc_test.cc @@ -65,33 +65,34 @@ void cached_deque_benchmark() using std::chrono::high_resolution_clock; using std::chrono::microseconds; - srsran::deque my_deque; - std::deque std_deque; + srsran::queue my_deque; + std::queue std_deque; high_resolution_clock::time_point tp; size_t N = 10000000, n_elems = 10; for (size_t i = 0; i < n_elems; ++i) { - my_deque.push_back(i); - std_deque.push_back(i); + my_deque.push(i); + std_deque.push(i); } // NOTE: this benchmark doesnt account for when memory is fragmented tp = high_resolution_clock::now(); for (size_t i = n_elems; i < N; ++i) { - std_deque.push_back(i); - std_deque.pop_front(); + std_deque.push(i); + std_deque.pop(); } microseconds t_std = std::chrono::duration_cast(high_resolution_clock::now() - tp); tp = high_resolution_clock::now(); for (size_t i = n_elems; i < N; ++i) { - my_deque.push_back(i); - my_deque.pop_front(); + my_deque.push(i); + my_deque.pop(); } microseconds t_cached = std::chrono::duration_cast(high_resolution_clock::now() - tp); - fmt::print("Time elapsed: cached alloc={} usec, std alloc={} usec", t_cached.count(), t_std.count()); + fmt::print("Time elapsed: cached alloc={} usec, std alloc={} usec\n", t_cached.count(), t_std.count()); + fmt::print("queue sizes: {} {}\n", my_deque.size(), std_deque.size()); } int main() From c6b9c12ba207d87337b832d8dde9ec1c23899e72 Mon Sep 17 00:00:00 2001 From: Francisco Date: Tue, 4 May 2021 19:44:40 +0100 Subject: [PATCH 36/50] gtpu, feature - added the ability to configure gtpu indirect tunnel timeout value. This parameter is useful in the case that the end marker is not received --- .../srsran/interfaces/enb_gtpu_interfaces.h | 9 +++++ srsenb/enb.conf.example | 2 ++ srsenb/hdr/enb.h | 1 + srsenb/hdr/stack/enb_stack_base.h | 1 + srsenb/hdr/stack/upper/gtpu.h | 14 +++----- srsenb/src/main.cc | 1 + srsenb/src/stack/enb_stack_lte.cc | 15 +++++---- srsenb/src/stack/upper/gtpu.cc | 33 +++++++++---------- srsenb/test/upper/gtpu_test.cc | 8 +++-- 9 files changed, 50 insertions(+), 34 deletions(-) diff --git a/lib/include/srsran/interfaces/enb_gtpu_interfaces.h b/lib/include/srsran/interfaces/enb_gtpu_interfaces.h index fd1c3ace7..a09aaf00b 100644 --- a/lib/include/srsran/interfaces/enb_gtpu_interfaces.h +++ b/lib/include/srsran/interfaces/enb_gtpu_interfaces.h @@ -18,6 +18,15 @@ namespace srsenb { +struct gtpu_args_t { + std::string gtp_bind_addr; + std::string mme_addr; + std::string embms_m1u_multiaddr; + std::string embms_m1u_if_addr; + bool embms_enable = false; + uint32_t indirect_tunnel_timeout_msec = 2000; +}; + // GTPU interface for PDCP class gtpu_interface_pdcp { diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 19716f130..507aa86a9 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -307,6 +307,7 @@ enable = false # nof_prealloc_ues: Number of UE memory resources to preallocate during eNB initialization for faster UE creation (Default 8) # eea_pref_list: Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1). # eia_pref_list: Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0). +# gtpu_tunnel_timeout: Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU. # ##################################################################### [expert] @@ -331,3 +332,4 @@ enable = false #nof_prealloc_ues = 8 #eea_pref_list = EEA0, EEA2, EEA1 #eia_pref_list = EIA2, EIA1, EIA0 +#gtpu_tunnel_timeout = 2000 diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h index b38d8397b..71b897974 100644 --- a/srsenb/hdr/enb.h +++ b/srsenb/hdr/enb.h @@ -95,6 +95,7 @@ struct general_args_t { std::string eea_pref_list; uint32_t max_mac_dl_kos; uint32_t max_mac_ul_kos; + uint32_t gtpu_indirect_tunnel_timeout; }; struct all_args_t { diff --git a/srsenb/hdr/stack/enb_stack_base.h b/srsenb/hdr/stack/enb_stack_base.h index a8d9b5b2a..0d92b2d88 100644 --- a/srsenb/hdr/stack/enb_stack_base.h +++ b/srsenb/hdr/stack/enb_stack_base.h @@ -70,6 +70,7 @@ typedef struct { typedef struct { std::string type; uint32_t sync_queue_size; // Max allowed difference between PHY and Stack clocks (in TTI) + uint32_t gtpu_indirect_tunnel_timeout_msec; mac_args_t mac; s1ap_args_t s1ap; pcap_args_t mac_pcap; diff --git a/srsenb/hdr/stack/upper/gtpu.h b/srsenb/hdr/stack/upper/gtpu.h index 2682eb3ae..e6304fbbf 100644 --- a/srsenb/hdr/stack/upper/gtpu.h +++ b/srsenb/hdr/stack/upper/gtpu.h @@ -90,7 +90,7 @@ public: using ue_lcid_tunnel_list = srsran::bounded_vector; explicit gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger); - void init(pdcp_interface_gtpu* pdcp_); + void init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_); bool has_teid(uint32_t teid) const { return tunnels.contains(teid); } const tunnel* find_tunnel(uint32_t teid); @@ -116,7 +116,8 @@ private: using tunnel_ctxt_it = typename tunnel_list_t::iterator; srsran::task_sched_handle task_sched; - pdcp_interface_gtpu* pdcp = nullptr; + const gtpu_args_t* gtpu_args = nullptr; + pdcp_interface_gtpu* pdcp = nullptr; srslog::basic_logger& logger; srsran::static_circular_map ue_teidin_db; @@ -134,12 +135,7 @@ public: srsran::socket_manager_itf* rx_socket_handler_); ~gtpu(); - int init(std::string gtp_bind_addr_, - std::string mme_addr_, - std::string m1u_multiaddr_, - std::string m1u_if_addr_, - pdcp_interface_gtpu* pdcp_, - bool enable_mbsfn = false); + int init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_); void stop(); // gtpu_interface_rrc @@ -168,7 +164,7 @@ private: srsran::socket_manager_itf* rx_socket_handler = nullptr; srsran::task_queue_handle gtpu_queue; - bool enable_mbsfn = false; + gtpu_args_t args; std::string gtp_bind_addr; std::string mme_addr; srsenb::pdcp_interface_gtpu* pdcp = nullptr; diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index e828ba721..446e60847 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -217,6 +217,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.nof_prealloc_ues", bpo::value(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate during eNB initialization") ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release") ("expert.max_mac_ul_kos", bpo::value(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release") + ("expert.gtpu_tunnel_timeout", bpo::value(&args->stack.gtpu_indirect_tunnel_timeout_msec)->default_value(2000), "Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU.") // eMBMS section diff --git a/srsenb/src/stack/enb_stack_lte.cc b/srsenb/src/stack/enb_stack_lte.cc index 7a193f208..66fa3915a 100644 --- a/srsenb/src/stack/enb_stack_lte.cc +++ b/srsenb/src/stack/enb_stack_lte.cc @@ -133,12 +133,15 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) stack_logger.error("Couldn't initialize S1AP"); return SRSRAN_ERROR; } - if (gtpu.init(args.s1ap.gtp_bind_addr, - args.s1ap.mme_addr, - args.embms.m1u_multiaddr, - args.embms.m1u_if_addr, - &pdcp, - args.embms.enable)) { + + gtpu_args_t gtpu_args; + gtpu_args.embms_enable = args.embms.enable; + gtpu_args.embms_m1u_multiaddr = args.embms.m1u_multiaddr; + gtpu_args.embms_m1u_if_addr = args.embms.m1u_if_addr; + gtpu_args.mme_addr = args.s1ap.mme_addr; + gtpu_args.gtp_bind_addr = args.s1ap.gtp_bind_addr; + gtpu_args.indirect_tunnel_timeout_msec = args.gtpu_indirect_tunnel_timeout_msec; + if (gtpu.init(gtpu_args, &pdcp) != SRSRAN_SUCCESS) { stack_logger.error("Couldn't initialize GTPU"); return SRSRAN_ERROR; } diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index fa9a5a992..1a0d242d3 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -36,9 +36,10 @@ gtpu_tunnel_manager::gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, logger(logger), task_sched(task_sched_), tunnels(1) {} -void gtpu_tunnel_manager::init(pdcp_interface_gtpu* pdcp_) +void gtpu_tunnel_manager::init(const gtpu_args_t& args, pdcp_interface_gtpu* pdcp_) { - pdcp = pdcp_; + gtpu_args = &args; + pdcp = pdcp_; } const gtpu_tunnel_manager::tunnel* gtpu_tunnel_manager::find_tunnel(uint32_t teid) @@ -239,10 +240,13 @@ void gtpu_tunnel_manager::set_tunnel_priority(uint32_t before_teid, uint32_t aft } }; - // Schedule auto-removal of this indirect tunnel + // Schedule auto-removal of the indirect tunnel in case the End Marker is not received + // TS 36.300 - On detection of the "end marker", the target eNB may also initiate the release of the data forwarding + // resource. However, the release of the data forwarding resource is implementation dependent and could + // also be based on other mechanisms (e.g. timer-based mechanism). before_tun.rx_timer = task_sched.get_unique_timer(); - before_tun.rx_timer.set(500, [this, before_teid](uint32_t tid) { - // This will self-destruct the callback object + before_tun.rx_timer.set(gtpu_args->indirect_tunnel_timeout_msec, [this, before_teid](uint32_t tid) { + // Note: This will self-destruct the callback object remove_tunnel(before_teid); }); before_tun.rx_timer.run(); @@ -323,18 +327,14 @@ gtpu::~gtpu() stop(); } -int gtpu::init(std::string gtp_bind_addr_, - std::string mme_addr_, - std::string m1u_multiaddr_, - std::string m1u_if_addr_, - srsenb::pdcp_interface_gtpu* pdcp_, - bool enable_mbsfn_) +int gtpu::init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_) { + args = gtpu_args; pdcp = pdcp_; - gtp_bind_addr = gtp_bind_addr_; - mme_addr = mme_addr_; + gtp_bind_addr = gtpu_args.gtp_bind_addr; + mme_addr = gtpu_args.mme_addr; - tunnels.init(pdcp); + tunnels.init(args, pdcp); char errbuf[128] = {}; @@ -374,9 +374,8 @@ int gtpu::init(std::string gtp_bind_addr_, rx_socket_handler->add_socket_handler(fd, srsran::make_sdu_handler(logger, gtpu_queue, rx_callback)); // Start MCH socket if enabled - enable_mbsfn = enable_mbsfn_; - if (enable_mbsfn) { - if (not m1u.init(m1u_multiaddr_, m1u_if_addr_)) { + if (args.embms_enable) { + if (not m1u.init(args.embms_m1u_multiaddr, args.embms_m1u_if_addr)) { return SRSRAN_ERROR; } } diff --git a/srsenb/test/upper/gtpu_test.cc b/srsenb/test/upper/gtpu_test.cc index a5f8bded3..15eaf1827 100644 --- a/srsenb/test/upper/gtpu_test.cc +++ b/srsenb/test/upper/gtpu_test.cc @@ -232,8 +232,12 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) dummy_socket_manager senb_rx_sockets, tenb_rx_sockets; srsenb::gtpu senb_gtpu(&task_sched, logger1, &senb_rx_sockets), tenb_gtpu(&task_sched, logger2, &tenb_rx_sockets); pdcp_tester senb_pdcp, tenb_pdcp; - senb_gtpu.init(senb_addr_str, sgw_addr_str, "", "", &senb_pdcp, false); - tenb_gtpu.init(tenb_addr_str, sgw_addr_str, "", "", &tenb_pdcp, false); + gtpu_args_t gtpu_args; + gtpu_args.gtp_bind_addr = senb_addr_str; + gtpu_args.mme_addr = sgw_addr_str; + senb_gtpu.init(gtpu_args, &senb_pdcp); + gtpu_args.gtp_bind_addr = tenb_addr_str; + tenb_gtpu.init(gtpu_args, &tenb_pdcp); // create tunnels MME-SeNB and MME-TeNB uint32_t senb_teid_in = senb_gtpu.add_bearer(rnti, drb1, sgw_addr, sgw_teidout1).value(); From 15484e9472b8190d303e15dd7d53e21babcde428 Mon Sep 17 00:00:00 2001 From: Francisco Date: Tue, 4 May 2021 21:50:13 +0100 Subject: [PATCH 37/50] rrc - make t304 configurable via rr.conf --- .../interfaces/enb_rrc_interface_types.h | 31 ++++++++++--------- srsenb/hdr/parser.h | 6 ++++ srsenb/rr.conf.example | 1 + srsenb/src/enb_cfg_parser.cc | 13 ++++++++ srsenb/src/stack/rrc/rrc_mobility.cc | 2 +- srsenb/src/stack/upper/gtpu.cc | 3 ++ srsenb/test/upper/gtpu_test.cc | 4 ++- 7 files changed, 43 insertions(+), 17 deletions(-) diff --git a/lib/include/srsran/interfaces/enb_rrc_interface_types.h b/lib/include/srsran/interfaces/enb_rrc_interface_types.h index 093bcb343..182e52824 100644 --- a/lib/include/srsran/interfaces/enb_rrc_interface_types.h +++ b/lib/include/srsran/interfaces/enb_rrc_interface_types.h @@ -47,21 +47,22 @@ struct rrc_meas_cfg_t { // Cell/Sector configuration struct cell_cfg_t { - uint32_t rf_port; - uint32_t cell_id; - uint16_t tac; - uint32_t pci; - uint16_t root_seq_idx; - uint32_t dl_earfcn; - double dl_freq_hz; - uint32_t ul_earfcn; - double ul_freq_hz; - int target_pucch_sinr_db; - int target_pusch_sinr_db; - uint32_t initial_dl_cqi; - bool enable_phr_handling; - std::vector scell_list; - rrc_meas_cfg_t meas_cfg; + uint32_t rf_port; + uint32_t cell_id; + uint16_t tac; + uint32_t pci; + uint16_t root_seq_idx; + uint32_t dl_earfcn; + double dl_freq_hz; + uint32_t ul_earfcn; + double ul_freq_hz; + int target_pucch_sinr_db; + int target_pusch_sinr_db; + uint32_t initial_dl_cqi; + bool enable_phr_handling; + asn1::rrc::mob_ctrl_info_s::t304_e_ t304; + std::vector scell_list; + rrc_meas_cfg_t meas_cfg; }; typedef std::vector cell_list_t; diff --git a/srsenb/hdr/parser.h b/srsenb/hdr/parser.h index cea034e04..fb71ca7a3 100644 --- a/srsenb/hdr/parser.h +++ b/srsenb/hdr/parser.h @@ -466,6 +466,12 @@ int opt_number_to_enum(EnumType& enum_val, bool& presence_flag, Setting& root, c return parse_opt_field(enum_val, root, name, number_to_enum, &presence_flag); } +template +int default_number_to_enum(EnumType& enum_val, Setting& root, const char* name, typename EnumType::options default_val) +{ + return parse_default_field(enum_val, root, name, EnumType(default_val), number_to_enum); +} + } // namespace asn1_parsers } // namespace srsenb diff --git a/srsenb/rr.conf.example b/srsenb/rr.conf.example index 1390f179f..37db345cd 100644 --- a/srsenb/rr.conf.example +++ b/srsenb/rr.conf.example @@ -65,6 +65,7 @@ cell_list = // target_pusch_sinr = -1; // target_pucch_sinr = -1; // allowed_meas_bw = 6; + // t304 = 2000; // in msec. possible values: 50, 100, 150, 200, 500, 1000, 2000 // CA cells scell_list = ( diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 4cdf72269..110bb564f 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -44,6 +44,17 @@ using namespace asn1::rrc; namespace srsenb { +template +bool contains_value(T value, const std::initializer_list& list) +{ + for (auto& v : list) { + if (v == value) { + return true; + } + } + return false; +} + bool sib_is_present(const sched_info_list_l& l, sib_type_e sib_num) { for (uint32_t i = 0; i < l.size(); i++) { @@ -754,6 +765,8 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root) HANDLEPARSERCODE(parse_default_field(cell_cfg.enable_phr_handling, cellroot, "enable_phr_handling", false)); parse_default_field(cell_cfg.meas_cfg.allowed_meas_bw, cellroot, "allowed_meas_bw", 6u); srsran_assert(srsran::is_lte_cell_nof_prb(cell_cfg.meas_cfg.allowed_meas_bw), "Invalid measurement Bandwidth"); + HANDLEPARSERCODE(asn1_parsers::default_number_to_enum( + cell_cfg.t304, cellroot, "t304", asn1::rrc::mob_ctrl_info_s::t304_opts::ms2000)); if (cellroot.exists("ho_active") and cellroot["ho_active"]) { HANDLEPARSERCODE(parse_meas_cell_list(&cell_cfg.meas_cfg, cellroot["meas_cell_list"])); diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 95bf9d8ee..16b9a4e77 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -491,7 +491,7 @@ void rrc::ue::rrc_mobility::fill_mobility_reconf_common(asn1::rrc::dl_dcch_msg_s recfg_r8.mob_ctrl_info_present = true; auto& mob_info = recfg_r8.mob_ctrl_info; mob_info.target_pci = target_cell.cell_cfg.pci; - mob_info.t304.value = mob_ctrl_info_s::t304_opts::ms2000; // TODO: make it reconfigurable + mob_info.t304 = target_cell.cell_cfg.t304; mob_info.new_ue_id.from_number(rrc_ue->rnti); mob_info.rr_cfg_common.pusch_cfg_common = target_cell.sib2.rr_cfg_common.pusch_cfg_common; mob_info.rr_cfg_common.prach_cfg.root_seq_idx = target_cell.sib2.rr_cfg_common.prach_cfg.root_seq_idx; diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index 1a0d242d3..d873e5e86 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -247,6 +247,9 @@ void gtpu_tunnel_manager::set_tunnel_priority(uint32_t before_teid, uint32_t aft before_tun.rx_timer = task_sched.get_unique_timer(); before_tun.rx_timer.set(gtpu_args->indirect_tunnel_timeout_msec, [this, before_teid](uint32_t tid) { // Note: This will self-destruct the callback object + logger.info("Forwarding tunnel " TEID_IN_FMT "being closed after timeout=%d msec", + before_teid, + gtpu_args->indirect_tunnel_timeout_msec); remove_tunnel(before_teid); }); before_tun.rx_timer.run(); diff --git a/srsenb/test/upper/gtpu_test.cc b/srsenb/test/upper/gtpu_test.cc index 15eaf1827..ca2092a11 100644 --- a/srsenb/test/upper/gtpu_test.cc +++ b/srsenb/test/upper/gtpu_test.cc @@ -155,8 +155,10 @@ void test_gtpu_tunnel_manager() uint32_t sgw_addr = ntohl(sgw_sockaddr.sin_addr.s_addr); const uint32_t drb1_lcid = 3; srsran::task_scheduler task_sched; + gtpu_args_t gtpu_args = {}; gtpu_tunnel_manager tunnels(&task_sched, srslog::fetch_basic_logger("GTPU")); + tunnels.init(gtpu_args, nullptr); TESTASSERT(tunnels.find_tunnel(0) == nullptr); TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty()); TESTASSERT(tunnels.find_rnti_tunnels(0x46) == nullptr); @@ -324,7 +326,7 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) TESTASSERT(tenb_pdcp.last_sdu == nullptr); if (event == tunnel_test_event::wait_end_marker_timeout) { // TEST: EndMarker does not reach TeNB, but there is a timeout that will resume the new GTPU tunnel - for (size_t i = 0; i < 1000; ++i) { + for (size_t i = 0; i < gtpu_args.indirect_tunnel_timeout_msec + 1; ++i) { task_sched.tic(); } } else { From c5396155adc6ea11fd264d98d837ce6a5692064a Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 5 May 2021 13:31:42 +0100 Subject: [PATCH 38/50] set default gtpu tunnel close timeout to infinity --- .../srsran/interfaces/enb_gtpu_interfaces.h | 2 +- srsenb/enb.conf.example | 4 ++-- srsenb/src/main.cc | 2 +- srsenb/src/stack/upper/gtpu.cc | 20 ++++++++++--------- srsenb/test/upper/gtpu_test.cc | 9 +++++---- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/include/srsran/interfaces/enb_gtpu_interfaces.h b/lib/include/srsran/interfaces/enb_gtpu_interfaces.h index a09aaf00b..c88edac4d 100644 --- a/lib/include/srsran/interfaces/enb_gtpu_interfaces.h +++ b/lib/include/srsran/interfaces/enb_gtpu_interfaces.h @@ -24,7 +24,7 @@ struct gtpu_args_t { std::string embms_m1u_multiaddr; std::string embms_m1u_if_addr; bool embms_enable = false; - uint32_t indirect_tunnel_timeout_msec = 2000; + uint32_t indirect_tunnel_timeout_msec = 0; }; // GTPU interface for PDCP diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 507aa86a9..ce64a9dae 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -307,7 +307,7 @@ enable = false # nof_prealloc_ues: Number of UE memory resources to preallocate during eNB initialization for faster UE creation (Default 8) # eea_pref_list: Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1). # eia_pref_list: Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0). -# gtpu_tunnel_timeout: Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU. +# gtpu_tunnel_timeout: Time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU (0 for no timer). # ##################################################################### [expert] @@ -332,4 +332,4 @@ enable = false #nof_prealloc_ues = 8 #eea_pref_list = EEA0, EEA2, EEA1 #eia_pref_list = EIA2, EIA1, EIA0 -#gtpu_tunnel_timeout = 2000 +#gtpu_tunnel_timeout = 0 diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 446e60847..a4e56f6cb 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -217,7 +217,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.nof_prealloc_ues", bpo::value(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate during eNB initialization") ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release") ("expert.max_mac_ul_kos", bpo::value(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release") - ("expert.gtpu_tunnel_timeout", bpo::value(&args->stack.gtpu_indirect_tunnel_timeout_msec)->default_value(2000), "Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU.") + ("expert.gtpu_tunnel_timeout", bpo::value(&args->stack.gtpu_indirect_tunnel_timeout_msec)->default_value(0), "Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU. (0 for infinity)") // eMBMS section diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index d873e5e86..0fbeba79a 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -244,15 +244,17 @@ void gtpu_tunnel_manager::set_tunnel_priority(uint32_t before_teid, uint32_t aft // TS 36.300 - On detection of the "end marker", the target eNB may also initiate the release of the data forwarding // resource. However, the release of the data forwarding resource is implementation dependent and could // also be based on other mechanisms (e.g. timer-based mechanism). - before_tun.rx_timer = task_sched.get_unique_timer(); - before_tun.rx_timer.set(gtpu_args->indirect_tunnel_timeout_msec, [this, before_teid](uint32_t tid) { - // Note: This will self-destruct the callback object - logger.info("Forwarding tunnel " TEID_IN_FMT "being closed after timeout=%d msec", - before_teid, - gtpu_args->indirect_tunnel_timeout_msec); - remove_tunnel(before_teid); - }); - before_tun.rx_timer.run(); + if (gtpu_args->indirect_tunnel_timeout_msec > 0) { + before_tun.rx_timer = task_sched.get_unique_timer(); + before_tun.rx_timer.set(gtpu_args->indirect_tunnel_timeout_msec, [this, before_teid](uint32_t tid) { + // Note: This will self-destruct the callback object + logger.info("Forwarding tunnel " TEID_IN_FMT "being closed after timeout=%d msec", + before_teid, + gtpu_args->indirect_tunnel_timeout_msec); + remove_tunnel(before_teid); + }); + before_tun.rx_timer.run(); + } } void gtpu_tunnel_manager::handle_rx_pdcp_sdu(uint32_t teid) diff --git a/srsenb/test/upper/gtpu_test.cc b/srsenb/test/upper/gtpu_test.cc index ca2092a11..020667ddd 100644 --- a/srsenb/test/upper/gtpu_test.cc +++ b/srsenb/test/upper/gtpu_test.cc @@ -209,6 +209,8 @@ enum class tunnel_test_event { success, wait_end_marker_timeout }; int test_gtpu_direct_tunneling(tunnel_test_event event) { + std::random_device rd; + std::mt19937 g(rd()); srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST"); logger.info("\n\n**** Test GTPU Direct Tunneling ****\n"); uint16_t rnti = 0x46, rnti2 = 0x50; @@ -235,8 +237,9 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) srsenb::gtpu senb_gtpu(&task_sched, logger1, &senb_rx_sockets), tenb_gtpu(&task_sched, logger2, &tenb_rx_sockets); pdcp_tester senb_pdcp, tenb_pdcp; gtpu_args_t gtpu_args; - gtpu_args.gtp_bind_addr = senb_addr_str; - gtpu_args.mme_addr = sgw_addr_str; + gtpu_args.gtp_bind_addr = senb_addr_str; + gtpu_args.mme_addr = sgw_addr_str; + gtpu_args.indirect_tunnel_timeout_msec = std::uniform_int_distribution{500, 2000}(g); senb_gtpu.init(gtpu_args, &senb_pdcp); gtpu_args.gtp_bind_addr = tenb_addr_str; tenb_gtpu.init(gtpu_args, &tenb_pdcp); @@ -262,8 +265,6 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) props.forward_from_teidin = senb_teid_in; senb_gtpu.add_bearer(rnti, drb1, tenb_addr, dl_tenb_teid_in, &props); - std::random_device rd; - std::mt19937 g(rd()); std::vector data_vec(10); std::iota(data_vec.begin(), data_vec.end(), 0); std::vector encoded_data; From 7c2e841eb40d0ca0e42d80f54793348addc660b3 Mon Sep 17 00:00:00 2001 From: Francisco Date: Mon, 3 May 2021 16:14:20 +0100 Subject: [PATCH 39/50] gtpu bugfix - call gtpu rem bearer when erab is removed --- srsenb/src/stack/rrc/rrc_bearer_cfg.cc | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc index e0ac3b6b8..594a86af6 100644 --- a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc +++ b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc @@ -304,6 +304,8 @@ int bearer_cfg_handler::release_erab(uint8_t erab_id) srsran::rem_rrc_obj_id(current_drbs, drb_id); + rem_gtpu_bearer(erab_id); + erabs.erase(it); erab_info_list.erase(erab_id); @@ -312,8 +314,6 @@ int bearer_cfg_handler::release_erab(uint8_t erab_id) void bearer_cfg_handler::release_erabs() { - // TODO: notify GTPU layer for each ERAB - erabs.clear(); while (not erabs.empty()) { release_erab(erabs.begin()->first); } @@ -380,13 +380,7 @@ srsran::expected bearer_cfg_handler::add_gtpu_bearer(uint32_t void bearer_cfg_handler::rem_gtpu_bearer(uint32_t erab_id) { - auto it = erabs.find(erab_id); - if (it != erabs.end()) { - // Map e.g. E-RAB 5 to LCID 3 (==DRB1) - gtpu->rem_bearer(rnti, erab_id - 2); - } else { - logger->error("Removing erab_id=%d to GTPU\n", erab_id); - } + gtpu->rem_bearer(rnti, erab_id - 2); } void bearer_cfg_handler::fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg) From 46bd8845ba34193ba2cf9805f7114340cbcbb7ac Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 5 May 2021 14:53:55 +0100 Subject: [PATCH 40/50] fix gtpu bearer removal during handover --- srsenb/src/stack/upper/gtpu.cc | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index 0fbeba79a..2e9afec35 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -152,22 +152,20 @@ bool gtpu_tunnel_manager::remove_tunnel(uint32_t teidin) bool gtpu_tunnel_manager::remove_bearer(uint16_t rnti, uint32_t lcid) { - srsran::span to_rem = find_rnti_lcid_tunnels(rnti, lcid); - if (to_rem.empty()) { - return false; - } logger.info("Removing rnti=0x%x,lcid=%d", rnti, lcid); - - for (lcid_tunnel& lcid_tun : to_rem) { - bool ret = tunnels.erase(lcid_tun.teid); + bool removed = false; + for (srsran::span to_rem = find_rnti_lcid_tunnels(rnti, lcid); not to_rem.empty(); + to_rem = find_rnti_lcid_tunnels(rnti, lcid)) { + uint32_t teid = to_rem.front().teid; + bool ret = remove_tunnel(teid); srsran_expect(ret, "Inconsistency detected between internal data structures for rnti=0x%x,lcid=%d," TEID_IN_FMT, rnti, lcid, - lcid_tun.teid); + teid); + removed |= ret; } - ue_teidin_db[rnti].erase(to_rem.begin(), to_rem.end()); - return true; + return removed; } bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti) From 0b91598e361bb4137eaad96709d82dee6f9a6008 Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Mon, 3 May 2021 11:01:09 +0200 Subject: [PATCH 41/50] Move mlockall() to main() in srsUE and srsENB --- srsenb/src/main.cc | 5 +++++ srsenb/src/phy/phy.cc | 3 --- srsue/src/main.cc | 5 +++++ srsue/src/phy/phy.cc | 2 -- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index a4e56f6cb..c8a842dc7 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "srsran/common/common_helper.h" @@ -534,6 +535,10 @@ int main(int argc, char* argv[]) event_logger::configure(json_channel); } + if (mlockall((uint32_t)MCL_CURRENT | (uint32_t)MCL_FUTURE) == -1) { + srsran::console("Failed to `mlockall`: {}", errno); + } + // Create eNB unique_ptr enb{new srsenb::enb(srslog::get_default_sink())}; if (enb->init(args) != SRSRAN_SUCCESS) { diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index f1f8cb535..8bbe4f712 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -15,7 +15,6 @@ #include #include #include -#include #include #include "srsenb/hdr/phy/phy.h" @@ -105,8 +104,6 @@ int phy::init(const phy_args_t& args, return SRSRAN_ERROR; } - mlockall((uint32_t)MCL_CURRENT | (uint32_t)MCL_FUTURE); - // Add PHY lib log. srslog::basic_levels log_lvl = srslog::str_to_basic_level(args.log.phy_lib_level); diff --git a/srsue/src/main.cc b/srsue/src/main.cc index f6f993de5..215b25b9a 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include extern bool simulate_rlf; @@ -675,6 +676,10 @@ int main(int argc, char* argv[]) srsran::check_scaling_governor(args.rf.device_name); + if (mlockall((uint32_t)MCL_CURRENT | (uint32_t)MCL_FUTURE) == -1) { + fprintf(stderr, "Failed to `mlockall`: %d", errno); + } + // Create UE instance. srsue::ue ue; if (ue.init(args)) { diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 694fa6444..f0116863a 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -11,7 +11,6 @@ */ #include -#include #include "srsran/common/band_helper.h" #include "srsran/common/standard_streams.h" @@ -104,7 +103,6 @@ int phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran:: int phy::init(const phy_args_t& args_) { std::unique_lock lock(config_mutex); - mlockall(MCL_CURRENT | MCL_FUTURE); args = args_; From c51ff0c9f9bc9b8cdb65316c8846a40f1fe1e23e Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Tue, 4 May 2021 20:49:04 +0200 Subject: [PATCH 42/50] Added safe exponential moving average filter --- lib/include/srsran/phy/utils/vector.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/include/srsran/phy/utils/vector.h b/lib/include/srsran/phy/utils/vector.h index c0022c6a8..e46493d88 100644 --- a/lib/include/srsran/phy/utils/vector.h +++ b/lib/include/srsran/phy/utils/vector.h @@ -53,6 +53,9 @@ extern "C" { // Exponential moving average #define SRSRAN_VEC_EMA(data, average, alpha) ((alpha) * (data) + (1 - alpha) * (average)) +// Safe exponential moving average +#define SRSRAN_VEC_SAFE_EMA(data, average, alpha) (isnormal(average) ? SRSRAN_VEC_EMA(data, average, alpha) : (data)) + static inline float srsran_convert_amplitude_to_dB(float v) { return 20.0f * log10f(v); From e68c822505e2f01f7b97656cf2b4a8189864cf8f Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Tue, 4 May 2021 20:50:25 +0200 Subject: [PATCH 43/50] NR Added CSI measurement channel processing --- lib/include/srsran/phy/phch/csi.h | 14 +++++--- lib/include/srsran/phy/phch/csi_cfg.h | 47 ++++++++++++++++++++++++-- lib/include/srsran/phy/ue/ue_dl_nr.h | 8 +++++ lib/include/srsran/srsran.h | 1 + lib/src/phy/phch/csi.c | 48 +++++++++++++++++++++++++++ lib/src/phy/ue/ue_dl_nr.c | 12 +++++++ 6 files changed, 123 insertions(+), 7 deletions(-) diff --git a/lib/include/srsran/phy/phch/csi.h b/lib/include/srsran/phy/phch/csi.h index f8b024b0e..eaca13393 100644 --- a/lib/include/srsran/phy/phch/csi.h +++ b/lib/include/srsran/phy/phch/csi.h @@ -15,12 +15,18 @@ #include "uci_cfg_nr.h" +SRSRAN_API int +srsran_csi_new_nzp_csi_rs_measurement(const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES], + srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + const srsran_csi_measurements_t* new_measure, + uint32_t nzp_csi_rs_id); + /** - * @brief Fills Uplink Control Information data with triggered reports for the given slot - * @param cfg CSI report configuration + * @brief Generates CSI report configuration and values from the higher layer configuration and a list of measurements + * @param cfg Higher layer report configuration * @param slot_idx Slot index within the radio frame - * @param measurements CSI measurements - * @param[out] uci_data Uplink Control Information data + * @param measurements Filtered CSI measurements + * @param[out] report_cfg Report configuration re * @return The number CSI reports for transmission if the provided data is valid, SRSRAN_ERROR code otherwise */ SRSRAN_API int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, diff --git a/lib/include/srsran/phy/phch/csi_cfg.h b/lib/include/srsran/phy/phch/csi_cfg.h index 40787c6be..73382e9b6 100644 --- a/lib/include/srsran/phy/phch/csi_cfg.h +++ b/lib/include/srsran/phy/phch/csi_cfg.h @@ -27,6 +27,22 @@ */ #define SRSRAN_CSI_MAX_NOF_RESOURCES 112 +/** + * @brief Maximum number of NZP-CSI-RS resources sets per config, defined in TS 38.331 + * maxNrofNZP-CSI-RS-ResourceSetsPerConfig + */ +#define SRSRAN_CSI_MAX_NOF_NZP_CSI_RS_RESOURCE_SETS_X_CONFIG 16 + +/** + * @brief Maximum number of CSI-SSB resources sets per config, defined in TS 38.331 maxNrofCSI-SSB-ResourceSetsPerConfig + */ +#define SRSRAN_CSI_MAX_NOF_CSI_SSB_RESOURCE_SETS_X_CONFIG 1 + +/** + * @brief Maximum number of CSI-SSB resources sets per config, defined in TS 38.331 maxNrofCSI-IM-ResourceSetsPerConfig + */ +#define SRSRAN_CSI_MAX_NOF_CSI_IM_RESOURCE_SETS_X_CONFIG 12 + /** * @brief CSI report types defined in TS 38.331 CSI-ReportConfig */ @@ -97,16 +113,41 @@ typedef struct SRSRAN_API { srsran_csi_report_freq_t freq_cfg; ///< Determine whether it is wideband or subband } srsran_csi_hl_report_cfg_t; +/** + * @brief CSI Resource configuration + */ +typedef struct SRSRAN_API { + enum { + SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_NONE = 0, + SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_NZP_CSI_RS_SSB, + SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_IM + } type; + union { + struct { + uint32_t nzp_csi_rs_resource_set_id_list[SRSRAN_CSI_MAX_NOF_NZP_CSI_RS_RESOURCE_SETS_X_CONFIG]; + uint32_t nzp_csi_rs_resource_set_id_list_count; + uint32_t csi_ssb_rs_resource_set_id_list[SRSRAN_CSI_MAX_NOF_CSI_SSB_RESOURCE_SETS_X_CONFIG]; + uint32_t csi_ssb_rs_resource_set_id_list_count; + } nzp_csi_rs_ssb; + struct { + uint32_t resource_set_id_list[SRSRAN_CSI_MAX_NOF_CSI_IM_RESOURCE_SETS_X_CONFIG]; + uint32_t resource_set_id_list_count; + } csi_im; + }; +} srsran_csi_hl_resource_cfg_t; + /** * @brief General CSI configuration provided by higher layers */ typedef struct SRSRAN_API { - srsran_csi_hl_report_cfg_t reports[SRSRAN_CSI_MAX_NOF_REPORT]; ///< CSI report configuration - // ... add here physical CSI measurement sets + srsran_csi_hl_report_cfg_t reports[SRSRAN_CSI_MAX_NOF_REPORT]; ///< CSI report configuration indexed by + ///< reportConfigId + srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES]; ///< Configured CSI resource settings, + ///< indexed by csi-ResourceConfigId } srsran_csi_hl_cfg_t; /** - * @brief Generic measurement structure + * @brief Generic CSI measurement structure */ typedef struct SRSRAN_API { uint32_t cri; ///< CSI-RS Resource Indicator diff --git a/lib/include/srsran/phy/ue/ue_dl_nr.h b/lib/include/srsran/phy/ue/ue_dl_nr.h index fef512fad..380538d98 100644 --- a/lib/include/srsran/phy/ue/ue_dl_nr.h +++ b/lib/include/srsran/phy/ue/ue_dl_nr.h @@ -13,9 +13,11 @@ #ifndef SRSRAN_UE_DL_NR_H #define SRSRAN_UE_DL_NR_H +#include "srsran/phy/ch_estimation/csi_rs.h" #include "srsran/phy/ch_estimation/dmrs_pdcch.h" #include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/dft/ofdm.h" +#include "srsran/phy/phch/csi.h" #include "srsran/phy/phch/dci_nr.h" #include "srsran/phy/phch/pdcch_cfg_nr.h" #include "srsran/phy/phch/pdcch_nr.h" @@ -171,4 +173,10 @@ SRSRAN_API int srsran_ue_dl_nr_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, srs SRSRAN_API uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* str, uint32_t str_len); +SRSRAN_API +int srsran_ue_dl_nr_csi_measure(const srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, + srsran_csi_measurements_t* measurement); + #endif // SRSRAN_UE_DL_NR_H diff --git a/lib/include/srsran/srsran.h b/lib/include/srsran/srsran.h index 6bcf7ceeb..b696984ce 100644 --- a/lib/include/srsran/srsran.h +++ b/lib/include/srsran/srsran.h @@ -39,6 +39,7 @@ extern "C" { #include "srsran/phy/ch_estimation/chest_dl.h" #include "srsran/phy/ch_estimation/chest_ul.h" +#include "srsran/phy/ch_estimation/csi_rs.h" #include "srsran/phy/ch_estimation/dmrs_pdcch.h" #include "srsran/phy/ch_estimation/dmrs_sch.h" #include "srsran/phy/ch_estimation/refsignal_dl.h" diff --git a/lib/src/phy/phch/csi.c b/lib/src/phy/phch/csi.c index f8f4599b1..9d87134a5 100644 --- a/lib/src/phy/phch/csi.c +++ b/lib/src/phy/phch/csi.c @@ -17,6 +17,8 @@ #define CSI_WIDEBAND_CSI_NOF_BITS 4 +#define CSI_DEFAULT_ALPHA 0.5f + /// Implements SNRI to CQI conversion uint32_t csi_snri_db_to_cqi(srsran_csi_cqi_table_t table, float snri_db) { @@ -142,6 +144,52 @@ csi_none_unpack(const srsran_csi_report_cfg_t* cfg, const uint8_t* o_csi1, srsra return cfg->K_csi_rs; } +int srsran_csi_new_nzp_csi_rs_measurement( + const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES], + srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + const srsran_csi_measurements_t* new_measure, + uint32_t nzp_csi_rs_id) +{ + if (csi_resources == NULL || measurements == NULL || new_measure == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Iterate all resources + for (uint32_t res_idx = 0; res_idx < SRSRAN_CSI_MAX_NOF_RESOURCES; res_idx++) { + // Skip resource is set to none + if (csi_resources[res_idx].type != SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_NZP_CSI_RS_SSB) { + continue; + } + + // Look for the NZP-CSI reource set + bool present = false; + for (uint32_t i = 0; i < SRSRAN_CSI_MAX_NOF_NZP_CSI_RS_RESOURCE_SETS_X_CONFIG && !present; i++) { + if (csi_resources[res_idx].nzp_csi_rs_ssb.nzp_csi_rs_resource_set_id_list[i] == nzp_csi_rs_id) { + present = true; + } + } + + // Skip Resource if it does not contain the NZP-CSI-RS set + if (!present) { + continue; + } + + // Filter measurements + measurements[res_idx].wideband_rsrp_dBm = + SRSRAN_VEC_SAFE_EMA(new_measure->wideband_rsrp_dBm, measurements[res_idx].wideband_rsrp_dBm, CSI_DEFAULT_ALPHA); + measurements[res_idx].wideband_epre_dBm = + SRSRAN_VEC_SAFE_EMA(new_measure->wideband_epre_dBm, measurements[res_idx].wideband_epre_dBm, CSI_DEFAULT_ALPHA); + measurements[res_idx].wideband_snr_db = + SRSRAN_VEC_SAFE_EMA(new_measure->wideband_snr_db, measurements[res_idx].wideband_snr_db, CSI_DEFAULT_ALPHA); + + // Force rest + measurements[res_idx].cri = new_measure->cri; + measurements[res_idx].K_csi_rs = new_measure->K_csi_rs; + } + + return SRSRAN_SUCCESS; +} + int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, uint32_t slot_idx, const srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], diff --git a/lib/src/phy/ue/ue_dl_nr.c b/lib/src/phy/ue/ue_dl_nr.c index cf45ea0fc..8c6105078 100644 --- a/lib/src/phy/ue/ue_dl_nr.c +++ b/lib/src/phy/ue/ue_dl_nr.c @@ -838,3 +838,15 @@ uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* s return len; } + +int srsran_ue_dl_nr_csi_measure(const srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, + srsran_csi_measurements_t* measurement) +{ + if (q == NULL || slot_cfg == NULL || csi_rs_nzp_set == NULL || measurement == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + return srsran_csi_rs_nzp_measure_channel(&q->carrier, slot_cfg, csi_rs_nzp_set, q->sf_symbols[0], measurement); +} \ No newline at end of file From 653177ca7c2fcd9190dbea56f6ca022591c86006 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Tue, 4 May 2021 20:51:08 +0200 Subject: [PATCH 44/50] SRSUE: Refactored work_dl for NR. Added NZP-CSI-RS measurement --- srsue/hdr/phy/nr/cc_worker.h | 6 + srsue/hdr/phy/nr/state.h | 14 +- srsue/src/phy/nr/cc_worker.cc | 256 ++++++++++++++++++++-------------- 3 files changed, 168 insertions(+), 108 deletions(-) diff --git a/srsue/hdr/phy/nr/cc_worker.h b/srsue/hdr/phy/nr/cc_worker.h index 9ea44588e..4c95702f4 100644 --- a/srsue/hdr/phy/nr/cc_worker.h +++ b/srsue/hdr/phy/nr/cc_worker.h @@ -57,6 +57,12 @@ private: // Methods for DCI blind search void decode_pdcch_ul(); void decode_pdcch_dl(); + + // Method for decode PDSCH + bool decode_pdsch_dl(); + + // Method for measurements + bool measure(); }; } // namespace nr diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index 80cb79a1d..0cc50622d 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -57,6 +57,7 @@ private: mutable std::mutex metrics_mutex; /// CSI-RS measurements + std::mutex csi_measurements_mutex; std::array csi_measurements = {}; /** @@ -90,7 +91,7 @@ public: csi_measurements[0].K_csi_rs = 1; csi_measurements[0].nof_ports = 1; csi_measurements[1].K_csi_rs = 4; - csi_measurements[0].nof_ports = 1; + csi_measurements[1].nof_ports = 1; } /** @@ -422,6 +423,17 @@ public: // Reset all metrics reset_metrics_(); } + + void new_nzp_csi_rs_channel_measurement(const srsran_csi_measurements_t& new_measure, uint32_t resource_set_id) + { + std::lock_guard lock(csi_measurements_mutex); + + if (srsran_csi_new_nzp_csi_rs_measurement( + cfg.csi.csi_resources, csi_measurements.data(), &new_measure, resource_set_id) < SRSRAN_SUCCESS) { + ERROR("Error processing new NZP-CSI-RS"); + return; + } + } }; } // namespace nr } // namespace srsue diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 454a24378..e0025a0ec 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -192,135 +192,177 @@ void cc_worker::decode_pdcch_ul() } } -bool cc_worker::work_dl() +bool cc_worker::decode_pdsch_dl() { - // Do NOT process any DL if it is not configured - if (not configured) { - return true; + // Get DL grant for this TTI, if available + uint32_t pid = 0; + srsran_sch_cfg_nr_t pdsch_cfg = {}; + srsran_pdsch_ack_resource_nr_t ack_resource = {}; + if (not phy->get_dl_pending_grant(dl_slot_cfg.idx, pdsch_cfg, ack_resource, pid)) { + // Early return if no grant was available + return false; } + // Notify MAC about PDSCH grant + mac_interface_phy_nr::tb_action_dl_t dl_action = {}; + mac_interface_phy_nr::mac_nr_grant_dl_t mac_dl_grant = {}; + mac_dl_grant.rnti = pdsch_cfg.grant.rnti; + mac_dl_grant.pid = pid; + mac_dl_grant.rv = pdsch_cfg.grant.tb[0].rv; + mac_dl_grant.ndi = pdsch_cfg.grant.tb[0].ndi; + mac_dl_grant.tbs = pdsch_cfg.grant.tb[0].tbs / 8; + mac_dl_grant.tti = dl_slot_cfg.idx; + phy->stack->new_grant_dl(0, mac_dl_grant, &dl_action); + + // Abort if MAC says it doesn't need the TB + if (not dl_action.tb.enabled) { + // Force positive ACK + if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) { + phy->set_pending_ack(dl_slot_cfg.idx, ack_resource, true); + } - // Check if it is a DL slot, if not skip - if (!srsran_tdd_nr_is_dl(&phy->cfg.tdd, 0, dl_slot_cfg.idx)) { + logger.info("Decoding not required. Skipping PDSCH. ack_tti_tx=%d", TTI_ADD(dl_slot_cfg.idx, ack_resource.k1)); return true; } - // Run FFT - srsran_ue_dl_nr_estimate_fft(&ue_dl, &dl_slot_cfg); + // Get data buffer + srsran::unique_byte_buffer_t data = srsran::make_byte_buffer(); + if (data == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } + data->N_bytes = pdsch_cfg.grant.tb[0].tbs / 8U; - // Decode PDCCH DL first - decode_pdcch_dl(); + // Initialise PDSCH Result + srsran_pdsch_res_nr_t pdsch_res = {}; + pdsch_res.tb[0].payload = data->msg; + pdsch_cfg.grant.tb[0].softbuffer.rx = dl_action.tb.softbuffer; - // Decode PDCCH UL after - decode_pdcch_ul(); + // Decode actual PDSCH transmission + if (srsran_ue_dl_nr_decode_pdsch(&ue_dl, &dl_slot_cfg, &pdsch_cfg, &pdsch_res) < SRSRAN_SUCCESS) { + ERROR("Error decoding PDSCH"); + return false; + } - // Get DL grant for this TTI, if available - uint32_t pid = 0; - srsran_sch_cfg_nr_t pdsch_cfg = {}; - srsran_pdsch_ack_resource_nr_t ack_resource = {}; - if (phy->get_dl_pending_grant(dl_slot_cfg.idx, pdsch_cfg, ack_resource, pid)) { - // Notify MAC about PDSCH grant - mac_interface_phy_nr::tb_action_dl_t dl_action = {}; - mac_interface_phy_nr::mac_nr_grant_dl_t mac_dl_grant = {}; - mac_dl_grant.rnti = pdsch_cfg.grant.rnti; - mac_dl_grant.pid = pid; - mac_dl_grant.rv = pdsch_cfg.grant.tb[0].rv; - mac_dl_grant.ndi = pdsch_cfg.grant.tb[0].ndi; - mac_dl_grant.tbs = pdsch_cfg.grant.tb[0].tbs / 8; - mac_dl_grant.tti = dl_slot_cfg.idx; - phy->stack->new_grant_dl(0, mac_dl_grant, &dl_action); - - // Abort if MAC says it doesn't need the TB - if (not dl_action.tb.enabled) { - // Force positive ACK - if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) { - phy->set_pending_ack(dl_slot_cfg.idx, ack_resource, true); - } + // Logging + if (logger.info.enabled()) { + str_info_t str; + srsran_ue_dl_nr_pdsch_info(&ue_dl, &pdsch_cfg, &pdsch_res, str.data(), (uint32_t)str.size()); - logger.info("Decoding not required. Skipping PDSCH. ack_tti_tx=%d", TTI_ADD(dl_slot_cfg.idx, ack_resource.k1)); - return true; + if (logger.debug.enabled()) { + str_extra_t str_extra; + srsran_sch_cfg_nr_info(&pdsch_cfg, str_extra.data(), (uint32_t)str_extra.size()); + logger.info(pdsch_res.tb[0].payload, + pdsch_cfg.grant.tb[0].tbs / 8, + "PDSCH: cc=%d pid=%d %s\n%s", + cc_idx, + pid, + str.data(), + str_extra.data()); + } else { + logger.info(pdsch_res.tb[0].payload, + pdsch_cfg.grant.tb[0].tbs / 8, + "PDSCH: cc=%d pid=%d %s ack_tti_tx=%d", + cc_idx, + pid, + str.data(), + TTI_ADD(dl_slot_cfg.idx, ack_resource.k1)); } + } - // Get data buffer - srsran::unique_byte_buffer_t data = srsran::make_byte_buffer(); - if (data == nullptr) { - logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); - return false; - } - data->N_bytes = pdsch_cfg.grant.tb[0].tbs / 8U; + // Enqueue PDSCH ACK information only if the RNTI is type C + if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) { + phy->set_pending_ack(dl_slot_cfg.idx, ack_resource, pdsch_res.tb[0].crc); + } - // Initialise PDSCH Result - srsran_pdsch_res_nr_t pdsch_res = {}; - pdsch_res.tb[0].payload = data->msg; - pdsch_cfg.grant.tb[0].softbuffer.rx = dl_action.tb.softbuffer; + // Notify MAC about PDSCH decoding result + mac_interface_phy_nr::tb_action_dl_result_t mac_dl_result = {}; + mac_dl_result.ack = pdsch_res.tb[0].crc; + mac_dl_result.payload = mac_dl_result.ack ? std::move(data) : nullptr; // only pass data when successful + phy->stack->tb_decoded(cc_idx, mac_dl_grant, std::move(mac_dl_result)); - // Decode actual PDSCH transmission - if (srsran_ue_dl_nr_decode_pdsch(&ue_dl, &dl_slot_cfg, &pdsch_cfg, &pdsch_res) < SRSRAN_SUCCESS) { - ERROR("Error decoding PDSCH"); - return false; - } + if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_ra) { + phy->rar_grant_tti = dl_slot_cfg.idx; + } - // Logging - if (logger.info.enabled()) { - str_info_t str; - srsran_ue_dl_nr_pdsch_info(&ue_dl, &pdsch_cfg, &pdsch_res, str.data(), (uint32_t)str.size()); + if (pdsch_res.tb[0].crc) { + // Generate DL metrics + dl_metrics_t dl_m = {}; + dl_m.mcs = pdsch_cfg.grant.tb[0].mcs; + dl_m.fec_iters = pdsch_res.tb[0].avg_iter; + dl_m.evm = pdsch_res.evm[0]; + phy->set_dl_metrics(dl_m); + + // Generate Synch metrics + sync_metrics_t sync_m = {}; + sync_m.cfo = ue_dl.chest.cfo; + phy->set_sync_metrics(sync_m); + + // Generate channel metrics + ch_metrics_t ch_m = {}; + ch_m.n = ue_dl.chest.noise_estimate; + ch_m.sinr = ue_dl.chest.snr_db; + ch_m.rsrp = ue_dl.chest.rsrp_dbm; + ch_m.sync_err = ue_dl.chest.sync_error; + phy->set_channel_metrics(ch_m); + } - if (logger.debug.enabled()) { - str_extra_t str_extra; - srsran_sch_cfg_nr_info(&pdsch_cfg, str_extra.data(), (uint32_t)str_extra.size()); - logger.info(pdsch_res.tb[0].payload, - pdsch_cfg.grant.tb[0].tbs / 8, - "PDSCH: cc=%d pid=%d %s\n%s", - cc_idx, - pid, - str.data(), - str_extra.data()); - } else { - logger.info(pdsch_res.tb[0].payload, - pdsch_cfg.grant.tb[0].tbs / 8, - "PDSCH: cc=%d pid=%d %s ack_tti_tx=%d", - cc_idx, - pid, - str.data(), - TTI_ADD(dl_slot_cfg.idx, ack_resource.k1)); - } + return true; +} + +bool cc_worker::measure() +{ + // Iterate all NZP-CSI-RS and perform channel measurements + for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) { + // Select NZP-CSI-RS set + const srsran_csi_rs_nzp_set_t& nzp_set = phy->cfg.pdsch.nzp_csi_rs_sets[resource_set_id]; + + srsran_csi_measurements_t measurements = {}; + int n = srsran_ue_dl_nr_csi_measure(&ue_dl, &dl_slot_cfg, &nzp_set, &measurements); + if (n < SRSRAN_SUCCESS) { + logger.error("Error measuring CSI-RS"); + return false; } - // Enqueue PDSCH ACK information only if the RNTI is type C - if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) { - phy->set_pending_ack(dl_slot_cfg.idx, ack_resource, pdsch_res.tb[0].crc); + // Report new measurement to the PHY state + if (n > 0) { + phy->new_nzp_csi_rs_channel_measurement(measurements, resource_set_id); } + } - // Notify MAC about PDSCH decoding result - mac_interface_phy_nr::tb_action_dl_result_t mac_dl_result = {}; - mac_dl_result.ack = pdsch_res.tb[0].crc; - mac_dl_result.payload = mac_dl_result.ack ? std::move(data) : nullptr; // only pass data when successful - phy->stack->tb_decoded(cc_idx, mac_dl_grant, std::move(mac_dl_result)); + return true; +} - if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_ra) { - phy->rar_grant_tti = dl_slot_cfg.idx; - } +bool cc_worker::work_dl() +{ + // Do NOT process any DL if it is not configured + if (not configured) { + return true; + } - if (pdsch_res.tb[0].crc) { - // Generate DL metrics - dl_metrics_t dl_m = {}; - dl_m.mcs = pdsch_cfg.grant.tb[0].mcs; - dl_m.fec_iters = pdsch_res.tb[0].avg_iter; - dl_m.evm = pdsch_res.evm[0]; - phy->set_dl_metrics(dl_m); - - // Generate Synch metrics - sync_metrics_t sync_m = {}; - sync_m.cfo = ue_dl.chest.cfo; - phy->set_sync_metrics(sync_m); - - // Generate channel metrics - ch_metrics_t ch_m = {}; - ch_m.n = ue_dl.chest.noise_estimate; - ch_m.sinr = ue_dl.chest.snr_db; - ch_m.rsrp = ue_dl.chest.rsrp_dbm; - ch_m.sync_err = ue_dl.chest.sync_error; - phy->set_channel_metrics(ch_m); - } + // Check if it is a DL slot, if not skip + if (!srsran_tdd_nr_is_dl(&phy->cfg.tdd, 0, dl_slot_cfg.idx)) { + return true; + } + + // Run FFT + srsran_ue_dl_nr_estimate_fft(&ue_dl, &dl_slot_cfg); + + // Decode PDCCH DL first + decode_pdcch_dl(); + + // Decode PDCCH UL after + decode_pdcch_ul(); + + // Decode PDSCH + if (not decode_pdsch_dl()) { + logger.error("Error decoding PDSCH, aborting work DL"); + return false; + } + + // Measure CSI-RS + if (not measure()) { + logger.error("Error measuring CSI-RS, aborting work DL"); + return false; } return true; From fd0d804321c002ff6209774d523f9bf0e01efcb1 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Wed, 5 May 2021 15:36:01 +0200 Subject: [PATCH 45/50] Added macro for calculating the squared absolute value of a complex variable --- lib/include/srsran/phy/utils/vector.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/include/srsran/phy/utils/vector.h b/lib/include/srsran/phy/utils/vector.h index e46493d88..f62875cbf 100644 --- a/lib/include/srsran/phy/utils/vector.h +++ b/lib/include/srsran/phy/utils/vector.h @@ -44,6 +44,9 @@ extern "C" { #define SRSRAN_FLOOR(NUM, DEN) ((NUM) / (DEN)) #define SRSRAN_ROUND(NUM, DEN) ((uint32_t)round((double)(NUM) / (double)(DEN))) +// Complex squared absolute value +#define SRSRAN_CSQABS(X) (__real__(X) * __real__(X) + __imag__(X) * __imag__(X)) + // Cumulative moving average #define SRSRAN_VEC_CMA(data, average, n) ((average) + ((data) - (average)) / ((n) + 1)) From 1396c2a1e29ec2187a4550e08e5dd1b5a1033b33 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Wed, 5 May 2021 15:52:07 +0200 Subject: [PATCH 46/50] Mulpiple CSI measurement fixes. Channel and sync metrics from CSI measurements --- lib/include/srsran/phy/ch_estimation/csi_rs.h | 32 +++--- lib/include/srsran/phy/phch/csi.h | 26 +++-- lib/include/srsran/phy/phch/csi_cfg.h | 4 +- lib/include/srsran/phy/ue/ue_dl_nr.h | 14 ++- lib/src/phy/ch_estimation/csi_rs.c | 56 +++++----- lib/src/phy/ch_estimation/test/csi_rs_test.c | 6 +- lib/src/phy/phch/csi.c | 32 +++--- lib/src/phy/ue/ue_dl_nr.c | 22 +++- srsue/hdr/phy/nr/cc_worker.h | 12 +- srsue/hdr/phy/nr/state.h | 22 +++- srsue/src/phy/nr/cc_worker.cc | 105 ++++++++++++++---- 11 files changed, 222 insertions(+), 109 deletions(-) diff --git a/lib/include/srsran/phy/ch_estimation/csi_rs.h b/lib/include/srsran/phy/ch_estimation/csi_rs.h index 1dae8fb52..fc87326bb 100644 --- a/lib/include/srsran/phy/ch_estimation/csi_rs.h +++ b/lib/include/srsran/phy/ch_estimation/csi_rs.h @@ -42,7 +42,7 @@ /** * @brief Describes a measurement for NZP-CSI-RS * @note Used for fine tracking RSRP, SNR, CFO, SFO, and so on - * @note srsran_csi_measurements_t is used for CSI report generation + * @note srsran_csi_channel_measurements_t is used for CSI report generation */ typedef struct SRSRAN_API { float rsrp; ///< Linear scale RSRP @@ -57,7 +57,7 @@ typedef struct SRSRAN_API { float delay_us; ///< Average measured delay in microseconds uint32_t nof_re; ///< Number of available RE for the measurement, it can be used for weighting among different ///< measurements -} srsran_csi_rs_nzp_measure_t; +} srsran_csi_trs_measurements_t; /** * @brief Calculates if the given periodicity implies a CSI-RS transmission in the given slot @@ -118,7 +118,7 @@ SRSRAN_API int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* car const srsran_slot_cfg_t* slot_cfg, const srsran_csi_rs_nzp_resource_t* resource, const cf_t* grid, - srsran_csi_rs_nzp_measure_t* measure); + srsran_csi_trs_measurements_t* measure); /** * @brief Performs measurements of NZP-CSI-RS resource set flagged as TRS @@ -150,9 +150,11 @@ SRSRAN_API int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carr const srsran_slot_cfg_t* slot_cfg, const srsran_csi_rs_nzp_set_t* set, const cf_t* grid, - srsran_csi_rs_nzp_measure_t* measure); + srsran_csi_trs_measurements_t* measure); -SRSRAN_API uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_nzp_measure_t* measure, char* str, uint32_t str_len); +SRSRAN_API uint32_t srsran_csi_rs_measure_info(const srsran_csi_trs_measurements_t* measure, + char* str, + uint32_t str_len); /** * @brief Performs channel measurements of NZP-CSI-RS resource set for CSI reports @@ -172,11 +174,11 @@ SRSRAN_API uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_nzp_measure_t * @return The number of NZP-CSI-RS resources scheduled for this slot if the configuration is right, SRSLTE_ERROR code * if the configuration is invalid */ -SRSRAN_API int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_set_t* set, - const cf_t* grid, - srsran_csi_measurements_t* measure); +SRSRAN_API int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + srsran_csi_channel_measurements_t* measure); /** * @brief Performs measurements of ZP-CSI-RS resource set for CSI reports @@ -194,10 +196,10 @@ SRSRAN_API int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* * @return The number of ZP-CSI-RS resources scheduled for this slot if the configuration is right, SRSLTE_ERROR code if * the configuration is invalid */ -SRSRAN_API int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_zp_set_t* set, - const cf_t* grid, - srsran_csi_measurements_t* measure); +SRSRAN_API int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_zp_set_t* set, + const cf_t* grid, + srsran_csi_channel_measurements_t* measure); #endif // SRSRAN_CSI_RS_H_ diff --git a/lib/include/srsran/phy/phch/csi.h b/lib/include/srsran/phy/phch/csi.h index eaca13393..c59e48b5b 100644 --- a/lib/include/srsran/phy/phch/csi.h +++ b/lib/include/srsran/phy/phch/csi.h @@ -15,11 +15,20 @@ #include "uci_cfg_nr.h" +/** + * @brief Processes a new NZP-CSI-RS channel measurement, it maps the given measurement into the current measurements + * applying an exponential moving average filter + * @param csi_resources CSI Resource configuration, links NZP-CSI-RS resources with CSI Measurements + * @param measurements Current CSI measurements + * @param new_measure New NZP-CSI-RS channel measurement + * @param nzp_csi_rs_id NZP-CSI-RS resource set identifier + * @return SRSLTE_SUCCESS if the provided information is valid, SRSLTE_ERROR code otherwise + */ SRSRAN_API int srsran_csi_new_nzp_csi_rs_measurement(const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES], - srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], - const srsran_csi_measurements_t* new_measure, - uint32_t nzp_csi_rs_id); + srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + const srsran_csi_channel_measurements_t* new_measure, + uint32_t nzp_csi_rs_id); /** * @brief Generates CSI report configuration and values from the higher layer configuration and a list of measurements @@ -29,11 +38,12 @@ srsran_csi_new_nzp_csi_rs_measurement(const srsran_csi_hl_resource_cfg_t csi_res * @param[out] report_cfg Report configuration re * @return The number CSI reports for transmission if the provided data is valid, SRSRAN_ERROR code otherwise */ -SRSRAN_API int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, - uint32_t slot_idx, - const srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], - srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT], - srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]); +SRSRAN_API int +srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, + uint32_t slot_idx, + const srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT], + srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]); /** * @brief Compute number of CSI bits necessary to transmit all the CSI reports for a PUCCH transmission diff --git a/lib/include/srsran/phy/phch/csi_cfg.h b/lib/include/srsran/phy/phch/csi_cfg.h index 73382e9b6..5fb4e3b84 100644 --- a/lib/include/srsran/phy/phch/csi_cfg.h +++ b/lib/include/srsran/phy/phch/csi_cfg.h @@ -147,7 +147,7 @@ typedef struct SRSRAN_API { } srsran_csi_hl_cfg_t; /** - * @brief Generic CSI measurement structure + * @brief Generic CSI measurement structure, used for generating CSI reports */ typedef struct SRSRAN_API { uint32_t cri; ///< CSI-RS Resource Indicator @@ -158,7 +158,7 @@ typedef struct SRSRAN_API { // Resource set context uint32_t nof_ports; ///< Number of antenna ports uint32_t K_csi_rs; ///< Number of CSI-RS in the corresponding resource set -} srsran_csi_measurements_t; +} srsran_csi_channel_measurements_t; /** * @brief CSI report configuration diff --git a/lib/include/srsran/phy/ue/ue_dl_nr.h b/lib/include/srsran/phy/ue/ue_dl_nr.h index 380538d98..ded651383 100644 --- a/lib/include/srsran/phy/ue/ue_dl_nr.h +++ b/lib/include/srsran/phy/ue/ue_dl_nr.h @@ -174,9 +174,15 @@ SRSRAN_API int srsran_ue_dl_nr_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, srs SRSRAN_API uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* str, uint32_t str_len); SRSRAN_API -int srsran_ue_dl_nr_csi_measure(const srsran_ue_dl_nr_t* q, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, - srsran_csi_measurements_t* measurement); +int srsran_ue_dl_nr_csi_measure_trs(const srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, + srsran_csi_trs_measurements_t* measurement); + +SRSRAN_API +int srsran_ue_dl_nr_csi_measure_channel(const srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, + srsran_csi_channel_measurements_t* measurement); #endif // SRSRAN_UE_DL_NR_H diff --git a/lib/src/phy/ch_estimation/csi_rs.c b/lib/src/phy/ch_estimation/csi_rs.c index df6cdc166..e604ae5ee 100644 --- a/lib/src/phy/ch_estimation/csi_rs.c +++ b/lib/src/phy/ch_estimation/csi_rs.c @@ -177,7 +177,7 @@ static uint32_t csi_rs_cinit(const srsran_carrier_nr_t* carrier, uint32_t n = SRSRAN_SLOT_NR_MOD(carrier->scs, slot_cfg->idx); uint32_t n_id = resource->scrambling_id; - return SRSRAN_SEQUENCE_MOD(((SRSRAN_NSYMB_PER_SLOT_NR * n + l + 1UL) * (2UL * n_id) << 10UL) + n_id); + return SRSRAN_SEQUENCE_MOD((((SRSRAN_NSYMB_PER_SLOT_NR * n + l + 1UL) * (2UL * n_id + 1UL)) << 10UL) + n_id); } bool srsran_csi_rs_send(const srsran_csi_rs_period_and_offset_t* periodicity, const srsran_slot_cfg_t* slot_cfg) @@ -538,16 +538,22 @@ static int csi_rs_nzp_measure_resource(const srsran_carrier_nr_t* carri } // Compute LSE - srsran_sequence_state_apply_f(&sequence_state, (float*)lse, (float*)lse, 2 * count_re); + cf_t r[CSI_RS_MAX_SUBC_PRB * SRSRAN_MAX_PRB_NR]; + srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)r, 2 * count_re); + srsran_vec_prod_conj_ccc(lse, r, lse, count_re); + + // Compute average delay + float delay = srsran_vec_estimate_frequency(lse, (int)count_re); + delay_acc += delay; + + // Pre-compensate delay to avoid RSRP measurements get affected by average delay + srsran_vec_apply_cfo(lse, delay, lse, (int)count_re); // Compute EPRE epre_acc += srsran_vec_avg_power_cf(lse, count_re); // Compute correlation corr_acc += srsran_vec_acc_cc(lse, count_re) / (float)count_re; - - // Compute average delay - delay_acc += srsran_vec_estimate_frequency(lse, count_re); } // Set measure fields @@ -591,7 +597,7 @@ int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, const srsran_slot_cfg_t* slot_cfg, const srsran_csi_rs_nzp_resource_t* resource, const cf_t* grid, - srsran_csi_rs_nzp_measure_t* measure) + srsran_csi_trs_measurements_t* measure) { if (carrier == NULL || slot_cfg == NULL || resource == NULL || grid == NULL || measure == NULL) { return SRSRAN_ERROR; @@ -605,7 +611,7 @@ int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, // Copy measurements measure->epre = m.epre; - measure->rsrp = (__real__ m.corr * __real__ m.corr + __imag__ m.corr * __imag__ m.corr); + measure->rsrp = SRSRAN_CSQABS(m.corr); measure->delay_us = m.delay_us; measure->nof_re = m.nof_re; @@ -616,7 +622,7 @@ int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, measure->n0 = 0.0f; } - // CFo cannot be estimated with a single resource + // CFO cannot be estimated with a single resource measure->cfo_hz = 0.0f; measure->cfo_hz_max = 0.0f; @@ -633,7 +639,7 @@ int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carrier, const srsran_slot_cfg_t* slot_cfg, const srsran_csi_rs_nzp_set_t* set, const cf_t* grid, - srsran_csi_rs_nzp_measure_t* measure) + srsran_csi_trs_measurements_t* measure) { // Verify inputs if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL || measure == NULL) { @@ -680,9 +686,7 @@ int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carrier, uint32_t nof_re = 0; for (uint32_t i = 0; i < count; i++) { epre_sum += measurements[i].epre / (float)count; - rsrp_sum += (__real__ measurements[i].corr * __real__ measurements[i].corr + - __imag__ measurements[i].corr * __imag__ measurements[i].corr) / - (float)count; + rsrp_sum += SRSRAN_CSQABS(measurements[i].corr) / (float)count; delay_sum += measurements[i].delay_us / (float)count; nof_re += measurements[i].nof_re; } @@ -732,11 +736,11 @@ int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carrier, return count; } -int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_set_t* set, - const cf_t* grid, - srsran_csi_measurements_t* measure) +int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + srsran_csi_channel_measurements_t* measure) { // Verify inputs if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL || measure == NULL) { @@ -761,9 +765,7 @@ int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier, float rsrp_sum = 0.0f; for (uint32_t i = 0; i < count; i++) { epre_sum += measurements[i].epre / (float)count; - rsrp_sum += (__real__ measurements[i].corr * __real__ measurements[i].corr + - __imag__ measurements[i].corr * __imag__ measurements[i].corr) / - (float)count; + rsrp_sum += SRSRAN_CSQABS(measurements[i].corr) / (float)count; } // Estimate noise from EPRE and RSPR @@ -909,11 +911,11 @@ static int csi_rs_zp_measure_set(const srsran_carrier_nr_t* carrier, return count; } -int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_zp_set_t* set, - const cf_t* grid, - srsran_csi_measurements_t* measure) +int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_zp_set_t* set, + const cf_t* grid, + srsran_csi_channel_measurements_t* measure) { // Verify inputs if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL || measure == NULL) { @@ -953,7 +955,7 @@ int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier, return count; } -uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_nzp_measure_t* measure, char* str, uint32_t str_len) +uint32_t srsran_csi_rs_measure_info(const srsran_csi_trs_measurements_t* measure, char* str, uint32_t str_len) { uint32_t len = 0; @@ -972,4 +974,4 @@ uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_nzp_measure_t* measure, } return len; -} \ No newline at end of file +} diff --git a/lib/src/phy/ch_estimation/test/csi_rs_test.c b/lib/src/phy/ch_estimation/test/csi_rs_test.c index 095b0774e..ea96b06d1 100644 --- a/lib/src/phy/ch_estimation/test/csi_rs_test.c +++ b/lib/src/phy/ch_estimation/test/csi_rs_test.c @@ -39,7 +39,7 @@ static int nzp_test_case(const srsran_slot_cfg_t* slot_cfg, srsran_channel_awgn_t* awgn, cf_t* grid) { - srsran_csi_rs_nzp_measure_t measure = {}; + srsran_csi_trs_measurements_t measure = {}; // Put NZP-CSI-RS TESTASSERT(srsran_csi_rs_nzp_put_resource(&carrier, slot_cfg, resource, grid) == SRSRAN_SUCCESS); @@ -316,8 +316,8 @@ static int nzp_test_trs(srsran_channel_awgn_t* awgn, cf_t* grid) srsran_channel_awgn_run_c(awgn, grid, grid, SRSRAN_SLOT_LEN_RE_NR(carrier.nof_prb)); // Measure - srsran_csi_rs_nzp_measure_t measure = {}; - ret = srsran_csi_rs_nzp_measure_trs(&carrier, &slot_cfg, &set, grid, &measure); + srsran_csi_trs_measurements_t measure = {}; + ret = srsran_csi_rs_nzp_measure_trs(&carrier, &slot_cfg, &set, grid, &measure); // Check return and assert measurement if (slot_cfg.idx == 11 || slot_cfg.idx == 12) { diff --git a/lib/src/phy/phch/csi.c b/lib/src/phy/phch/csi.c index 9d87134a5..d7d7a1704 100644 --- a/lib/src/phy/phch/csi.c +++ b/lib/src/phy/phch/csi.c @@ -36,11 +36,11 @@ static bool csi_report_trigger(const srsran_csi_hl_report_cfg_t* cfg, uint32_t s return false; } -static void csi_wideband_cri_ri_pmi_cqi_quantify(const srsran_csi_hl_report_cfg_t* cfg, - const srsran_csi_measurements_t* channel_meas, - const srsran_csi_measurements_t* interf_meas, - srsran_csi_report_cfg_t* report_cfg, - srsran_csi_report_value_t* report_value) +static void csi_wideband_cri_ri_pmi_cqi_quantify(const srsran_csi_hl_report_cfg_t* cfg, + const srsran_csi_channel_measurements_t* channel_meas, + const srsran_csi_channel_measurements_t* interf_meas, + srsran_csi_report_cfg_t* report_cfg, + srsran_csi_report_value_t* report_value) { // Take SNR by default float wideband_sinr_db = channel_meas->wideband_snr_db; @@ -145,10 +145,10 @@ csi_none_unpack(const srsran_csi_report_cfg_t* cfg, const uint8_t* o_csi1, srsra } int srsran_csi_new_nzp_csi_rs_measurement( - const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES], - srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], - const srsran_csi_measurements_t* new_measure, - uint32_t nzp_csi_rs_id) + const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES], + srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + const srsran_csi_channel_measurements_t* new_measure, + uint32_t nzp_csi_rs_id) { if (csi_resources == NULL || measurements == NULL || new_measure == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; @@ -190,11 +190,11 @@ int srsran_csi_new_nzp_csi_rs_measurement( return SRSRAN_SUCCESS; } -int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, - uint32_t slot_idx, - const srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], - srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT], - srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]) +int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, + uint32_t slot_idx, + const srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT], + srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]) { uint32_t count = 0; @@ -215,10 +215,10 @@ int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, ERROR("Channel measurement ID (%d) is out of range", cfg->reports->channel_meas_id); return SRSRAN_ERROR; } - const srsran_csi_measurements_t* channel_meas = &measurements[cfg->reports->channel_meas_id]; + const srsran_csi_channel_measurements_t* channel_meas = &measurements[cfg->reports->channel_meas_id]; // Select interference measurement - const srsran_csi_measurements_t* interf_meas = NULL; + const srsran_csi_channel_measurements_t* interf_meas = NULL; if (cfg->reports->interf_meas_present) { if (cfg->reports->interf_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) { ERROR("Interference measurement ID (%d) is out of range", cfg->reports->interf_meas_id); diff --git a/lib/src/phy/ue/ue_dl_nr.c b/lib/src/phy/ue/ue_dl_nr.c index 8c6105078..fab77bdf7 100644 --- a/lib/src/phy/ue/ue_dl_nr.c +++ b/lib/src/phy/ue/ue_dl_nr.c @@ -839,14 +839,26 @@ uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* s return len; } -int srsran_ue_dl_nr_csi_measure(const srsran_ue_dl_nr_t* q, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, - srsran_csi_measurements_t* measurement) +int srsran_ue_dl_nr_csi_measure_trs(const srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, + srsran_csi_trs_measurements_t* measurement) +{ + if (q == NULL || slot_cfg == NULL || csi_rs_nzp_set == NULL || measurement == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + return srsran_csi_rs_nzp_measure_trs(&q->carrier, slot_cfg, csi_rs_nzp_set, q->sf_symbols[0], measurement); +} + +int srsran_ue_dl_nr_csi_measure_channel(const srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, + srsran_csi_channel_measurements_t* measurement) { if (q == NULL || slot_cfg == NULL || csi_rs_nzp_set == NULL || measurement == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } return srsran_csi_rs_nzp_measure_channel(&q->carrier, slot_cfg, csi_rs_nzp_set, q->sf_symbols[0], measurement); -} \ No newline at end of file +} diff --git a/srsue/hdr/phy/nr/cc_worker.h b/srsue/hdr/phy/nr/cc_worker.h index 4c95702f4..1688117ce 100644 --- a/srsue/hdr/phy/nr/cc_worker.h +++ b/srsue/hdr/phy/nr/cc_worker.h @@ -58,11 +58,17 @@ private: void decode_pdcch_ul(); void decode_pdcch_dl(); - // Method for decode PDSCH + /** + * @brief Decodes PDSCH in the current processing slot + * @return true if current configuration is valid and no error occur, false otherwise + */ bool decode_pdsch_dl(); - // Method for measurements - bool measure(); + /** + * @brief Performs Channel State Information (CSI) measurements + * @return true if current configuration is valid and no error occur, false otherwise + */ + bool measure_csi(); }; } // namespace nr diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index 0cc50622d..fbac9a8e8 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -57,8 +57,8 @@ private: mutable std::mutex metrics_mutex; /// CSI-RS measurements - std::mutex csi_measurements_mutex; - std::array csi_measurements = {}; + std::mutex csi_measurements_mutex; + std::array csi_measurements = {}; /** * @brief Resets all metrics (unprotected) @@ -278,6 +278,7 @@ public: { clear_pending_grants(); reset_metrics(); + reset_measurements(); } bool has_valid_sr_resource(uint32_t sr_id) @@ -424,7 +425,22 @@ public: reset_metrics_(); } - void new_nzp_csi_rs_channel_measurement(const srsran_csi_measurements_t& new_measure, uint32_t resource_set_id) + /** + * @brief Resets all PHY measurements (protected) + */ + void reset_measurements() + { + std::lock_guard lock(csi_measurements_mutex); + csi_measurements = {}; + } + + /** + * @brief Processes a new NZP-CSI-RS channel measurement + * @param new_measure New measurement + * @param resource_set_id NZP-CSI-RS resource set identifier used for the channel measurement + */ + void new_nzp_csi_rs_channel_measurement(const srsran_csi_channel_measurements_t& new_measure, + uint32_t resource_set_id) { std::lock_guard lock(csi_measurements_mutex); diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index e0025a0ec..b1b1d1c68 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -200,7 +200,7 @@ bool cc_worker::decode_pdsch_dl() srsran_pdsch_ack_resource_nr_t ack_resource = {}; if (not phy->get_dl_pending_grant(dl_slot_cfg.idx, pdsch_cfg, ack_resource, pid)) { // Early return if no grant was available - return false; + return true; } // Notify MAC about PDSCH grant mac_interface_phy_nr::tb_action_dl_t dl_action = {}; @@ -291,42 +291,101 @@ bool cc_worker::decode_pdsch_dl() dl_m.fec_iters = pdsch_res.tb[0].avg_iter; dl_m.evm = pdsch_res.evm[0]; phy->set_dl_metrics(dl_m); - - // Generate Synch metrics - sync_metrics_t sync_m = {}; - sync_m.cfo = ue_dl.chest.cfo; - phy->set_sync_metrics(sync_m); - - // Generate channel metrics - ch_metrics_t ch_m = {}; - ch_m.n = ue_dl.chest.noise_estimate; - ch_m.sinr = ue_dl.chest.snr_db; - ch_m.rsrp = ue_dl.chest.rsrp_dbm; - ch_m.sync_err = ue_dl.chest.sync_error; - phy->set_channel_metrics(ch_m); } return true; } -bool cc_worker::measure() +bool cc_worker::measure_csi() { - // Iterate all NZP-CSI-RS and perform channel measurements + // Iterate all NZP-CSI-RS marked as TRS and perform channel measurements for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) { // Select NZP-CSI-RS set const srsran_csi_rs_nzp_set_t& nzp_set = phy->cfg.pdsch.nzp_csi_rs_sets[resource_set_id]; - srsran_csi_measurements_t measurements = {}; - int n = srsran_ue_dl_nr_csi_measure(&ue_dl, &dl_slot_cfg, &nzp_set, &measurements); + // Skip set if not set as TRS (it will be processed later) + if (not nzp_set.trs_info) { + continue; + } + + // Perform measurement, n > 0 is any measurement is performed, n = 0 otherwise + srsran_csi_trs_measurements_t trs_measurements = {}; + int n = srsran_ue_dl_nr_csi_measure_trs(&ue_dl, &dl_slot_cfg, &nzp_set, &trs_measurements); if (n < SRSRAN_SUCCESS) { logger.error("Error measuring CSI-RS"); return false; } - // Report new measurement to the PHY state - if (n > 0) { - phy->new_nzp_csi_rs_channel_measurement(measurements, resource_set_id); + // If no measurement performed, skip + if (n == 0) { + continue; } + + logger.info("NZP-CSI-RS (TRS): id=%d rsrp=%+.1f epre=%+.1f snr=%+.1f cfo=%+.1f delay=%.1f", + resource_set_id, + trs_measurements.rsrp_dB, + trs_measurements.epre_dB, + trs_measurements.snr_dB, + trs_measurements.cfo_hz, + trs_measurements.delay_us); + + // Compute channel metrics and push it + ch_metrics_t ch_metrics = {}; + ch_metrics.sinr = trs_measurements.snr_dB; + ch_metrics.rsrp = trs_measurements.rsrp_dB; + ch_metrics.rsrq = 0.0f; // Not supported + ch_metrics.rssi = 0.0f; // Not supported + ch_metrics.sync_err = + trs_measurements.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs)); + phy->set_channel_metrics(ch_metrics); + + // Compute synch metrics and report it to the PHY state + sync_metrics_t sync_metrics = {}; + sync_metrics.cfo = trs_measurements.cfo_hz; + phy->set_sync_metrics(sync_metrics); + + // Convert to CSI channel measurement and report new NZP-CSI-RS measurement to the PHY state + srsran_csi_channel_measurements_t measurements = {}; + measurements.cri = 0; + measurements.wideband_rsrp_dBm = trs_measurements.rsrp_dB; + measurements.wideband_epre_dBm = trs_measurements.epre_dB; + measurements.wideband_snr_db = trs_measurements.snr_dB; + measurements.nof_ports = 1; // Other values are not supported + measurements.K_csi_rs = (uint32_t)n; + phy->new_nzp_csi_rs_channel_measurement(measurements, resource_set_id); + } + + // Iterate all NZP-CSI-RS not marked as TRS and perform channel measurements + for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) { + // Select NZP-CSI-RS set + const srsran_csi_rs_nzp_set_t& nzp_set = phy->cfg.pdsch.nzp_csi_rs_sets[resource_set_id]; + + // Skip set if set as TRS (it was processed previously) + if (nzp_set.trs_info) { + continue; + } + + // Perform channel measurement, n > 0 is any measurement is performed, n = 0 otherwise + srsran_csi_channel_measurements_t measurements = {}; + int n = srsran_ue_dl_nr_csi_measure_channel(&ue_dl, &dl_slot_cfg, &nzp_set, &measurements); + if (n < SRSRAN_SUCCESS) { + logger.error("Error measuring CSI-RS"); + return false; + } + + // If no measurement performed, skip + if (n == 0) { + continue; + } + + logger.info("NZP-CSI-RS: id=%d, rsrp=%+.1f epre=%+.1f snr=%+.1f", + resource_set_id, + measurements.wideband_rsrp_dBm, + measurements.wideband_epre_dBm, + measurements.wideband_snr_db); + + // Report new measurement to the PHY state + phy->new_nzp_csi_rs_channel_measurement(measurements, resource_set_id); } return true; @@ -360,8 +419,8 @@ bool cc_worker::work_dl() } // Measure CSI-RS - if (not measure()) { - logger.error("Error measuring CSI-RS, aborting work DL"); + if (not measure_csi()) { + logger.error("Error measuring, aborting work DL"); return false; } From 9464f36714eedf290e48b50a99d06dc644f49ebe Mon Sep 17 00:00:00 2001 From: Francisco Date: Thu, 6 May 2021 16:25:03 +0100 Subject: [PATCH 47/50] make eNB UE release timer after RLF detection configurable --- srsenb/enb.conf.example | 2 + srsenb/hdr/enb.h | 1 + srsenb/hdr/stack/rrc/rrc_config.h | 1 + srsenb/hdr/stack/rrc/rrc_ue.h | 7 ++-- srsenb/src/enb_cfg_parser.cc | 5 ++- srsenb/src/main.cc | 1 + srsenb/src/stack/rrc/rrc_ue.cc | 67 ++++++++++++++++++------------- 7 files changed, 51 insertions(+), 33 deletions(-) diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index ce64a9dae..ca2654dbf 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -305,6 +305,7 @@ enable = false # rrc_inactivity_timer Inactivity timeout used to remove UE context from RRC (in milliseconds). # max_prach_offset_us: Maximum allowed RACH offset (in us) # nof_prealloc_ues: Number of UE memory resources to preallocate during eNB initialization for faster UE creation (Default 8) +# rlf_release_timer_ms: Time taken by eNB to release UE context after it detects an RLF # eea_pref_list: Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1). # eia_pref_list: Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0). # gtpu_tunnel_timeout: Time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU (0 for no timer). @@ -330,6 +331,7 @@ enable = false #max_nof_kos = 100 #max_prach_offset_us = 30 #nof_prealloc_ues = 8 +#rlf_release_timer_ms = 4000 #eea_pref_list = EEA0, EEA2, EEA1 #eia_pref_list = EIA2, EIA1, EIA0 #gtpu_tunnel_timeout = 0 diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h index 71b897974..eece0fb9e 100644 --- a/srsenb/hdr/enb.h +++ b/srsenb/hdr/enb.h @@ -96,6 +96,7 @@ struct general_args_t { uint32_t max_mac_dl_kos; uint32_t max_mac_ul_kos; uint32_t gtpu_indirect_tunnel_timeout; + uint32_t rlf_release_timer_ms; }; struct all_args_t { diff --git a/srsenb/hdr/stack/rrc/rrc_config.h b/srsenb/hdr/stack/rrc/rrc_config.h index 041f492e8..fb5e7ca5a 100644 --- a/srsenb/hdr/stack/rrc/rrc_config.h +++ b/srsenb/hdr/stack/rrc/rrc_config.h @@ -61,6 +61,7 @@ struct rrc_cfg_t { cell_list_t cell_list_nr; uint32_t max_mac_dl_kos; uint32_t max_mac_ul_kos; + uint32_t rlf_release_timer_ms; }; constexpr uint32_t UE_PCELL_CC_IDX = 0; diff --git a/srsenb/hdr/stack/rrc/rrc_ue.h b/srsenb/hdr/stack/rrc/rrc_ue.h index 8e48c7816..0ac8484a1 100644 --- a/srsenb/hdr/stack/rrc/rrc_ue.h +++ b/srsenb/hdr/stack/rrc/rrc_ue.h @@ -40,8 +40,9 @@ public: std::string to_string(const activity_timeout_type_t& type); void set_activity_timeout(const activity_timeout_type_t type); - void set_rlf_timeout(); void set_activity(); + void start_rlf_timer(); + void stop_rlf_timer(); void set_radiolink_dl_state(bool crc_res); void set_radiolink_ul_state(bool crc_res); void activity_timer_expired(const activity_timeout_type_t type); @@ -158,8 +159,8 @@ public: private: // args - srsran::timer_handler::unique_timer activity_timer; - srsran::timer_handler::unique_timer rlf_timer; + srsran::unique_timer activity_timer; + srsran::unique_timer rlf_release_timer; /// cached ASN1 fields for RRC config update checking, and ease of context transfer during HO ue_var_cfg_t current_ue_cfg; diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 110bb564f..44ba97fb0 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -1160,8 +1160,9 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ rrc_cfg_->enb_id = args_->stack.s1ap.enb_id; // Set max number of KOs - rrc_cfg_->max_mac_dl_kos = args_->general.max_mac_dl_kos; - rrc_cfg_->max_mac_ul_kos = args_->general.max_mac_ul_kos; + rrc_cfg_->max_mac_dl_kos = args_->general.max_mac_dl_kos; + rrc_cfg_->max_mac_ul_kos = args_->general.max_mac_ul_kos; + rrc_cfg_->rlf_release_timer_ms = args_->general.rlf_release_timer_ms; // Set sync queue capacity to 1 for ZMQ if (args_->rf.device_name == "zmq") { diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index c8a842dc7..bc79431d3 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -219,6 +219,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release") ("expert.max_mac_ul_kos", bpo::value(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release") ("expert.gtpu_tunnel_timeout", bpo::value(&args->stack.gtpu_indirect_tunnel_timeout_msec)->default_value(0), "Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU. (0 for infinity)") + ("expert.rlf_release_timer_ms", bpo::value(&args->general.rlf_release_timer_ms)->default_value(4000), "Time taken by eNB to release UE context after it detects an RLF") // eMBMS section diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index 12192230e..d3d910c8c 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -56,10 +56,14 @@ int rrc::ue::init() // Configure apply_setup_phy_common(parent->cfg.sibs[1].sib2().rr_cfg_common, true); - rlf_timer = parent->task_sched.get_unique_timer(); - activity_timer = parent->task_sched.get_unique_timer(); + rlf_release_timer = parent->task_sched.get_unique_timer(); + activity_timer = parent->task_sched.get_unique_timer(); set_activity_timeout(MSG3_RX_TIMEOUT); // next UE response is Msg3 - set_rlf_timeout(); + + // Set timeout to release UE context after RLF detection + uint32_t deadline_ms = parent->cfg.rlf_release_timer_ms; + rlf_release_timer.set(deadline_ms, [this](uint32_t tid) { rlf_timer_expired(); }); + parent->logger.info("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline_ms); mobility_handler = make_rnti_obj(rnti, this); return SRSRAN_SUCCESS; @@ -93,6 +97,20 @@ void rrc::ue::set_activity() } } +void rrc::ue::start_rlf_timer() +{ + rlf_release_timer.run(); + parent->logger.info("RLF timer started for rnti=0x%x (duration=%dms)", rnti, rlf_release_timer.duration()); +} + +void rrc::ue::stop_rlf_timer() +{ + if (rlf_release_timer.is_running()) { + parent->logger.info("RLF timer stopped for rnti=0x%x (time elapsed=%dms)", rnti, rlf_release_timer.time_elapsed()); + } + rlf_release_timer.stop(); +} + void rrc::ue::set_radiolink_dl_state(bool crc_res) { parent->logger.debug( @@ -102,13 +120,13 @@ void rrc::ue::set_radiolink_dl_state(bool crc_res) if (crc_res) { consecutive_kos_dl = 0; consecutive_kos_ul = 0; - rlf_timer.stop(); + stop_rlf_timer(); return; } // Count KOs in MAC and trigger release if it goes above a certain value. // This is done to detect out-of-coverage UEs - if (rlf_timer.is_running()) { + if (rlf_release_timer.is_running()) { // RLF timer already running, no need to count KOs return; } @@ -129,13 +147,13 @@ void rrc::ue::set_radiolink_ul_state(bool crc_res) if (crc_res) { consecutive_kos_dl = 0; consecutive_kos_ul = 0; - rlf_timer.stop(); + stop_rlf_timer(); return; } // Count KOs in MAC and trigger release if it goes above a certain value. // This is done to detect out-of-coverage UEs - if (rlf_timer.is_running()) { + if (rlf_release_timer.is_running()) { // RLF timer already running, no need to count KOs return; } @@ -149,7 +167,7 @@ void rrc::ue::set_radiolink_ul_state(bool crc_res) void rrc::ue::activity_timer_expired(const activity_timeout_type_t type) { - rlf_timer.stop(); + stop_rlf_timer(); if (parent) { parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed()); @@ -182,7 +200,7 @@ void rrc::ue::rlf_timer_expired() { activity_timer.stop(); if (parent) { - parent->logger.info("RLF timer for rnti=0x%x expired after %d ms", rnti, rlf_timer.time_elapsed()); + parent->logger.info("RLF timer for rnti=0x%x expired after %d ms", rnti, rlf_release_timer.time_elapsed()); if (parent->s1ap->user_exists(rnti)) { parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost); @@ -203,28 +221,12 @@ void rrc::ue::max_retx_reached() parent->logger.info("Max retx reached for rnti=0x%x", rnti); // Give UE time to start re-establishment - rlf_timer.run(); + start_rlf_timer(); mac_ctrl.handle_max_retx(); } } -void rrc::ue::set_rlf_timeout() -{ - uint32_t deadline_s = 0; - uint32_t deadline_ms = 0; - - deadline_ms = static_cast((get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t310.to_number()) + - (get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t311.to_number()) + - (get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.n310.to_number())); - deadline_s = deadline_ms / 1000; - deadline_ms = deadline_ms % 1000; - - uint32_t deadline = deadline_s * 1e3 + deadline_ms; - rlf_timer.set(deadline, [this](uint32_t tid) { rlf_timer_expired(); }); - parent->logger.info("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline); -} - void rrc::ue::set_activity_timeout(const activity_timeout_type_t type) { uint32_t deadline_s = 0; @@ -514,10 +516,16 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) static_cast(rrc_event_type::con_reest_req), static_cast(procedure_result_code::none), rnti); + const rrc_conn_reest_request_r8_ies_s& req_r8 = msg->crit_exts.rrc_conn_reest_request_r8(); + uint16_t old_rnti = req_r8.ue_id.c_rnti.to_number(); + + srsran::console( + "User 0x%x requesting RRC Reestablishment as 0x%x. Cause: %s\n", rnti, old_rnti, req_r8.reest_cause.to_string()); if (not parent->s1ap->is_mme_connected()) { parent->logger.error("MME isn't connected. Sending Connection Reject"); - send_connection_reject(procedure_result_code::error_mme_not_connected); + send_connection_reest_rej(procedure_result_code::error_mme_not_connected); + srsran::console("User 0x%x RRC Reestablishment Request rejected\n", rnti); return; } parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s", @@ -526,7 +534,6 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) (uint32_t)msg->crit_exts.rrc_conn_reest_request_r8().ue_id.short_mac_i.to_number(), msg->crit_exts.rrc_conn_reest_request_r8().reest_cause.to_string()); if (is_idle()) { - uint16_t old_rnti = msg->crit_exts.rrc_conn_reest_request_r8().ue_id.c_rnti.to_number(); uint16_t old_pci = msg->crit_exts.rrc_conn_reest_request_r8().ue_id.pci; const enb_cell_common* old_cell = parent->cell_common_list->get_pci(old_pci); auto ue_it = parent->users.find(old_rnti); @@ -581,9 +588,13 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) } else { parent->logger.error("Received ConnectionReestablishment for rnti=0x%x without context", old_rnti); send_connection_reest_rej(procedure_result_code::error_unknown_rnti); + srsran::console( + "User 0x%x RRC Reestablishment Request rejected. Cause: no rnti=0x%x context available\n", rnti, old_rnti); } } else { parent->logger.error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE", rnti); + send_connection_reest_rej(procedure_result_code::error_unknown_rnti); + srsran::console("ERROR: User 0x%x requesting Reestablishment is not in RRC_IDLE\n", rnti); } } From 1677abce8d090b1a5fb7d8f9f89b49c732cc69d8 Mon Sep 17 00:00:00 2001 From: Francisco Date: Thu, 6 May 2021 18:27:41 +0100 Subject: [PATCH 48/50] create typedef for circular map of rntis. check return of gtpu ue creation --- srsenb/hdr/common/common_enb.h | 5 +++++ srsenb/hdr/stack/mac/mac.h | 6 +++--- srsenb/hdr/stack/mac/schedulers/sched_time_pf.h | 2 +- srsenb/hdr/stack/upper/gtpu.h | 5 ++--- srsenb/src/stack/upper/gtpu.cc | 6 +++++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/srsenb/hdr/common/common_enb.h b/srsenb/hdr/common/common_enb.h index 4c87f8f39..995bfc664 100644 --- a/srsenb/hdr/common/common_enb.h +++ b/srsenb/hdr/common/common_enb.h @@ -17,6 +17,7 @@ INCLUDES *******************************************************************************/ +#include "srsran/adt/circular_map.h" #include "srsran/common/common_lte.h" #include @@ -48,6 +49,10 @@ constexpr uint32_t drb_to_lcid(lte_drb drb_id) #define SRSENB_MAX_BUFFER_SIZE_BYTES 12756 #define SRSENB_BUFFER_HEADER_OFFSET 1024 +/// Typedef of circular map container which key corresponding to rnti value and that can be used across layers +template +using rnti_map_t = srsran::static_circular_map; + } // namespace srsenb #endif // SRSENB_COMMON_ENB_H diff --git a/srsenb/hdr/stack/mac/mac.h b/srsenb/hdr/stack/mac/mac.h index 5f68f710a..3461e09d8 100644 --- a/srsenb/hdr/stack/mac/mac.h +++ b/srsenb/hdr/stack/mac/mac.h @@ -136,9 +136,9 @@ private: sched_interface::dl_pdu_mch_t mch = {}; /* Map of active UEs */ - srsran::static_circular_map, 64> ue_db; - std::map > ues_to_rem; - uint16_t last_rnti = 70; + rnti_map_t > ue_db; + std::map > ues_to_rem; + uint16_t last_rnti = 70; srsran::static_blocking_queue, 32> ue_pool; ///< Pool of pre-allocated UE objects void prealloc_ue(uint32_t nof_ue); diff --git a/srsenb/hdr/stack/mac/schedulers/sched_time_pf.h b/srsenb/hdr/stack/mac/schedulers/sched_time_pf.h index 40526e655..8bddae0ea 100644 --- a/srsenb/hdr/stack/mac/schedulers/sched_time_pf.h +++ b/srsenb/hdr/stack/mac/schedulers/sched_time_pf.h @@ -64,7 +64,7 @@ private: uint32_t ul_nof_samples = 0; }; - srsran::static_circular_map ue_history_db; + rnti_map_t ue_history_db; struct ue_dl_prio_compare { bool operator()(const ue_ctxt* lhs, const ue_ctxt* rhs) const; diff --git a/srsenb/hdr/stack/upper/gtpu.h b/srsenb/hdr/stack/upper/gtpu.h index e6304fbbf..e1426ba01 100644 --- a/srsenb/hdr/stack/upper/gtpu.h +++ b/srsenb/hdr/stack/upper/gtpu.h @@ -36,7 +36,6 @@ struct gtpu_header_t; namespace srsenb { class pdcp_interface_gtpu; -class stack_interface_gtpu_lte; class gtpu_tunnel_manager { @@ -120,8 +119,8 @@ private: pdcp_interface_gtpu* pdcp = nullptr; srslog::basic_logger& logger; - srsran::static_circular_map ue_teidin_db; - tunnel_list_t tunnels; + rnti_map_t ue_teidin_db; + tunnel_list_t tunnels; }; using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state; diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index 2e9afec35..aec0d9df8 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -91,7 +91,11 @@ const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid, tun->spgw_addr = spgw_addr; if (not ue_teidin_db.contains(rnti)) { - ue_teidin_db.insert(rnti, ue_lcid_tunnel_list()); + auto ret = ue_teidin_db.insert(rnti, ue_lcid_tunnel_list()); + if (ret.is_error()) { + logger.error("Failed to allocate rnti=0x%x", rnti); + return nullptr; + } } auto& ue_tunnels = ue_teidin_db[rnti]; From 6b84754ede8f347e462d66a818f68ea8acab94d4 Mon Sep 17 00:00:00 2001 From: Francisco Date: Thu, 6 May 2021 19:08:19 +0100 Subject: [PATCH 49/50] fix rrc and gtpu bearer rnti update during reestablishment. The rnti of UE's rrc_bearer_handler was not being updated --- srsenb/hdr/stack/rrc/rrc_bearer_cfg.h | 3 +++ srsenb/src/stack/rrc/rrc_bearer_cfg.cc | 8 ++++++++ srsenb/src/stack/rrc/rrc_ue.cc | 4 ++-- srsenb/src/stack/upper/gtpu.cc | 18 +++++++++++------- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h b/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h index 59d8c78df..639672418 100644 --- a/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h +++ b/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h @@ -79,6 +79,9 @@ public: bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_, gtpu_interface_rrc* gtpu_); + /// Called after RRCReestablishmentComplete, to add E-RABs of old rnti + void reestablish_bearers(bearer_cfg_handler&& old_rnti_bearers); + int add_erab(uint8_t erab_id, const asn1::s1ap::erab_level_qos_params_s& qos, const asn1::bounded_bitstring<1, 160, true, true>& addr, diff --git a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc index 594a86af6..e0fc3d266 100644 --- a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc +++ b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc @@ -204,6 +204,14 @@ bearer_cfg_handler::bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_, gt rnti(rnti_), cfg(&cfg_), gtpu(gtpu_), logger(&srslog::fetch_basic_logger("RRC")) {} +void bearer_cfg_handler::reestablish_bearers(bearer_cfg_handler&& old_rnti_bearers) +{ + erab_info_list = std::move(old_rnti_bearers.erab_info_list); + erabs = std::move(old_rnti_bearers.erabs); + current_drbs = std::move(old_rnti_bearers.current_drbs); + old_rnti_bearers.current_drbs.clear(); +} + int bearer_cfg_handler::add_erab(uint8_t erab_id, const asn1::s1ap::erab_level_qos_params_s& qos, const asn1::bounded_bitstring<1, 160, true, true>& addr, diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index d3d910c8c..a1cec4f48 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -665,8 +665,8 @@ void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsr parent->pdcp->enable_integrity(rnti, srb_to_lcid(lte_srb::srb1)); parent->pdcp->enable_encryption(rnti, srb_to_lcid(lte_srb::srb1)); - // Reestablish current DRBs during ConnectionReconfiguration - bearer_list = std::move(parent->users.at(old_reest_rnti)->bearer_list); + // Reestablish E-RABs of old rnti during ConnectionReconfiguration + bearer_list.reestablish_bearers(std::move(parent->users.at(old_reest_rnti)->bearer_list)); // remove old RNTI parent->rem_user_thread(old_reest_rnti); diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index aec0d9df8..cb3a0814d 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -126,16 +126,20 @@ bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti) auto* old_rnti_ptr = find_rnti_tunnels(old_rnti); logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti); - // Change RNTI bearers map - ue_teidin_db.insert(new_rnti, std::move(*old_rnti_ptr)); - ue_teidin_db.erase(old_rnti); - - // Change TEID in existing tunnels - auto* new_rnti_ptr = find_rnti_tunnels(new_rnti); - for (lcid_tunnel& bearer : *new_rnti_ptr) { + // create new RNTI and update TEIDs of old rnti to reflect new rnti + if (not ue_teidin_db.insert(new_rnti, ue_lcid_tunnel_list())) { + logger.error("Failure to create new rnti=0x%x", new_rnti); + return false; + } + std::swap(ue_teidin_db[new_rnti], *old_rnti_ptr); + auto& new_rnti_obj = ue_teidin_db[new_rnti]; + for (lcid_tunnel& bearer : new_rnti_obj) { tunnels[bearer.teid].rnti = new_rnti; } + // Leave old_rnti as zombie to be removed later + old_rnti_ptr->clear(); + return true; } From b54bb35343f1c079bb7419f6cad82e7d588ac4c1 Mon Sep 17 00:00:00 2001 From: Francisco Date: Thu, 6 May 2021 19:53:48 +0100 Subject: [PATCH 50/50] use rnti_map_t in scheduler --- lib/include/srsran/adt/circular_map.h | 11 +++++++++++ srsenb/hdr/stack/mac/sched.h | 4 ++-- srsenb/hdr/stack/mac/sched_carrier.h | 18 +++++++++--------- srsenb/hdr/stack/mac/sched_ue.h | 11 +++++------ srsenb/src/stack/mac/sched.cc | 4 ++-- .../src/stack/mac/schedulers/sched_time_pf.cc | 6 +++--- 6 files changed, 32 insertions(+), 22 deletions(-) diff --git a/lib/include/srsran/adt/circular_map.h b/lib/include/srsran/adt/circular_map.h index 1ab4534f3..ce6500112 100644 --- a/lib/include/srsran/adt/circular_map.h +++ b/lib/include/srsran/adt/circular_map.h @@ -28,9 +28,20 @@ class static_circular_map using obj_t = std::pair; public: + using key_type = K; + using mapped_type = T; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + class iterator { public: + using iterator_category = std::forward_iterator_tag; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + iterator() = default; iterator(static_circular_map* map, size_t idx_) : ptr(map), idx(idx_) { diff --git a/srsenb/hdr/stack/mac/sched.h b/srsenb/hdr/stack/mac/sched.h index 0a6977f1b..16cc2541f 100644 --- a/srsenb/hdr/stack/mac/sched.h +++ b/srsenb/hdr/stack/mac/sched.h @@ -15,11 +15,11 @@ #include "sched_grid.h" #include "sched_ue.h" +#include "srsenb/hdr/common/common_enb.h" #include "srsran/interfaces/sched_interface.h" #include #include #include -#include namespace srsenb { @@ -91,7 +91,7 @@ protected: sched_args_t sched_cfg = {}; std::vector sched_cell_params; - std::map > ue_db; + rnti_map_t > ue_db; // independent schedulers for each carrier std::vector > carrier_schedulers; diff --git a/srsenb/hdr/stack/mac/sched_carrier.h b/srsenb/hdr/stack/mac/sched_carrier.h index 6fada4517..3ffc51f27 100644 --- a/srsenb/hdr/stack/mac/sched_carrier.h +++ b/srsenb/hdr/stack/mac/sched_carrier.h @@ -26,10 +26,10 @@ class ra_sched; class sched::carrier_sched { public: - explicit carrier_sched(rrc_interface_mac* rrc_, - std::map >* ue_db_, - uint32_t enb_cc_idx_, - sched_result_ringbuffer* sched_results_); + explicit carrier_sched(rrc_interface_mac* rrc_, + sched_ue_list* ue_db_, + uint32_t enb_cc_idx_, + sched_result_ringbuffer* sched_results_); ~carrier_sched(); void reset(); void carrier_cfg(const sched_cell_params_t& sched_params_); @@ -51,11 +51,11 @@ private: sf_sched* get_sf_sched(srsran::tti_point tti_rx); // args - const sched_cell_params_t* cc_cfg = nullptr; - srslog::basic_logger& logger; - rrc_interface_mac* rrc = nullptr; - std::map >* ue_db = nullptr; - const uint32_t enb_cc_idx; + const sched_cell_params_t* cc_cfg = nullptr; + srslog::basic_logger& logger; + rrc_interface_mac* rrc = nullptr; + sched_ue_list* ue_db = nullptr; + const uint32_t enb_cc_idx; // Subframe scheduling logic srsran::circular_array sf_scheds; diff --git a/srsenb/hdr/stack/mac/sched_ue.h b/srsenb/hdr/stack/mac/sched_ue.h index 72d985fa0..91b31e551 100644 --- a/srsenb/hdr/stack/mac/sched_ue.h +++ b/srsenb/hdr/stack/mac/sched_ue.h @@ -14,15 +14,14 @@ #define SRSENB_SCHEDULER_UE_H #include "sched_common.h" -#include "srsran/srslog/srslog.h" -#include -#include - #include "sched_ue_ctrl/sched_lch.h" #include "sched_ue_ctrl/sched_ue_cell.h" #include "sched_ue_ctrl/tpc.h" +#include "srsenb/hdr/common/common_enb.h" +#include "srsran/srslog/srslog.h" #include -#include +#include +#include namespace srsenb { @@ -211,7 +210,7 @@ private: std::vector cells; ///< List of eNB cells that may be configured/activated/deactivated for the UE }; -using sched_ue_list = std::map >; +using sched_ue_list = rnti_map_t >; } // namespace srsenb diff --git a/srsenb/src/stack/mac/sched.cc b/srsenb/src/stack/mac/sched.cc index 48e1724a0..8156adfab 100644 --- a/srsenb/src/stack/mac/sched.cc +++ b/srsenb/src/stack/mac/sched.cc @@ -107,14 +107,14 @@ int sched::ue_cfg(uint16_t rnti, const sched_interface::ue_cfg_t& ue_cfg) // Add new user case std::unique_ptr ue{new sched_ue(rnti, sched_cell_params, ue_cfg)}; std::lock_guard lock(sched_mutex); - ue_db.insert(std::make_pair(rnti, std::move(ue))); + ue_db.insert(rnti, std::move(ue)); return SRSRAN_SUCCESS; } int sched::ue_rem(uint16_t rnti) { std::lock_guard lock(sched_mutex); - if (ue_db.count(rnti) > 0) { + if (ue_db.contains(rnti)) { ue_db.erase(rnti); } else { Error("User rnti=0x%x not found", rnti); diff --git a/srsenb/src/stack/mac/schedulers/sched_time_pf.cc b/srsenb/src/stack/mac/schedulers/sched_time_pf.cc index 539c51980..11a0f8aab 100644 --- a/srsenb/src/stack/mac/schedulers/sched_time_pf.cc +++ b/srsenb/src/stack/mac/schedulers/sched_time_pf.cc @@ -24,11 +24,11 @@ sched_time_pf::sched_time_pf(const sched_cell_params_t& cell_params_, const sche fairness_coeff = std::stof(sched_args.sched_policy_args); } - std::vector dl_storage; + std::vector dl_storage; dl_storage.reserve(SRSENB_MAX_UES); dl_queue = ue_dl_queue_t(ue_dl_prio_compare{}, std::move(dl_storage)); - std::vector ul_storage; + std::vector ul_storage; ul_storage.reserve(SRSENB_MAX_UES); ul_queue = ue_ul_queue_t(ue_ul_prio_compare{}, std::move(ul_storage)); } @@ -38,7 +38,7 @@ void sched_time_pf::new_tti(sched_ue_list& ue_db, sf_sched* tti_sched) current_tti_rx = tti_point{tti_sched->get_tti_rx()}; // remove deleted users from history for (auto it = ue_history_db.begin(); it != ue_history_db.end();) { - if (not ue_db.count(it->first)) { + if (not ue_db.contains(it->first)) { it = ue_history_db.erase(it); } else { ++it;