Merge branch 'next' into agpl_next

master
Codebot 4 years ago committed by Your Name
commit 5fe9a14aa5

@ -37,9 +37,20 @@ class static_circular_map
using obj_t = std::pair<K, T>; using obj_t = std::pair<K, T>;
public: public:
using key_type = K;
using mapped_type = T;
using value_type = std::pair<K, T>;
using difference_type = std::ptrdiff_t;
class iterator class iterator
{ {
public: public:
using iterator_category = std::forward_iterator_tag;
using value_type = std::pair<K, T>;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
iterator() = default; iterator() = default;
iterator(static_circular_map<K, T, N>* map, size_t idx_) : ptr(map), idx(idx_) iterator(static_circular_map<K, T, N>* map, size_t idx_) : ptr(map), idx(idx_)
{ {

@ -0,0 +1,116 @@
/**
*
* \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 <deque>
#include <queue>
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 <typename T>
class cached_alloc : public std::allocator<T>
{
struct memblock_t : public intrusive_double_linked_list_element<> {
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);
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<T>::deallocate(reinterpret_cast<T*>(&b), n);
}
}
cached_alloc() = default;
cached_alloc(cached_alloc<T>&& other) noexcept = default;
cached_alloc(const cached_alloc<T>& other) noexcept : cached_alloc() {}
template <typename U>
explicit cached_alloc(const cached_alloc<U>& other) noexcept : cached_alloc()
{
// start empty, as cached blocks cannot be copied
}
cached_alloc& operator=(const cached_alloc<T>& 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<T*>(&b);
}
}
return std::allocator<T>::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<memblock_t*>(p);
new (block) memblock_t(req_n);
free_list.push_front(block);
}
template <typename U>
struct rebind {
using other = cached_alloc<U>;
};
private:
intrusive_double_linked_list<memblock_t> free_list;
};
} // namespace srsran
template <typename T1, typename T2>
bool operator==(const srsran::cached_alloc<T1>& lhs, const srsran::cached_alloc<T2>& rhs) noexcept
{
return &lhs == &rhs;
}
template <typename T1, typename T2>
bool operator!=(const srsran::cached_alloc<T1>& lhs, const srsran::cached_alloc<T2>& rhs) noexcept
{
return not(lhs == rhs);
}
namespace srsran {
template <typename T>
using deque = std::deque<T, cached_alloc<T> >;
template <typename T>
using queue = std::queue<T, srsran::deque<T> >;
} // namespace srsran
#endif // SRSRAN_CACHED_ALLOC_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<rrcestablishment_cause_opts, true, 1>;
using cause_radio_network_e = enumerated<cause_radio_network_opts, true, 2>;
} // namespace ngap
} // namespace asn1
#endif // SRSRAN_NGAP_UTILS_H

@ -89,7 +89,7 @@ public:
std::map<std::string, uint32_t> buffer_cnt; std::map<std::string, uint32_t> buffer_cnt;
for (uint32_t i = 0; i < pool.size(); i++) { for (uint32_t i = 0; i < pool.size(); i++) {
if (std::find(free_list.cbegin(), free_list.cend(), pool[i]) == free_list.cend()) { 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<std::string, uint32_t>::iterator it; std::map<std::string, uint32_t>::iterator it;

@ -28,6 +28,7 @@
#include <cstdint> #include <cstdint>
//#define SRSRAN_BUFFER_POOL_LOG_ENABLED //#define SRSRAN_BUFFER_POOL_LOG_ENABLED
#define SRSRAN_BUFFER_POOL_LOG_NAME_LEN 128
namespace srsran { namespace srsran {

@ -27,6 +27,15 @@
namespace srsenb { 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 = 0;
};
// GTPU interface for PDCP // GTPU interface for PDCP
class gtpu_interface_pdcp class gtpu_interface_pdcp
{ {

@ -31,7 +31,7 @@ struct mac_args_t {
uint32_t nof_prb; ///< Needed to dimension MAC softbuffers for all cells uint32_t nof_prb; ///< Needed to dimension MAC softbuffers for all cells
sched_interface::sched_args_t sched; sched_interface::sched_args_t sched;
int nr_tb_size = -1; int nr_tb_size = -1;
uint32_t max_nof_ues; uint32_t nof_prealloc_ues; ///< Number of UE resources to pre-allocate at eNB startup
uint32_t max_nof_kos; uint32_t max_nof_kos;
}; };

@ -35,8 +35,6 @@ public:
* Segmentation happens in this function. RLC PDU is stored in payload. */ * 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 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. /* 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. */ * 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; virtual void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0;

@ -56,21 +56,22 @@ struct rrc_meas_cfg_t {
// Cell/Sector configuration // Cell/Sector configuration
struct cell_cfg_t { struct cell_cfg_t {
uint32_t rf_port; uint32_t rf_port;
uint32_t cell_id; uint32_t cell_id;
uint16_t tac; uint16_t tac;
uint32_t pci; uint32_t pci;
uint16_t root_seq_idx; uint16_t root_seq_idx;
uint32_t dl_earfcn; uint32_t dl_earfcn;
double dl_freq_hz; double dl_freq_hz;
uint32_t ul_earfcn; uint32_t ul_earfcn;
double ul_freq_hz; double ul_freq_hz;
int target_pucch_sinr_db; int target_pucch_sinr_db;
int target_pusch_sinr_db; int target_pusch_sinr_db;
uint32_t initial_dl_cqi; uint32_t initial_dl_cqi;
bool enable_phr_handling; bool enable_phr_handling;
std::vector<scell_cfg_t> scell_list; asn1::rrc::mob_ctrl_info_s::t304_e_ t304;
rrc_meas_cfg_t meas_cfg; std::vector<scell_cfg_t> scell_list;
rrc_meas_cfg_t meas_cfg;
}; };
typedef std::vector<cell_cfg_t> cell_list_t; typedef std::vector<cell_cfg_t> cell_list_t;

@ -99,7 +99,6 @@ public:
class rrc_interface_rlc class rrc_interface_rlc
{ {
public: 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 max_retx_attempted(uint16_t rnti) = 0;
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0;
}; };
@ -109,12 +108,13 @@ class rrc_interface_mac
{ {
public: public:
/* Radio Link failure */ /* Radio Link failure */
virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 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 upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0;
virtual void set_activity_user(uint16_t 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_dl_state(uint16_t rnti, bool crc_res) = 0;
virtual void set_radiolink_ul_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 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) ///< 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; virtual uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) = 0;

@ -168,18 +168,6 @@ class rrc_interface_pdcp_nr
public: public:
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; 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 class phy_interface_stack_nr
{ {

@ -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

@ -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

@ -102,11 +102,11 @@ struct phy_cfg_nr_t {
dci_cfg.enable_transform_precoding = false; dci_cfg.enable_transform_precoding = false;
dci_cfg.dynamic_dual_harq_ack_codebook = false; dci_cfg.dynamic_dual_harq_ack_codebook = false;
dci_cfg.pusch_tx_config_non_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_ptrs = false;
dci_cfg.pusch_dynamic_betas = false; dci_cfg.pusch_dynamic_betas = false;
dci_cfg.pusch_alloc_type = pusch.alloc; 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) // Format 1_1 specific configuration (for PDSCH only)
dci_cfg.nof_dl_bwp = 0; dci_cfg.nof_dl_bwp = 0;
@ -121,12 +121,12 @@ struct phy_cfg_nr_t {
dci_cfg.pdsch_rm_pattern2 = false; dci_cfg.pdsch_rm_pattern2 = false;
dci_cfg.pdsch_2cw = false; dci_cfg.pdsch_2cw = false;
dci_cfg.multiple_scell = 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_tci = false;
dci_cfg.pdsch_cbg_flush = false; dci_cfg.pdsch_cbg_flush = false;
dci_cfg.pdsch_dynamic_bundling = false; dci_cfg.pdsch_dynamic_bundling = false;
dci_cfg.pdsch_alloc_type = pdsch.alloc; 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; return dci_cfg;
}; };

@ -49,18 +49,24 @@
#define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_OTHER 6 #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_channel_measurements_t is used for CSI report generation
*/ */
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
float rsrp; float rsrp; ///< Linear scale RSRP
float rsrp_dB; float rsrp_dB; ///< Logarithm scale RSRP relative to full-scale
float epre; float epre; ///< Linear scale EPRE
float epre_dB; float epre_dB; ///< Logarithm scale EPRE relative to full-scale
float n0; float n0; ///< Linear noise level
float n0_dB; float n0_dB; ///< Logarithm scale noise level relative to full-scale
float snr_dB; float snr_dB; ///< Signal to noise ratio in decibels
uint32_t nof_re; float cfo_hz; ///< Carrier frequency offset in Hz. Only set if more than 2 symbols are available in a TRS set
} srsran_csi_rs_measure_t; 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_trs_measurements_t;
/** /**
* @brief Calculates if the given periodicity implies a CSI-RS transmission in the given slot * @brief Calculates if the given periodicity implies a CSI-RS transmission in the given slot
@ -76,7 +82,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 * @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. * reserved RE pattern for PDSCH transmission.
* @param carrier Provides carrier configuration * @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 nof_resources Provides the number of ZP-CSI-RS resources
* @param l Symbol index in the slot * @param l Symbol index in the slot
* @param[out] rvd_mask Provides the reserved mask * @param[out] rvd_mask Provides the reserved mask
@ -86,17 +92,123 @@ SRSRAN_API int srsran_csi_rs_append_resource_to_pattern(const srsran_carrier_nr_
const srsran_csi_rs_resource_mapping_t* resource, const srsran_csi_rs_resource_mapping_t* resource,
srsran_re_pattern_list_t* re_pattern_list); 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, * @brief Puts in the provided resource grid NZP-CSI-RS signals given by a NZP-CSI-RS resource
const srsran_csi_rs_nzp_resource_t* resource, *
cf_t* grid); * @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, SRSRAN_API int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier,
const srsran_slot_cfg_t* slot_cfg, const srsran_slot_cfg_t* slot_cfg,
const srsran_csi_rs_nzp_resource_t* resource, const srsran_csi_rs_nzp_resource_t* resource,
const cf_t* grid, const cf_t* grid,
srsran_csi_rs_measure_t* measure); srsran_csi_trs_measurements_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_trs_measurements_t* measure);
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
*
* @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_channel_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_channel_measurements_t* measure);
#endif // SRSRAN_CSI_RS_H_ #endif // SRSRAN_CSI_RS_H_

@ -45,6 +45,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_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); SRSRAN_API void srsran_sequence_state_advance(srsran_sequence_state_t* s, uint32_t length);
typedef struct SRSRAN_API { typedef struct SRSRAN_API {

@ -25,18 +25,34 @@
#include "uci_cfg_nr.h" #include "uci_cfg_nr.h"
/** /**
* @brief Fills Uplink Control Information data with triggered reports for the given slot * @brief Processes a new NZP-CSI-RS channel measurement, it maps the given measurement into the current measurements
* @param cfg CSI report configuration * 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_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
* @param cfg Higher layer report configuration
* @param slot_idx Slot index within the radio frame * @param slot_idx Slot index within the radio frame
* @param measurements CSI measurements * @param measurements Filtered CSI measurements
* @param[out] uci_data Uplink Control Information data * @param[out] report_cfg Report configuration re
* @return The number CSI reports for transmission if the provided data is valid, SRSRAN_ERROR code otherwise * @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, SRSRAN_API int
uint32_t slot_idx, srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg,
const srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], uint32_t slot_idx,
srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT], const srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES],
srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]); 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 * @brief Compute number of CSI bits necessary to transmit all the CSI reports for a PUCCH transmission

@ -36,6 +36,22 @@
*/ */
#define SRSRAN_CSI_MAX_NOF_RESOURCES 112 #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 * @brief CSI report types defined in TS 38.331 CSI-ReportConfig
*/ */
@ -106,16 +122,41 @@ typedef struct SRSRAN_API {
srsran_csi_report_freq_t freq_cfg; ///< Determine whether it is wideband or subband srsran_csi_report_freq_t freq_cfg; ///< Determine whether it is wideband or subband
} srsran_csi_hl_report_cfg_t; } 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 * @brief General CSI configuration provided by higher layers
*/ */
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
srsran_csi_hl_report_cfg_t reports[SRSRAN_CSI_MAX_NOF_REPORT]; ///< CSI report configuration srsran_csi_hl_report_cfg_t reports[SRSRAN_CSI_MAX_NOF_REPORT]; ///< CSI report configuration indexed by
// ... add here physical CSI measurement sets ///< 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; } srsran_csi_hl_cfg_t;
/** /**
* @brief Generic measurement structure * @brief Generic CSI measurement structure, used for generating CSI reports
*/ */
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
uint32_t cri; ///< CSI-RS Resource Indicator uint32_t cri; ///< CSI-RS Resource Indicator
@ -126,7 +167,7 @@ typedef struct SRSRAN_API {
// Resource set context // Resource set context
uint32_t nof_ports; ///< Number of antenna ports uint32_t nof_ports; ///< Number of antenna ports
uint32_t K_csi_rs; ///< Number of CSI-RS in the corresponding resource set 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 * @brief CSI report configuration

@ -66,11 +66,11 @@ typedef struct SRSRAN_API {
bool enable_transform_precoding; ///< Set to true if PUSCH transform precoding is enabled 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 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_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_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 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_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) /// 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 uint32_t nof_dl_bwp; ///< Number of DL BWPs excluding the initial UL BWP, mentioned in the TS as N_BWP_RRC
@ -83,13 +83,12 @@ typedef struct SRSRAN_API {
bool pdsch_rm_pattern2; ///< Set to true if rateMatchPatternGroup2 is configured 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 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 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_tci; ///< Set to true if tci-PresentInDCI is enabled
bool pdsch_cbg_flush; ///< Set to true if codeBlockGroupFlushIndicator is true 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 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; } srsran_dci_cfg_nr_t;
/** /**

@ -193,10 +193,11 @@ typedef struct SRSRAN_API {
bool scrambling_id_present; bool scrambling_id_present;
uint32_t scambling_id; // Identifier used to initialize data scrambling (0-1023) 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 { struct {
srsran_dmrs_sch_type_t type;
srsran_dmrs_sch_add_pos_t additional_pos; srsran_dmrs_sch_add_pos_t additional_pos;
srsran_dmrs_sch_len_t length;
bool scrambling_id0_present; bool scrambling_id0_present;
uint32_t scrambling_id0; uint32_t scrambling_id0;
bool scrambling_id1_present; bool scrambling_id1_present;
@ -205,9 +206,7 @@ typedef struct SRSRAN_API {
} dmrs_typeA; } dmrs_typeA;
struct { struct {
srsran_dmrs_sch_type_t type;
srsran_dmrs_sch_add_pos_t additional_pos; srsran_dmrs_sch_add_pos_t additional_pos;
srsran_dmrs_sch_len_t length;
bool scrambling_id0_present; bool scrambling_id0_present;
uint32_t scrambling_id0; uint32_t scrambling_id0;
bool scrambling_id1_present; bool scrambling_id1_present;
@ -233,7 +232,8 @@ typedef struct SRSRAN_API {
srsran_csi_rs_nzp_set_t nzp_csi_rs_sets[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS]; srsran_csi_rs_nzp_set_t nzp_csi_rs_sets[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS];
/// PUSCH only /// 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. float scaling; /// Indicates a scaling factor to limit the number of resource elements assigned to UCI on PUSCH.
} srsran_sch_hl_cfg_nr_t; } srsran_sch_hl_cfg_nr_t;

@ -71,17 +71,32 @@ SRSRAN_API int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg,
*/ */
SRSRAN_API int 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); 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 cfg PDSCH NR configuration by upper layers
* @param[out] grant Provides grant pointer to fill * @param dci Provides PUSCH NR DCI
* @return Returns SRSRAN_SUCCESS if the provided data is valid, otherwise it returns SRSRAN_ERROR code * @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_API int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg,
srsran_sch_grant_nr_t* grant); 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. * @brief Calculates the PDSCH frequency resource allocation and stores it in the provided PDSCH NR grant.

@ -64,16 +64,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); 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 * @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 cfg PUSCH NR configuration by upper layers
* @param[out] grant Provides grant pointer to fill * @param dci Provides PUSCH NR DCI
* @return Returns SRSRAN_SUCCESS if the provided data is valid, otherwise it returns SRSRAN_ERROR code * @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_API int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg,
srsran_sch_grant_nr_t* grant); const srsran_dci_ul_nr_t* dci,
uint32_t L);
/** /**
* @brief Calculates the ratio of PUSCH EPRE to DM-RS EPRE * @brief Calculates the ratio of PUSCH EPRE to DM-RS EPRE
@ -96,7 +110,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. * @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 carrier Carrier information
* @param cfg PDSCH NR configuration by upper layers * @param cfg PDSCH NR configuration by upper layers
* @param dci_dl Unpacked DCI used to schedule the PDSCH grant * @param dci_dl Unpacked DCI used to schedule the PDSCH grant

@ -22,9 +22,11 @@
#ifndef SRSRAN_UE_DL_NR_H #ifndef SRSRAN_UE_DL_NR_H
#define 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/ch_estimation/dmrs_pdcch.h"
#include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/common/phy_common_nr.h"
#include "srsran/phy/dft/ofdm.h" #include "srsran/phy/dft/ofdm.h"
#include "srsran/phy/phch/csi.h"
#include "srsran/phy/phch/dci_nr.h" #include "srsran/phy/phch/dci_nr.h"
#include "srsran/phy/phch/pdcch_cfg_nr.h" #include "srsran/phy/phch/pdcch_cfg_nr.h"
#include "srsran/phy/phch/pdcch_nr.h" #include "srsran/phy/phch/pdcch_nr.h"
@ -180,4 +182,16 @@ 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 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_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 #endif // SRSRAN_UE_DL_NR_H

@ -53,6 +53,9 @@ extern "C" {
#define SRSRAN_FLOOR(NUM, DEN) ((NUM) / (DEN)) #define SRSRAN_FLOOR(NUM, DEN) ((NUM) / (DEN))
#define SRSRAN_ROUND(NUM, DEN) ((uint32_t)round((double)(NUM) / (double)(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 // Cumulative moving average
#define SRSRAN_VEC_CMA(data, average, n) ((average) + ((data) - (average)) / ((n) + 1)) #define SRSRAN_VEC_CMA(data, average, n) ((average) + ((data) - (average)) / ((n) + 1))
@ -62,6 +65,9 @@ extern "C" {
// Exponential moving average // Exponential moving average
#define SRSRAN_VEC_EMA(data, average, alpha) ((alpha) * (data) + (1 - alpha) * (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) static inline float srsran_convert_amplitude_to_dB(float v)
{ {
return 20.0f * log10f(v); return 20.0f * log10f(v);

@ -23,6 +23,7 @@
#define SRSLOG_DETAIL_LOG_BACKEND_H #define SRSLOG_DETAIL_LOG_BACKEND_H
#include "srsran/srslog/bundled/fmt/printf.h" #include "srsran/srslog/bundled/fmt/printf.h"
#include "srsran/srslog/shared_types.h"
namespace srslog { namespace srslog {
@ -40,7 +41,7 @@ public:
/// Starts the processing of incoming log entries. /// Starts the processing of incoming log entries.
/// NOTE: Calling this function more than once has no side effects. /// 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. /// Allocates a dyn_arg_store and returns a pointer to it on success, otherwise returns nullptr.
virtual fmt::dynamic_format_arg_store<fmt::printf_context>* alloc_arg_store() = 0; virtual fmt::dynamic_format_arg_store<fmt::printf_context>* alloc_arg_store() = 0;

@ -37,7 +37,7 @@ template <typename T, size_t capacity = SRSLOG_QUEUE_CAPACITY>
class work_queue class work_queue
{ {
srsran::dyn_circular_buffer<T> queue; srsran::dyn_circular_buffer<T> queue;
mutable condition_variable cond_var; mutable mutex m;
static constexpr size_t threshold = capacity * 0.98; static constexpr size_t threshold = capacity * 0.98;
public: public:
@ -50,15 +50,14 @@ public:
/// queue is full, otherwise true. /// queue is full, otherwise true.
bool push(const T& value) bool push(const T& value)
{ {
cond_var.lock(); m.lock();
// Discard the new element if we reach the maximum capacity. // Discard the new element if we reach the maximum capacity.
if (queue.full()) { if (queue.full()) {
cond_var.unlock(); m.unlock();
return false; return false;
} }
queue.push(value); queue.push(value);
cond_var.unlock(); m.unlock();
cond_var.signal();
return true; return true;
} }
@ -67,56 +66,26 @@ public:
/// queue is full, otherwise true. /// queue is full, otherwise true.
bool push(T&& value) bool push(T&& value)
{ {
cond_var.lock(); m.lock();
// Discard the new element if we reach the maximum capacity. // Discard the new element if we reach the maximum capacity.
if (queue.full()) { if (queue.full()) {
cond_var.unlock(); m.unlock();
return false; return false;
} }
queue.push(std::move(value)); queue.push(std::move(value));
cond_var.unlock(); m.unlock();
cond_var.signal();
return true; return true;
} }
/// Extracts the top most element from the queue. /// Extracts the top most element from the queue if it exists.
/// NOTE: This method blocks while the queue is empty. /// Returns a pair with a bool indicating if the pop has been successful.
T pop() std::pair<bool, T> try_pop()
{ {
cond_var.lock(); m.lock();
while (queue.empty()) { if (queue.empty()) {
cond_var.wait(); m.unlock();
}
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<bool, T> 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();
return {false, T()}; return {false, T()};
} }
@ -124,7 +93,7 @@ public:
T Item = std::move(queue.top()); T Item = std::move(queue.top());
queue.pop(); queue.pop();
cond_var.unlock(); m.unlock();
return {true, std::move(Item)}; return {true, std::move(Item)};
} }
@ -135,7 +104,7 @@ public:
/// Returns true when the queue is almost full, otherwise returns false. /// Returns true when the queue is almost full, otherwise returns false.
bool is_almost_full() const bool is_almost_full() const
{ {
cond_var_scoped_lock lock(cond_var); scoped_lock lock(m);
return queue.size() > threshold; return queue.size() > threshold;
} }

@ -29,6 +29,17 @@ namespace srslog {
/// Generic error handler callback. /// Generic error handler callback.
using error_handler = std::function<void(const std::string&)>; using error_handler = std::function<void(const std::string&)>;
/// 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 } // namespace srslog
#endif // SRSLOG_SHARED_TYPES_H #endif // SRSLOG_SHARED_TYPES_H

@ -232,7 +232,7 @@ sink* create_stderr_sink(const std::string& name = "stderr");
/// This function initializes the logging framework. It must be called before /// This function initializes the logging framework. It must be called before
/// any log entry is generated. /// any log entry is generated.
/// NOTE: Calling this function more than once has no side effects. /// 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 /// Flushes the contents of all the registered sinks. The caller thread will
/// block until the operation is completed. /// block until the operation is completed.

@ -48,6 +48,7 @@ extern "C" {
#include "srsran/phy/ch_estimation/chest_dl.h" #include "srsran/phy/ch_estimation/chest_dl.h"
#include "srsran/phy/ch_estimation/chest_ul.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_pdcch.h"
#include "srsran/phy/ch_estimation/dmrs_sch.h" #include "srsran/phy/ch_estimation/dmrs_sch.h"
#include "srsran/phy/ch_estimation/refsignal_dl.h" #include "srsran/phy/ch_estimation/refsignal_dl.h"

@ -65,7 +65,7 @@ static int csi_rs_location_f(const srsran_csi_rs_resource_mapping_t* resource, u
} }
if (count == i) { if (count == i) {
return j * mul; return (int)(j * mul);
} }
} }
@ -186,7 +186,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 = SRSRAN_SLOT_NR_MOD(carrier->scs, slot_cfg->idx);
uint32_t n_id = resource->scrambling_id; 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 + 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) bool srsran_csi_rs_send(const srsran_csi_rs_period_and_offset_t* periodicity, const srsran_slot_cfg_t* slot_cfg)
@ -247,7 +247,6 @@ uint32_t csi_rs_count(srsran_csi_rs_density_t density, uint32_t nprb)
case srsran_csi_rs_resource_mapping_density_three: case srsran_csi_rs_resource_mapping_density_three:
return nprb * 3; return nprb * 3;
case srsran_csi_rs_resource_mapping_density_dot5_even: case srsran_csi_rs_resource_mapping_density_dot5_even:
return nprb / 2;
case srsran_csi_rs_resource_mapping_density_dot5_odd: case srsran_csi_rs_resource_mapping_density_dot5_odd:
return nprb / 2; return nprb / 2;
case srsran_csi_rs_resource_mapping_density_one: case srsran_csi_rs_resource_mapping_density_one:
@ -348,12 +347,13 @@ int srsran_csi_rs_append_resource_to_pattern(const srsran_carrier_nr_t*
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, int srsran_csi_rs_nzp_put_resource(const srsran_carrier_nr_t* carrier,
const srsran_slot_cfg_t* slot_cfg, const srsran_slot_cfg_t* slot_cfg,
const srsran_csi_rs_nzp_resource_t* resource, const srsran_csi_rs_nzp_resource_t* resource,
cf_t* grid) 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; return SRSRAN_ERROR;
} }
@ -421,25 +421,76 @@ int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier,
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, int srsran_csi_rs_nzp_put_set(const srsran_carrier_nr_t* carrier,
const srsran_slot_cfg_t* slot_cfg, const srsran_slot_cfg_t* slot_cfg,
const srsran_csi_rs_nzp_resource_t* resource, const srsran_csi_rs_nzp_set_t* set,
const cf_t* grid, cf_t* grid)
srsran_csi_rs_measure_t* measure)
{ {
if (carrier == NULL || resource == NULL || grid == NULL) { // Verify inputs
if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL) {
return SRSRAN_ERROR; 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 // Force CDM group to 0
uint32_t j = 0; uint32_t j = 0;
// Get subcarrier indexes
uint32_t k_list[CSI_RS_MAX_SUBC_PRB]; uint32_t k_list[CSI_RS_MAX_SUBC_PRB];
int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, j, k_list); int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, j, k_list);
if (nof_k <= 0) { if (nof_k <= 0) {
return SRSRAN_ERROR; 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]; uint32_t l_list[CSI_RS_MAX_SYMBOLS_SLOT];
int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, j, l_list); int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, j, l_list);
if (nof_l <= 0) { if (nof_l <= 0) {
@ -451,11 +502,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_end = csi_rs_rb_end(carrier, &resource->resource_mapping);
uint32_t rb_stride = csi_rs_rb_stride(&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 // Accumulators
float epre_acc = 0.0f; float epre_acc = 0.0f;
cf_t rsrp_acc = 0.0f; cf_t corr_acc = 0.0f;
uint32_t count = 0; 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++) { for (int l_idx = 0; l_idx < nof_l; l_idx++) {
// Get symbol index // Get symbol index
uint32_t l = l_list[l_idx]; uint32_t l = l_list[l_idx];
@ -468,61 +526,461 @@ int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier,
// Skip unallocated RB // Skip unallocated RB
srsran_sequence_state_advance(&sequence_state, 2 * csi_rs_count(resource->resource_mapping.density, rb_begin)); srsran_sequence_state_advance(&sequence_state, 2 * csi_rs_count(resource->resource_mapping.density, rb_begin));
// Temporal R sequence // Temporal Least Square Estimates
cf_t r[64]; cf_t lse[CSI_RS_MAX_SUBC_PRB * SRSRAN_MAX_PRB_NR];
uint32_t r_idx = 64; 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 n = rb_begin; n < rb_end; n += rb_stride) {
for (uint32_t k_idx = 0; k_idx < nof_k; k_idx++) { for (uint32_t k_idx = 0; k_idx < nof_k; k_idx++) {
// Calculate sub-carrier index k // Calculate sub-carrier index k
uint32_t k = SRSRAN_NRE * n + k_list[k_idx]; uint32_t k = SRSRAN_NRE * n + k_list[k_idx];
// Do we need more r? lse[count_re++] = grid[l * SRSRAN_NRE * carrier->nof_prb + k];
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++;
} }
} }
// 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
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;
}
// 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++;
} }
if (count) { return 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); int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier,
if (measure->epre > measure->rsrp) { const srsran_slot_cfg_t* slot_cfg,
measure->n0 = measure->epre - measure->rsrp; const srsran_csi_rs_nzp_resource_t* resource,
} else { const cf_t* grid,
measure->n0 = 0.0f; srsran_csi_trs_measurements_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 = SRSRAN_CSQABS(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;
return SRSRAN_SUCCESS;
}
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_trs_measurements_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 += SRSRAN_CSQABS(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->rsrp_dB = srsran_convert_power_to_dB(measure->rsrp);
measure->epre_dB = srsran_convert_power_to_dB(measure->epre); measure->epre_dB = srsran_convert_power_to_dB(measure->epre);
measure->n0_dB = srsran_convert_power_to_dB(measure->n0); measure->n0_dB = srsran_convert_power_to_dB(measure->n0);
measure->snr_dB = measure->rsrp_dB - measure->n0_dB; measure->snr_dB = measure->rsrp_dB - measure->n0_dB;
measure->nof_re = count;
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_channel_measurements_t* measure)
{
// 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 += SRSRAN_CSQABS(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; return SRSRAN_SUCCESS;
} }
uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_measure_t* measure, char* str, uint32_t str_len) 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])
{ {
return srsran_print_check(str, uint32_t count = 0;
str_len,
0, // Iterate all resources in set
"rsrp=%+.1f, epre=%+.1f, n0=%+.1f, snr=%+.1f, nof_re=%d", for (uint32_t i = 0; i < set->count; i++) {
measure->rsrp_dB, // Skip resource
measure->epre_dB, if (!srsran_csi_rs_send(&set->data[i].periodicity, slot_cfg)) {
measure->n0_dB, continue;
measure->snr_dB, }
measure->nof_re);
// 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_channel_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_trs_measurements_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;
} }

@ -43,15 +43,15 @@ static uint32_t start_rb = UINT32_MAX;
static uint32_t nof_rb = UINT32_MAX; static uint32_t nof_rb = UINT32_MAX;
static uint32_t first_symbol = UINT32_MAX; static uint32_t first_symbol = UINT32_MAX;
static int test(const srsran_slot_cfg_t* slot_cfg, static int nzp_test_case(const srsran_slot_cfg_t* slot_cfg,
const srsran_csi_rs_nzp_resource_t* resource, const srsran_csi_rs_nzp_resource_t* resource,
srsran_channel_awgn_t* awgn, srsran_channel_awgn_t* awgn,
cf_t* grid) cf_t* grid)
{ {
srsran_csi_rs_measure_t measure = {}; srsran_csi_trs_measurements_t measure = {};
// Put NZP-CSI-RS // 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 // Configure N0 and add Noise
TESTASSERT(srsran_channel_awgn_set_n0(awgn, (float)resource->power_control_offset - snr_dB) == SRSRAN_SUCCESS); TESTASSERT(srsran_channel_awgn_set_n0(awgn, (float)resource->power_control_offset - snr_dB) == SRSRAN_SUCCESS);
@ -78,6 +78,267 @@ static int test(const srsran_slot_cfg_t* slot_cfg,
return SRSRAN_SUCCESS; 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_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) {
TESTASSERT(ret == 2);
} else {
TESTASSERT(ret == 0);
}
}
return SRSRAN_SUCCESS;
}
static void usage(char* prog) static void usage(char* prog)
{ {
printf("Usage: %s [recov]\n", prog); printf("Usage: %s [recov]\n", prog);
@ -129,10 +390,8 @@ static void parse_args(int argc, char** argv)
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
int ret = SRSRAN_ERROR; int ret = SRSRAN_ERROR;
srsran_slot_cfg_t slot_cfg = {}; srsran_channel_awgn_t awgn = {};
srsran_csi_rs_nzp_resource_t resource = {};
srsran_channel_awgn_t awgn = {};
parse_args(argc, argv); parse_args(argc, argv);
@ -147,56 +406,12 @@ int main(int argc, char** argv)
goto clean_exit; goto clean_exit;
} }
// Fixed parameters, other params are not implemented if (nzp_test_brute(&awgn, grid) < SRSRAN_SUCCESS) {
resource.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; goto clean_exit;
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 if (nzp_test_trs(&awgn, grid) < SRSRAN_SUCCESS) {
if (test(&slot_cfg, &resource, &awgn, grid) < SRSRAN_SUCCESS) { goto clean_exit;
goto clean_exit;
}
}
}
}
}
}
} }
ret = SRSRAN_SUCCESS; ret = SRSRAN_SUCCESS;

@ -161,7 +161,7 @@ uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb)
return 0; 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 // l0 must be smaller than l1
if (l0 >= l1) { if (l0 >= l1) {
@ -172,7 +172,7 @@ float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology)
uint32_t count = l1 - l0; uint32_t count = l1 - l0;
// Compute at what symbol there is a longer CP // 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 // Select whether extra CP shall be added
uint32_t extra_cp = 0; uint32_t extra_cp = 0;
@ -184,7 +184,7 @@ float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology)
uint32_t N = (2048 + 144) * count + extra_cp; uint32_t N = (2048 + 144) * count + extra_cp;
// Return symbol distance in microseconds // 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) bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx)

@ -220,10 +220,17 @@ void srsran_sequence_state_init(srsran_sequence_state_t* s, uint32_t seed)
s->x2 = sequence_get_x2_init(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) void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* out, uint32_t length)
{ {
uint32_t i = 0; uint32_t i = 0;
const float xor [2] = {+0.0F, -0.0F};
if (length >= SEQUENCE_PAR_BITS) { if (length >= SEQUENCE_PAR_BITS) {
for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) {
@ -255,7 +262,59 @@ void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float*
#endif #endif
// Finish the parallel bits with generic code // Finish the parallel bits with generic code
for (; j < SEQUENCE_PAR_BITS; j++) { 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
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++) {
FLOAT_U32_XOR(out[i], value, (s->x1 ^ s->x2) << 31U);
// 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_apply_f(srsran_sequence_state_t* s, const float* in, float* out, uint32_t length)
{
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)(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_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 // LV_HAVE_SSE
// Finish the parallel bits with generic code
for (; j < SEQUENCE_PAR_BITS; j++) {
FLOAT_U32_XOR(out[i + j], in[i + j], (c << (31U - j)) & 0x80000000);
} }
// Step sequences // Step sequences
@ -265,7 +324,7 @@ void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float*
} }
for (; i < length; i++) { for (; i < length; i++) {
*((uint32_t*)&out[i]) = *((uint32_t*)&value) ^ *((uint32_t*)&xor[(s->x1 ^ s->x2) & 1U]); FLOAT_U32_XOR(out[i], in[i], (s->x1 ^ s->x2) << 31U);
// Step sequences // Step sequences
s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1); s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1);
@ -439,56 +498,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) 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 srsran_sequence_state_t seq = {};
uint32_t x2 = sequence_get_x2_init(seed); // loads x2 initial state srsran_sequence_state_init(&seq, seed);
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));
// Load input srsran_sequence_state_apply_f(&seq, in, out, length);
__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);
}
} }
void srsran_sequence_apply_s(const int16_t* in, int16_t* out, uint32_t length, uint32_t seed) void srsran_sequence_apply_s(const int16_t* in, int16_t* out, uint32_t length, uint32_t seed)
@ -746,7 +759,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]; 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)) { while (i < (length / 8 - (SEQUENCE_PAR_BITS - 1) / 8)) {
uint32_t c = (uint32_t)(x1 ^ x2); uint32_t c = (uint32_t)(x1 ^ x2);

@ -26,6 +26,8 @@
#define CSI_WIDEBAND_CSI_NOF_BITS 4 #define CSI_WIDEBAND_CSI_NOF_BITS 4
#define CSI_DEFAULT_ALPHA 0.5f
/// Implements SNRI to CQI conversion /// Implements SNRI to CQI conversion
uint32_t csi_snri_db_to_cqi(srsran_csi_cqi_table_t table, float snri_db) uint32_t csi_snri_db_to_cqi(srsran_csi_cqi_table_t table, float snri_db)
{ {
@ -43,11 +45,11 @@ static bool csi_report_trigger(const srsran_csi_hl_report_cfg_t* cfg, uint32_t s
return false; return false;
} }
static void csi_wideband_cri_ri_pmi_cqi_quantify(const srsran_csi_hl_report_cfg_t* cfg, 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_channel_measurements_t* channel_meas,
const srsran_csi_measurements_t* interf_meas, const srsran_csi_channel_measurements_t* interf_meas,
srsran_csi_report_cfg_t* report_cfg, srsran_csi_report_cfg_t* report_cfg,
srsran_csi_report_value_t* report_value) srsran_csi_report_value_t* report_value)
{ {
// Take SNR by default // Take SNR by default
float wideband_sinr_db = channel_meas->wideband_snr_db; float wideband_sinr_db = channel_meas->wideband_snr_db;
@ -151,11 +153,57 @@ csi_none_unpack(const srsran_csi_report_cfg_t* cfg, const uint8_t* o_csi1, srsra
return cfg->K_csi_rs; return cfg->K_csi_rs;
} }
int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, int srsran_csi_new_nzp_csi_rs_measurement(
uint32_t slot_idx, const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES],
const srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES],
srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT], const srsran_csi_channel_measurements_t* new_measure,
srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]) 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_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; uint32_t count = 0;
@ -176,10 +224,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); ERROR("Channel measurement ID (%d) is out of range", cfg->reports->channel_meas_id);
return SRSRAN_ERROR; 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 // 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_present) {
if (cfg->reports->interf_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) { 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); ERROR("Interference measurement ID (%d) is out of range", cfg->reports->interf_meas_id);

@ -93,6 +93,65 @@ static uint32_t dci_nr_ptrs_size(const srsran_dci_cfg_nr_t* cfg)
return 2; 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) 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); uint32_t N_srs = SRSRAN_MIN(1, cfg->nof_srs);
@ -402,12 +461,7 @@ static uint32_t dci_nr_format_0_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_
} }
// Antenna ports // Antenna ports
if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { count += dci_nr_ul_ports_size(cfg);
count += 3;
} else {
ERROR("Not implemented");
return 0;
}
// SRS request - 2 or 3 bits // SRS request - 2 or 3 bits
count += cfg->enable_sul ? 3 : 2; count += cfg->enable_sul ? 3 : 2;
@ -511,12 +565,7 @@ static int dci_nr_format_0_1_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_
} }
// Antenna ports // Antenna ports
if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { srsran_bit_unpack(dci->ports, &y, dci_nr_ul_ports_size(cfg));
srsran_bit_unpack(dci->ports, &y, 3);
} else {
ERROR("Not implemented");
return 0;
}
// SRS request - 2 or 3 bits // SRS request - 2 or 3 bits
srsran_bit_unpack(dci->srs_request, &y, cfg->enable_sul ? 3 : 2); srsran_bit_unpack(dci->srs_request, &y, cfg->enable_sul ? 3 : 2);
@ -628,7 +677,7 @@ static int dci_nr_format_0_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_
} }
// Antenna ports // 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); dci->ports = srsran_bit_pack(&y, 3);
} else { } else {
ERROR("Not implemented"); ERROR("Not implemented");
@ -735,11 +784,8 @@ dci_nr_format_0_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci
} }
// Antenna ports // 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); len = srsran_print_check(str, str_len, len, "ports=%d ", dci->ports);
} else {
ERROR("Not implemented");
return 0;
} }
// SRS request - 2 bits // SRS request - 2 bits
@ -875,8 +921,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_rnti_type_t rnti_type = msg->ctx.rnti_type;
srsran_search_space_type_t ss_type = dci->ctx.ss_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) 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.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw
: q->cfg.bwp_dl_active_bw; : q->cfg.bwp_dl_active_bw;
// Identifier for DCI formats 1 bits // Identifier for DCI formats 1 bits
if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) {
@ -981,8 +1027,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_rnti_type_t rnti_type = msg->ctx.rnti_type;
srsran_search_space_type_t ss_type = msg->ctx.ss_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) 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.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw
: q->cfg.bwp_dl_active_bw; : q->cfg.bwp_dl_active_bw;
uint32_t nof_bits = srsran_dci_nr_size(q, ss_type, srsran_dci_format_nr_1_0); uint32_t nof_bits = srsran_dci_nr_size(q, ss_type, srsran_dci_format_nr_1_0);
if (msg->nof_bits != nof_bits) { if (msg->nof_bits != nof_bits) {
@ -1249,13 +1295,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); count += (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack);
// Antenna port(s) 4, 5, or 6 bits // Antenna port(s) 4, 5, or 6 bits
count += 4; count += dci_nr_dl_ports_size(cfg);
if (cfg->pdsch_dmrs_type2) {
count++;
}
if (cfg->pdsch_dmrs_double) {
count++;
}
// Transmission configuration indication 0 or 3 bits // Transmission configuration indication 0 or 3 bits
if (cfg->pdsch_tci) { if (cfg->pdsch_tci) {
@ -1371,13 +1411,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)); srsran_bit_unpack(dci->harq_feedback, &y, (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack));
// Antenna port(s) 4, 5, or 6 bits // Antenna port(s) 4, 5, or 6 bits
srsran_bit_unpack(dci->ports, &y, 4); srsran_bit_unpack(dci->ports, &y, dci_nr_dl_ports_size(cfg));
if (cfg->pdsch_dmrs_type2) {
y++;
}
if (cfg->pdsch_dmrs_double) {
y++;
}
// Transmission configuration indication 0 or 3 bits // Transmission configuration indication 0 or 3 bits
if (cfg->pdsch_tci) { if (cfg->pdsch_tci) {
@ -1507,13 +1541,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)); dci->harq_feedback = srsran_bit_pack(&y, (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack));
// Antenna port(s) 4, 5, or 6 bits // Antenna port(s) 4, 5, or 6 bits
dci->ports = srsran_bit_pack(&y, 4); dci->ports = srsran_bit_pack(&y, dci_nr_dl_ports_size(cfg));
if (cfg->pdsch_dmrs_type2) {
y++;
}
if (cfg->pdsch_dmrs_double) {
y++;
}
// Transmission configuration indication 0 or 3 bits // Transmission configuration indication 0 or 3 bits
if (cfg->pdsch_tci) { if (cfg->pdsch_tci) {
@ -1628,7 +1656,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 // 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 // Transmission configuration indication 0 or 3 bits
if (cfg->pdsch_tci) { if (cfg->pdsch_tci) {

@ -198,69 +198,98 @@ int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg,
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(const srsran_dmrs_sch_cfg_t* cfg, int srsran_ra_dl_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg,
srsran_sch_grant_nr_t* grant) const srsran_dci_dl_nr_t* dci,
srsran_dmrs_sch_len_t* dmrs_duration)
{ {
if (cfg == NULL || grant == NULL) { // Table 7.3.1.2.2-1: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=1
return SRSRAN_ERROR_INVALID_INPUTS; // 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: // Table 7.3.1.2.2-2: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=2
* When receiving PDSCH scheduled by DCI format 1_0, the UE shall assume the number of DM-RS CDM groups without data if (cfg->dmrs_type == srsran_dmrs_sch_type_1 && cfg->dmrs_max_length == srsran_dmrs_sch_len_2) {
* is 1 which corresponds to CDM group 0 for the case of PDSCH with allocation duration of 2 symbols, and the UE // Only one codeword supported!
* shall assume that the number of DM-RS CDM groups without data is 2 which corresponds to CDM group {0,1} for all if (dci->ports < 12) {
* other cases. *dmrs_duration = srsran_dmrs_sch_len_1;
*/ return SRSRAN_SUCCESS;
if (cfg->length == srsran_dmrs_sch_len_2) { }
grant->nof_dmrs_cdm_groups_without_data = 1;
} else { if (dci->ports < 31) {
grant->nof_dmrs_cdm_groups_without_data = 2; *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 */ if (dci->ports < 24) {
uint32_t srsran_ra_dl_nr_type0_P(uint32_t bwp_size, bool config_is_1) *dmrs_duration = srsran_dmrs_sch_len_2;
{ return SRSRAN_SUCCESS;
if (bwp_size <= 36) { }
return config_is_1 ? 2 : 4;
} else if (bwp_size <= 72) { ERROR("reserved value for ports (%d)", dci->ports);
return config_is_1 ? 4 : 8; return SRSRAN_ERROR;
} else if (bwp_size <= 144) {
return config_is_1 ? 8 : 16;
} else {
return 16;
} }
ERROR("Unhandled case");
return SRSRAN_ERROR;
} }
static int ra_freq_type0(const srsran_carrier_nr_t* carrier, int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg,
const srsran_sch_hl_cfg_nr_t* cfg, const srsran_dci_dl_nr_t* dci,
const srsran_dci_dl_nr_t* dci_dl, uint32_t L)
srsran_sch_grant_nr_t* grant)
{ {
uint32_t P = srsran_ra_dl_nr_type0_P(carrier->nof_prb, cfg->rbg_size_cfg_1); // According to TS 38.214 5.1.6.2 DM-RS reception procedure
switch (dci->ctx.format) {
uint32_t N_rbg = (int)ceilf((float)(carrier->nof_prb + (carrier->start % P)) / P); case srsran_dci_format_nr_1_0:
uint32_t rbg_offset = 0; // When receiving PDSCH scheduled by DCI format 1_0, the UE shall assume the number of DM-RS CDM groups
for (uint32_t i = 0; i < N_rbg; i++) { // without data is 1 which corresponds to CDM group 0 for the case of PDSCH with allocation duration of 2 symbols,
uint32_t rbg_size = P; // and the UE shall assume that the number of DM-RS CDM groups without data is 2 which corresponds to CDM group
if (i == 0) { // {0,1} for all other cases.
rbg_size -= (carrier->start % P); if (L == 2) {
} else if ((i == N_rbg - 1) && ((carrier->nof_prb + carrier->start) % P) > 0) { return 1;
rbg_size = (carrier->nof_prb + carrier->start) % P; } else {
} return 2;
if (dci_dl->freq_domain_assigment & (1 << (N_rbg - i - 1))) { }
for (uint32_t j = 0; j < rbg_size; j++) { return SRSRAN_SUCCESS;
if (rbg_offset + j < carrier->nof_prb) { case srsran_dci_format_nr_1_1:
grant->prb_idx[rbg_offset + j] = true; // When receiving PDSCH scheduled by DCI format 1_1, the UE shall assume that the CDM groups indicated in the
grant->nof_prb++; // 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, int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier,
@ -268,17 +297,34 @@ int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier,
const srsran_dci_dl_nr_t* dci_dl, const srsran_dci_dl_nr_t* dci_dl,
srsran_sch_grant_nr_t* grant) 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; 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) { 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); return ra_helper_freq_type1(carrier->nof_prb, dci_dl->freq_domain_assigment, grant);
} }
ra_freq_type0(carrier, cfg, dci_dl, grant); // If the scheduling DCI is configured to indicate the downlink resource allocation type as part of the Frequency
ERROR("Only DCI Format 1_0 is supported"); // 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; return SRSRAN_ERROR;
} }

@ -23,8 +23,52 @@
#define SRSRAN_RA_HELPER_H #define SRSRAN_RA_HELPER_H
#include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include <stdint.h> #include <stdint.h>
/* 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) 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; uint32_t low = v % N;

@ -537,13 +537,13 @@ int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg,
return SRSRAN_SUCCESS; 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 = 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 (dci->ctx.format == srsran_dci_format_nr_1_0 || !dedicated_dmrs_present) {
if (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 // Absent default values are defined is TS 38.331 - DMRS-DownlinkConfig
cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2; cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2;
cfg->dmrs.type = srsran_dmrs_sch_type_1; cfg->dmrs.type = srsran_dmrs_sch_type_1;
@ -555,34 +555,37 @@ static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* hl_cfg, srsran_sch_grant_nr_
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
} else { } 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.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_id0_present = false;
cfg->dmrs.scrambling_id1_present = false; cfg->dmrs.scrambling_id1_present = false;
} else { } else {
cfg->dmrs.additional_pos = hl_cfg->dmrs_typeB.additional_pos; 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_id0_present = false;
cfg->dmrs.scrambling_id1_present = false; cfg->dmrs.scrambling_id1_present = false;
} }
} }
// Set number of DMRS CDM groups without data // Set number of DMRS CDM groups without data
if (grant->dci_format == srsran_dci_format_nr_1_0) { int n = srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(hl_cfg, dci, cfg->grant.L);
if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&cfg->dmrs, grant) < SRSRAN_SUCCESS) { if (n < SRSRAN_SUCCESS) {
ERROR("Error loading number of DMRS CDM groups"); ERROR("Error loading number of DMRS CDM groups");
return SRSRAN_ERROR;
}
} else {
ERROR("Invalid case");
return SRSRAN_ERROR; 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 // 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"); ERROR("Error setting DMRS power offset");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -676,13 +679,14 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier,
// 5.1.2.3 Physical resource block (PRB) bundling // 5.1.2.3 Physical resource block (PRB) bundling
// ... // ...
pdsch_grant->nof_layers = 1; pdsch_grant->nof_layers = 1;
pdsch_grant->dci_format = dci_dl->ctx.format; pdsch_grant->dci_format = dci_dl->ctx.format;
pdsch_grant->rnti = dci_dl->ctx.rnti; pdsch_grant->rnti = dci_dl->ctx.rnti;
pdsch_grant->rnti_type = dci_dl->ctx.rnti_type; pdsch_grant->rnti_type = dci_dl->ctx.rnti_type;
pdsch_grant->tb[0].rv = dci_dl->rv; pdsch_grant->tb[0].rv = dci_dl->rv;
pdsch_grant->tb[0].mcs = dci_dl->mcs; pdsch_grant->tb[0].mcs = dci_dl->mcs;
pdsch_grant->tb[0].ndi = dci_dl->ndi; 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 // 5.1.4 PDSCH resource mapping
if (ra_dl_resource_mapping(carrier, slot, pdsch_hl_cfg, pdsch_cfg) < SRSRAN_SUCCESS) { if (ra_dl_resource_mapping(carrier, slot, pdsch_hl_cfg, pdsch_cfg) < SRSRAN_SUCCESS) {
@ -691,7 +695,7 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier,
} }
// 5.1.6.2 DM-RS reception procedure // 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"); ERROR("Error selecting DMRS configuration");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -706,15 +710,15 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier,
} }
static int 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_typeA.present
: pusch_hl_cfg->dmrs_typeB.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) { !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 // Absent default values are defined is TS 38.331 - DMRS-DownlinkConfig
cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2; cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2;
cfg->dmrs.type = srsran_dmrs_sch_type_1; cfg->dmrs.type = srsran_dmrs_sch_type_1;
@ -726,34 +730,36 @@ ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, srsran_sch_grant_nr_t* pu
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
} else { } 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.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_id0_present = false;
cfg->dmrs.scrambling_id1_present = false; cfg->dmrs.scrambling_id1_present = false;
} else { } else {
cfg->dmrs.additional_pos = pusch_hl_cfg->dmrs_typeB.additional_pos; 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_id0_present = false;
cfg->dmrs.scrambling_id1_present = false; cfg->dmrs.scrambling_id1_present = false;
} }
} }
// Set number of DMRS CDM groups without data // 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) { int n = srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(pusch_hl_cfg, dci, cfg->grant.L);
if (srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(cfg, pusch_grant) < SRSRAN_SUCCESS) { if (n < SRSRAN_SUCCESS) {
ERROR("Error loading number of DMRS CDM groups"); ERROR("Error getting number of DMRS CDM groups without data");
return SRSRAN_ERROR;
}
} else {
ERROR("DCI format not implemented %s", srsran_dci_format_nr_string(pusch_grant->dci_format));
return SRSRAN_ERROR; 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 // 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"); ERROR("Error setting DMRS power offset");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -787,16 +793,17 @@ int srsran_ra_ul_dci_to_grant_nr(const srsran_carrier_nr_t* carrier,
// 5.1.2.3 Physical resource block (PRB) bundling // 5.1.2.3 Physical resource block (PRB) bundling
// ... // ...
pusch_grant->nof_layers = 1; pusch_grant->nof_layers = 1;
pusch_grant->dci_format = dci_ul->ctx.format; pusch_grant->dci_format = dci_ul->ctx.format;
pusch_grant->rnti = dci_ul->ctx.rnti; pusch_grant->rnti = dci_ul->ctx.rnti;
pusch_grant->rnti_type = dci_ul->ctx.rnti_type; pusch_grant->rnti_type = dci_ul->ctx.rnti_type;
pusch_grant->tb[0].rv = dci_ul->rv; pusch_grant->tb[0].rv = dci_ul->rv;
pusch_grant->tb[0].mcs = dci_ul->mcs; pusch_grant->tb[0].mcs = dci_ul->mcs;
pusch_grant->tb[0].ndi = dci_ul->ndi; 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 // 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"); ERROR("Error selecting DMRS configuration");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }

@ -205,31 +205,95 @@ int srsran_ra_ul_nr_time(const srsran_sch_hl_cfg_nr_t* cfg,
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(const srsran_sch_cfg_nr_t* cfg, int srsran_ra_ul_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg,
srsran_sch_grant_nr_t* grant) 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; return SRSRAN_ERROR_INVALID_INPUTS;
} }
/* According to TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure: // Table 7.3.1.1.2-6: Antenna port(s), transform precoder is enabled, dmrs-Type=1, maxLength=1
* For PUSCH scheduled by DCI format 0_0 or by activation DCI format 0_0 with CRC scrambled by CS-RNTI, the UE // Table 7.3.1.1.2-8: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=1
* shall assume the number of DM-RS CDM groups without data is 1 which corresponds to CDM group 0 for the case of // Table 7.3.1.1.2-9: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=2
* PUSCH with allocation duration of 2 or less OFDM symbols with transform precoding disabled, the UE shall assume // Table 7.3.1.1.2-10: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=3
* 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 // Table 7.3.1.1.2-11: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=4
* scheduled by activation DCI format 0_0 and the dmrs-Type in cg-DMRS-Configuration equal to 'type2' and the PUSCH // Table 7.3.1.1.2-16: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=1
* allocation duration being more than 2 OFDM symbols, and the UE shall assume that the number of DM-RS CDM groups // Table 7.3.1.1.2-17: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=2
* without data is 2 which corresponds to CDM group {0,1} for all other cases. // 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 (grant->L <= 2 && !cfg->enable_transform_precoder) { if (cfg->dmrs_max_length == srsran_dmrs_sch_len_1) {
grant->nof_dmrs_cdm_groups_without_data = 1; *dmrs_duration = srsran_dmrs_sch_len_1;
// } else if (grant->L > 2 && cfg->dmrs_cg.type == srsran_dmrs_sch_type_2){ return SRSRAN_SUCCESS;
// grant->nof_dmrs_cdm_groups_without_data = 3;
} else {
grant->nof_dmrs_cdm_groups_without_data = 2;
} }
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:
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
// 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 #define RA_UL_PUCCH_CODE_RATE_N 8
@ -379,17 +443,39 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier,
const srsran_dci_ul_nr_t* dci_ul, const srsran_dci_ul_nr_t* dci_ul,
srsran_sch_grant_nr_t* grant) 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; return SRSRAN_ERROR_INVALID_INPUTS;
} }
// RA scheme // TS 38.213 PUSCH scheduled by RAR UL grant
if (dci_ul->ctx.format == srsran_dci_format_nr_0_0 || dci_ul->ctx.format == srsran_dci_format_nr_rar) { if (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. 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); 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; return SRSRAN_ERROR;
} }

@ -655,6 +655,8 @@ static int sch_nr_decode(srsran_sch_nr_t* q,
input_ptr += E; input_ptr += E;
} }
// Set average number of iterations
res->avg_iter = (float)nof_iter_sum / (float)cfg.C;
// Set average number of iterations // Set average number of iterations
if (cfg.C > 0) { if (cfg.C > 0) {

@ -64,11 +64,11 @@ static int test_52prb_base()
cfg.enable_transform_precoding = false; cfg.enable_transform_precoding = false;
cfg.dynamic_dual_harq_ack_codebook = false; cfg.dynamic_dual_harq_ack_codebook = false;
cfg.pusch_tx_config_non_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_ptrs = false;
cfg.pusch_dynamic_betas = false; cfg.pusch_dynamic_betas = false;
cfg.pusch_alloc_type = srsran_resource_alloc_type1; 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 // DCI 1_1 parameters
cfg.nof_dl_bwp = 0; cfg.nof_dl_bwp = 0;
@ -81,12 +81,12 @@ static int test_52prb_base()
cfg.pdsch_rm_pattern2 = false; cfg.pdsch_rm_pattern2 = false;
cfg.pdsch_2cw = false; cfg.pdsch_2cw = false;
cfg.multiple_scell = false; cfg.multiple_scell = false;
cfg.pdsch_dmrs_type2 = false;
cfg.pdsch_dmrs_double = false;
cfg.pdsch_tci = false; cfg.pdsch_tci = false;
cfg.pdsch_cbg_flush = false; cfg.pdsch_cbg_flush = false;
cfg.pdsch_dynamic_bundling = false; cfg.pdsch_dynamic_bundling = false;
cfg.pdsch_alloc_type = srsran_resource_alloc_type1; 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 // Configure DCI
srsran_dci_nr_t dci = {}; srsran_dci_nr_t dci = {};

@ -169,15 +169,11 @@ int main(int argc, char** argv)
goto clean_exit; goto clean_exit;
} }
// Load number of DMRS CDM groups without data // Set PDSCH grant without considering any procedure
if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&pdsch_cfg.dmrs, &pdsch_cfg.grant) < SRSRAN_SUCCESS) { pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO
ERROR("Error loading number of DMRS CDM groups without data"); pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers;
goto clean_exit; pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0;
} pdsch_cfg.grant.rnti = rnti;
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_start = 1;
uint32_t n_prb_end = carrier.nof_prb + 1; uint32_t n_prb_end = carrier.nof_prb + 1;

@ -175,15 +175,11 @@ int main(int argc, char** argv)
goto clean_exit; goto clean_exit;
} }
// Load number of DMRS CDM groups without data // Set PDSCH grant without considering any procedure
if (srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(&pusch_cfg, &pusch_cfg.grant) < SRSRAN_SUCCESS) { pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO
ERROR("Error loading number of DMRS CDM groups without data"); pusch_cfg.grant.nof_layers = carrier.max_mimo_layers;
goto clean_exit; pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0;
} pusch_cfg.grant.rnti = rnti;
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_start = 1;
uint32_t n_prb_end = carrier.nof_prb + 1; uint32_t n_prb_end = carrier.nof_prb + 1;

@ -188,12 +188,7 @@ int main(int argc, char** argv)
for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) { for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) {
pdsch_cfg.grant.prb_idx[n] = (n < n_prb); pdsch_cfg.grant.prb_idx[n] = (n < n_prb);
} }
pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO
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;
}
srsran_sch_tb_t tb = {}; srsran_sch_tb_t tb = {};
tb.rv = rv; tb.rv = rv;

@ -847,3 +847,27 @@ uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* s
return len; return len;
} }
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);
}

@ -33,12 +33,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"); assert(!running_flag && "Only one worker thread should be created");
std::thread t([this]() { std::thread t([this, priority]() {
running_flag = true; running_flag = true;
set_thread_priority(priority);
do_work(); do_work();
}); });
@ -50,21 +86,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. // 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() void backend_worker::do_work()
{ {
assert(running_flag && "Thread entry function called without running thread"); 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) { 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) { if (!item.first) {
std::this_thread::sleep_for(sleep_period);
continue; continue;
} }
@ -122,7 +163,7 @@ void backend_worker::process_outstanding_entries()
assert(!running_flag && "Cannot process outstanding entries while thread is running"); assert(!running_flag && "Cannot process outstanding entries while thread is running");
while (true) { while (true) {
auto item = queue.timed_pop(1); auto item = queue.try_pop();
// Check if the queue is empty. // Check if the queue is empty.
if (!item.first) { if (!item.first) {

@ -35,11 +35,6 @@ namespace srslog {
/// log entries from a work queue and dispatches them to the selected sinks. /// log entries from a work queue and dispatches them to the selected sinks.
class backend_worker 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: public:
backend_worker(detail::work_queue<detail::log_entry>& queue, detail::dyn_arg_store_pool& arg_pool) : backend_worker(detail::work_queue<detail::log_entry>& queue, detail::dyn_arg_store_pool& arg_pool) :
queue(queue), arg_pool(arg_pool), running_flag(false) queue(queue), arg_pool(arg_pool), running_flag(false)
@ -53,7 +48,7 @@ public:
/// Starts the backend worker thread. After returning from this function the /// Starts the backend worker thread. After returning from this function the
/// secondary thread is ensured to be running. Calling this function more than /// secondary thread is ensured to be running. Calling this function more than
/// once has no effect. /// once has no effect.
void start(); void start(backend_priority priority);
/// Stops the backend worker thread if it is running, otherwise the call has /// Stops the backend worker thread if it is running, otherwise the call has
/// no effect. After returning from this function the secondary thread is /// no effect. After returning from this function the secondary thread is
@ -87,7 +82,7 @@ public:
private: private:
/// Creates the worker thread. /// Creates the worker thread.
/// NOTE: This function should be only called once. /// NOTE: This function should be only called once.
void create_worker(); void create_worker(backend_priority priority);
/// Entry function used by the secondary thread. /// Entry function used by the secondary thread.
void do_work(); void do_work();
@ -112,6 +107,9 @@ private:
} }
} }
/// Establishes the specified thread priority for the calling thread.
void set_thread_priority(backend_priority priority) const;
private: private:
detail::work_queue<detail::log_entry>& queue; detail::work_queue<detail::log_entry>& queue;
detail::dyn_arg_store_pool& arg_pool; detail::dyn_arg_store_pool& arg_pool;

@ -38,7 +38,7 @@ public:
log_backend_impl(const log_backend_impl& other) = delete; log_backend_impl(const log_backend_impl& other) = delete;
log_backend_impl& operator=(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 bool push(detail::log_entry&& entry) override
{ {

@ -190,9 +190,9 @@ bool srslog::install_custom_sink(const std::string& id, std::unique_ptr<sink> s)
/// Framework configuration and control function implementations. /// 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() void srslog::flush()

@ -69,3 +69,7 @@ add_test(fsm_test fsm_test)
add_executable(optional_test optional_test.cc) add_executable(optional_test optional_test.cc)
target_link_libraries(optional_test srsran_common) target_link_libraries(optional_test srsran_common)
add_test(optional_test optional_test) 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)

@ -0,0 +1,104 @@
/**
*
* \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 <chrono>
void test_cached_deque_basic_operations()
{
srsran::deque<int> 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<int> 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<C> my_queue;
TESTASSERT(my_queue.empty());
my_queue.push(C{});
TESTASSERT(my_queue.size() == 1);
srsran::queue<C> 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::queue<int> my_deque;
std::queue<int> 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(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(i);
std_deque.pop();
}
microseconds t_std = std::chrono::duration_cast<microseconds>(high_resolution_clock::now() - tp);
tp = high_resolution_clock::now();
for (size_t i = n_elems; i < N; ++i) {
my_deque.push(i);
my_deque.pop();
}
microseconds t_cached = std::chrono::duration_cast<microseconds>(high_resolution_clock::now() - tp);
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()
{
test_cached_deque_basic_operations();
test_cached_queue_basic_operations();
cached_deque_benchmark();
return 0;
}

@ -18,6 +18,9 @@
# and at http://www.gnu.org/licenses/. # and at http://www.gnu.org/licenses/.
# #
add_executable(srslog_frontend_latency benchmarks/frontend_latency.cpp)
target_link_libraries(srslog_frontend_latency srslog)
add_executable(srslog_test srslog_test.cpp) add_executable(srslog_test srslog_test.cpp)
target_link_libraries(srslog_test srslog) target_link_libraries(srslog_test srslog)
add_test(srslog_test srslog_test) add_test(srslog_test srslog_test)

@ -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 <atomic>
#include <sys/resource.h>
#include <thread>
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<unsigned>& 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<unsigned>& 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<uint64_t>& results, std::atomic<unsigned>& 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<std::chrono::nanoseconds>(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<std::vector<uint64_t> > 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<std::thread> workers;
workers.reserve(num_threads);
std::atomic<unsigned> 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<uint64_t> 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<size_t>(results.size() * 0.5)],
results[static_cast<size_t>(results.size() * 0.75)],
results[static_cast<size_t>(results.size() * 0.9)],
results[static_cast<size_t>(results.size() * 0.99)],
results[static_cast<size_t>(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;
}

@ -34,7 +34,7 @@ namespace {
class backend_spy : public detail::log_backend class backend_spy : public detail::log_backend
{ {
public: public:
void start() override {} void start(srslog::backend_priority priority) override {}
bool push(detail::log_entry&& entry) override bool push(detail::log_entry&& entry) override
{ {

@ -69,7 +69,7 @@ namespace {
class backend_spy : public detail::log_backend class backend_spy : public detail::log_backend
{ {
public: public:
void start() override {} void start(srslog::backend_priority priority) override {}
bool push(detail::log_entry&& entry) override bool push(detail::log_entry&& entry) override
{ {

@ -23,6 +23,7 @@
#define TEST_DUMMIES #define TEST_DUMMIES
#include "srsran/srslog/detail/log_backend.h" #include "srsran/srslog/detail/log_backend.h"
#include "srsran/srslog/shared_types.h"
#include "srsran/srslog/sink.h" #include "srsran/srslog/sink.h"
namespace test_dummies { namespace test_dummies {
@ -76,7 +77,7 @@ public:
class backend_dummy : public srslog::detail::log_backend class backend_dummy : public srslog::detail::log_backend
{ {
public: public:
void start() override {} void start(srslog::backend_priority priority) override {}
bool push(srslog::detail::log_entry&& entry) override { return true; } bool push(srslog::detail::log_entry&& entry) override { return true; }

@ -304,8 +304,11 @@ enable = false
# tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR) # 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). # rrc_inactivity_timer Inactivity timeout used to remove UE context from RRC (in milliseconds).
# max_prach_offset_us: Maximum allowed RACH offset (in us) # 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). # 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). # 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).
# #
##################################################################### #####################################################################
[expert] [expert]
@ -327,5 +330,8 @@ enable = false
#rrc_inactivity_timer = 30000 #rrc_inactivity_timer = 30000
#max_nof_kos = 100 #max_nof_kos = 100
#max_prach_offset_us = 30 #max_prach_offset_us = 30
#nof_prealloc_ues = 8
#rlf_release_timer_ms = 4000
#eea_pref_list = EEA0, EEA2, EEA1 #eea_pref_list = EEA0, EEA2, EEA1
#eia_pref_list = EIA2, EIA1, EIA0 #eia_pref_list = EIA2, EIA1, EIA0
#gtpu_tunnel_timeout = 0

@ -26,6 +26,7 @@
INCLUDES INCLUDES
*******************************************************************************/ *******************************************************************************/
#include "srsran/adt/circular_map.h"
#include "srsran/common/common_lte.h" #include "srsran/common/common_lte.h"
#include <stdint.h> #include <stdint.h>
@ -57,6 +58,10 @@ constexpr uint32_t drb_to_lcid(lte_drb drb_id)
#define SRSENB_MAX_BUFFER_SIZE_BYTES 12756 #define SRSENB_MAX_BUFFER_SIZE_BYTES 12756
#define SRSENB_BUFFER_HEADER_OFFSET 1024 #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 <typename UEObject>
using rnti_map_t = srsran::static_circular_map<uint16_t, UEObject, SRSENB_MAX_UES>;
} // namespace srsenb } // namespace srsenb
#endif // SRSENB_COMMON_ENB_H #endif // SRSENB_COMMON_ENB_H

@ -104,6 +104,8 @@ struct general_args_t {
std::string eea_pref_list; std::string eea_pref_list;
uint32_t max_mac_dl_kos; uint32_t max_mac_dl_kos;
uint32_t max_mac_ul_kos; uint32_t max_mac_ul_kos;
uint32_t gtpu_indirect_tunnel_timeout;
uint32_t rlf_release_timer_ms;
}; };
struct all_args_t { struct all_args_t {

@ -475,6 +475,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<EnumType>, &presence_flag); return parse_opt_field(enum_val, root, name, number_to_enum<EnumType>, &presence_flag);
} }
template <typename EnumType>
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<EnumType>);
}
} // namespace asn1_parsers } // namespace asn1_parsers
} // namespace srsenb } // namespace srsenb

@ -79,6 +79,7 @@ typedef struct {
typedef struct { typedef struct {
std::string type; std::string type;
uint32_t sync_queue_size; // Max allowed difference between PHY and Stack clocks (in TTI) 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; mac_args_t mac;
s1ap_args_t s1ap; s1ap_args_t s1ap;
pcap_args_t mac_pcap; pcap_args_t mac_pcap;

@ -145,9 +145,9 @@ private:
sched_interface::dl_pdu_mch_t mch = {}; sched_interface::dl_pdu_mch_t mch = {};
/* Map of active UEs */ /* Map of active UEs */
srsran::static_circular_map<uint16_t, std::unique_ptr<ue>, 64> ue_db; rnti_map_t<std::unique_ptr<ue> > ue_db;
std::map<uint16_t, std::unique_ptr<ue> > ues_to_rem; std::map<uint16_t, std::unique_ptr<ue> > ues_to_rem;
uint16_t last_rnti = 70; uint16_t last_rnti = 70;
srsran::static_blocking_queue<std::unique_ptr<ue>, 32> ue_pool; ///< Pool of pre-allocated UE objects srsran::static_blocking_queue<std::unique_ptr<ue>, 32> ue_pool; ///< Pool of pre-allocated UE objects
void prealloc_ue(uint32_t nof_ue); void prealloc_ue(uint32_t nof_ue);

@ -24,11 +24,11 @@
#include "sched_grid.h" #include "sched_grid.h"
#include "sched_ue.h" #include "sched_ue.h"
#include "srsenb/hdr/common/common_enb.h"
#include "srsran/interfaces/sched_interface.h" #include "srsran/interfaces/sched_interface.h"
#include <atomic> #include <atomic>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <queue>
namespace srsenb { namespace srsenb {
@ -93,14 +93,14 @@ protected:
bool is_generated(srsran::tti_point, uint32_t enb_cc_idx) const; bool is_generated(srsran::tti_point, uint32_t enb_cc_idx) const;
// Helper methods // Helper methods
template <typename Func> template <typename Func>
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 // args
rrc_interface_mac* rrc = nullptr; rrc_interface_mac* rrc = nullptr;
sched_args_t sched_cfg = {}; sched_args_t sched_cfg = {};
std::vector<sched_cell_params_t> sched_cell_params; std::vector<sched_cell_params_t> sched_cell_params;
std::map<uint16_t, std::unique_ptr<sched_ue> > ue_db; rnti_map_t<std::unique_ptr<sched_ue> > ue_db;
// independent schedulers for each carrier // independent schedulers for each carrier
std::vector<std::unique_ptr<carrier_sched> > carrier_schedulers; std::vector<std::unique_ptr<carrier_sched> > carrier_schedulers;

@ -24,6 +24,7 @@
#include "sched.h" #include "sched.h"
#include "schedulers/sched_base.h" #include "schedulers/sched_base.h"
#include "srsran/adt/pool/cached_alloc.h"
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
namespace srsenb { namespace srsenb {
@ -34,10 +35,10 @@ class ra_sched;
class sched::carrier_sched class sched::carrier_sched
{ {
public: public:
explicit carrier_sched(rrc_interface_mac* rrc_, explicit carrier_sched(rrc_interface_mac* rrc_,
std::map<uint16_t, std::unique_ptr<sched_ue> >* ue_db_, sched_ue_list* ue_db_,
uint32_t enb_cc_idx_, uint32_t enb_cc_idx_,
sched_result_ringbuffer* sched_results_); sched_result_ringbuffer* sched_results_);
~carrier_sched(); ~carrier_sched();
void reset(); void reset();
void carrier_cfg(const sched_cell_params_t& sched_params_); void carrier_cfg(const sched_cell_params_t& sched_params_);
@ -59,11 +60,11 @@ private:
sf_sched* get_sf_sched(srsran::tti_point tti_rx); sf_sched* get_sf_sched(srsran::tti_point tti_rx);
// args // args
const sched_cell_params_t* cc_cfg = nullptr; const sched_cell_params_t* cc_cfg = nullptr;
srslog::basic_logger& logger; srslog::basic_logger& logger;
rrc_interface_mac* rrc = nullptr; rrc_interface_mac* rrc = nullptr;
std::map<uint16_t, std::unique_ptr<sched_ue> >* ue_db = nullptr; sched_ue_list* ue_db = nullptr;
const uint32_t enb_cc_idx; const uint32_t enb_cc_idx;
// Subframe scheduling logic // Subframe scheduling logic
srsran::circular_array<sf_sched, TTIMOD_SZ> sf_scheds; srsran::circular_array<sf_sched, TTIMOD_SZ> sf_scheds;
@ -131,9 +132,9 @@ private:
const sched_cell_params_t* cc_cfg = nullptr; const sched_cell_params_t* cc_cfg = nullptr;
sched_ue_list* ue_db = nullptr; sched_ue_list* ue_db = nullptr;
std::deque<pending_rar_t> pending_rars; srsran::deque<pending_rar_t> pending_rars;
uint32_t rar_aggr_level = 2; uint32_t rar_aggr_level = 2;
static const uint32_t PRACH_RAR_OFFSET = 3; // TS 36.321 Sec. 5.1.4 static const uint32_t PRACH_RAR_OFFSET = 3; // TS 36.321 Sec. 5.1.4
}; };
} // namespace srsenb } // namespace srsenb

@ -23,15 +23,14 @@
#define SRSENB_SCHEDULER_UE_H #define SRSENB_SCHEDULER_UE_H
#include "sched_common.h" #include "sched_common.h"
#include "srsran/srslog/srslog.h"
#include <map>
#include <vector>
#include "sched_ue_ctrl/sched_lch.h" #include "sched_ue_ctrl/sched_lch.h"
#include "sched_ue_ctrl/sched_ue_cell.h" #include "sched_ue_ctrl/sched_ue_cell.h"
#include "sched_ue_ctrl/tpc.h" #include "sched_ue_ctrl/tpc.h"
#include "srsenb/hdr/common/common_enb.h"
#include "srsran/srslog/srslog.h"
#include <bitset> #include <bitset>
#include <deque> #include <map>
#include <vector>
namespace srsenb { namespace srsenb {
@ -90,10 +89,12 @@ public:
uint32_t get_required_prb_ul(uint32_t enb_cc_idx, uint32_t req_bytes); 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); /// Get total pending bytes to be transmitted in DL.
srsran::interval<uint32_t> get_requested_dl_bytes(uint32_t enb_cc_idx); /// The amount of CEs to transmit depends on whether enb_cc_idx is UE's PCell
uint32_t get_pending_dl_rlc_data() const; uint32_t get_pending_dl_bytes(uint32_t enb_cc_idx);
uint32_t get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs = -1) const; 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_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); uint32_t get_pending_ul_new_data(tti_point tti_tx_ul, int this_enb_cc_idx);
@ -147,6 +148,8 @@ public:
bool pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_pdcch) const; bool pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_pdcch) const;
private: private:
srsran::interval<uint32_t> get_requested_dl_bytes(uint32_t enb_cc_idx);
bool is_sr_triggered(); bool is_sr_triggered();
tbs_info allocate_new_dl_mac_pdu(sched_interface::dl_sched_data_t* data, tbs_info allocate_new_dl_mac_pdu(sched_interface::dl_sched_data_t* data,
@ -216,7 +219,7 @@ private:
std::vector<sched_ue_cell> cells; ///< List of eNB cells that may be configured/activated/deactivated for the UE std::vector<sched_ue_cell> cells; ///< List of eNB cells that may be configured/activated/deactivated for the UE
}; };
using sched_ue_list = std::map<uint16_t, std::unique_ptr<sched_ue> >; using sched_ue_list = rnti_map_t<std::unique_ptr<sched_ue> >;
} // namespace srsenb } // namespace srsenb

@ -22,10 +22,10 @@
#ifndef SRSRAN_SCHED_LCH_H #ifndef SRSRAN_SCHED_LCH_H
#define SRSRAN_SCHED_LCH_H #define SRSRAN_SCHED_LCH_H
#include "srsran/adt/pool/cached_alloc.h"
#include "srsran/interfaces/sched_interface.h" #include "srsran/interfaces/sched_interface.h"
#include "srsran/mac/pdu.h" #include "srsran/mac/pdu.h"
#include "srsran/srslog/srslog.h" #include "srsran/srslog/srslog.h"
#include <deque>
namespace srsenb { namespace srsenb {
@ -68,7 +68,7 @@ public:
// Control Element Command queue // Control Element Command queue
using ce_cmd = srsran::dl_sch_lcid; using ce_cmd = srsran::dl_sch_lcid;
std::deque<ce_cmd> pending_ces; srsran::deque<ce_cmd> pending_ces;
private: private:
struct ue_bearer_t { struct ue_bearer_t {

@ -119,14 +119,14 @@ public:
* @remark See TS 36.213 Section 5.1.1 * @remark See TS 36.213 Section 5.1.1
* @return accumulated TPC value {-1, 0, 1, 3} * @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 * Called during DCI format1/2A/A encoding to set PUCCH TPC command
* @remark See TS 36.213 Section 5.1.2 * @remark See TS 36.213 Section 5.1.2
* @return accumulated TPC value {-1, 0, 1, 3} * @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; } uint32_t max_ul_prbs() const { return max_prbs_cached; }
@ -147,18 +147,14 @@ private:
return 1; 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; float target_snr_dB = cc == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB;
auto& ch_snr = snr_estim_list[cc]; auto& ch_snr = snr_estim_list[cc];
assert(ch_snr.pending_delta == 0); // ensure called once per {cc,tti} assert(ch_snr.pending_delta == 0); // ensure called once per {cc,tti}
if (target_snr_dB < 0) { if (target_snr_dB < 0) {
// undefined target SINR case. Increase Tx power once per PHR, considering the number of allocable PRBs remains // undefined target sinr case.
// unchanged ch_snr.pending_delta = 0;
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;
}
} else { } else {
// target SINR is finite and there is power headroom // target SINR is finite and there is power headroom
float diff = target_snr_dB - ch_snr.snr_avg.value(); float diff = target_snr_dB - ch_snr.snr_avg.value();

@ -73,7 +73,7 @@ private:
uint32_t ul_nof_samples = 0; uint32_t ul_nof_samples = 0;
}; };
srsran::static_circular_map<uint16_t, ue_ctxt, SRSENB_MAX_UES> ue_history_db; rnti_map_t<ue_ctxt> ue_history_db;
struct ue_dl_prio_compare { struct ue_dl_prio_compare {
bool operator()(const ue_ctxt* lhs, const ue_ctxt* rhs) const; bool operator()(const ue_ctxt* lhs, const ue_ctxt* rhs) const;

@ -45,6 +45,8 @@ class rlc_interface_rrc;
class mac_interface_rrc; class mac_interface_rrc;
class phy_interface_rrc_lte; class phy_interface_rrc_lte;
class paging_manager;
static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE", static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE",
"WAIT FOR CON SETUP COMPLETE", "WAIT FOR CON SETUP COMPLETE",
"WAIT FOR SECURITY MODE COMPLETE", "WAIT FOR SECURITY MODE COMPLETE",
@ -84,7 +86,7 @@ public:
uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override; uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override;
// rrc_interface_rlc // 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; void max_retx_attempted(uint16_t rnti) override;
// rrc_interface_s1ap // rrc_interface_s1ap
@ -109,7 +111,7 @@ public:
asn1::s1ap::cause_c& cause) override; asn1::s1ap::cause_c& cause) override;
bool release_erabs(uint32_t rnti) override; bool release_erabs(uint32_t rnti) override;
int release_erab(uint16_t rnti, uint16_t erab_id) 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, void ho_preparation_complete(uint16_t rnti,
rrc::ho_prep_result result, rrc::ho_prep_result result,
const asn1::s1ap::ho_cmd_s& msg, const asn1::s1ap::ho_cmd_s& msg,
@ -172,9 +174,9 @@ private:
std::unique_ptr<enb_cell_common_list> cell_common_list; std::unique_ptr<enb_cell_common_list> cell_common_list;
// state // state
std::unique_ptr<freq_res_common_list> cell_res_list; std::unique_ptr<freq_res_common_list> cell_res_list;
std::map<uint16_t, unique_rnti_ptr<ue> > users; // NOTE: has to have fixed addr std::map<uint16_t, unique_rnti_ptr<ue> > users; // NOTE: has to have fixed addr
std::map<uint32_t, asn1::rrc::paging_record_s> pending_paging; std::unique_ptr<paging_manager> pending_paging;
void process_release_complete(uint16_t rnti); void process_release_complete(uint16_t rnti);
void rem_user(uint16_t rnti); void rem_user(uint16_t rnti);
@ -187,11 +189,9 @@ private:
void parse_ul_ccch(uint16_t rnti, srsran::unique_byte_buffer_t pdu); void parse_ul_ccch(uint16_t rnti, srsran::unique_byte_buffer_t pdu);
void send_rrc_connection_reject(uint16_t rnti); void send_rrc_connection_reject(uint16_t rnti);
uint32_t paging_tti = INVALID_TTI; const static int mcch_payload_len = 3000;
srsran::byte_buffer_t byte_buf_paging; int current_mcch_length = 0;
const static int mcch_payload_len = 3000; uint8_t mcch_payload_buffer[mcch_payload_len] = {};
int current_mcch_length = 0;
uint8_t mcch_payload_buffer[mcch_payload_len] = {};
typedef struct { typedef struct {
uint16_t rnti; uint16_t rnti;
uint32_t lcid; uint32_t lcid;
@ -199,13 +199,13 @@ private:
srsran::unique_byte_buffer_t pdu; srsran::unique_byte_buffer_t pdu;
} rrc_pdu; } rrc_pdu;
const static uint32_t LCID_EXIT = 0xffff0000; const static uint32_t LCID_EXIT = 0xffff0000;
const static uint32_t LCID_REM_USER = 0xffff0001; const static uint32_t LCID_REM_USER = 0xffff0001;
const static uint32_t LCID_REL_USER = 0xffff0002; const static uint32_t LCID_REL_USER = 0xffff0002;
const static uint32_t LCID_ACT_USER = 0xffff0004; const static uint32_t LCID_ACT_USER = 0xffff0004;
const static uint32_t LCID_RTX_USER = 0xffff0005; const static uint32_t LCID_RTX_USER = 0xffff0005;
const static uint32_t LCID_RADLINK_DL = 0xffff0006; const static uint32_t LCID_RADLINK_DL = 0xffff0006;
const static uint32_t LCID_RADLINK_UL = 0xffff0007; const static uint32_t LCID_RADLINK_UL = 0xffff0007;
bool running = false; bool running = false;
srsran::dyn_blocking_queue<rrc_pdu> rx_pdu_queue; srsran::dyn_blocking_queue<rrc_pdu> rx_pdu_queue;
@ -217,8 +217,6 @@ private:
asn1::rrc::sib_type7_s sib7; asn1::rrc::sib_type7_s sib7;
void rem_user_thread(uint16_t rnti); void rem_user_thread(uint16_t rnti);
std::mutex paging_mutex;
}; };
} // namespace srsenb } // namespace srsenb

@ -88,6 +88,9 @@ public:
bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_, gtpu_interface_rrc* gtpu_); 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, int add_erab(uint8_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos, const asn1::s1ap::erab_level_qos_params_s& qos,
const asn1::bounded_bitstring<1, 160, true, true>& addr, const asn1::bounded_bitstring<1, 160, true, true>& addr,

@ -70,6 +70,7 @@ struct rrc_cfg_t {
cell_list_t cell_list_nr; cell_list_t cell_list_nr;
uint32_t max_mac_dl_kos; uint32_t max_mac_dl_kos;
uint32_t max_mac_ul_kos; uint32_t max_mac_ul_kos;
uint32_t rlf_release_timer_ms;
}; };
constexpr uint32_t UE_PCELL_CC_IDX = 0; constexpr uint32_t UE_PCELL_CC_IDX = 0;

@ -33,6 +33,8 @@
#include "srsran/common/threads.h" #include "srsran/common/threads.h"
#include "srsran/common/timeout.h" #include "srsran/common/timeout.h"
#include "srsran/interfaces/gnb_interfaces.h" #include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/interfaces/gnb_ngap_interfaces.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include <map> #include <map>
#include <queue> #include <queue>

@ -0,0 +1,234 @@
/**
*
* \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;
}
}
}
/// 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)
* - "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 <typename Callable>
bool read_pdu_pcch(tti_point tti_tx_dl, const Callable& callable);
private:
struct pcch_info {
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();
}
};
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][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][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
uint32_t T;
uint32_t Nb;
uint32_t N;
uint32_t Ns;
srslog::basic_logger& logger;
mutable std::array<std::mutex, nof_paging_subframes> sf_idx_mutex;
std::vector<std::array<pcch_info, nof_paging_subframes> > pending_paging;
};
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][get_sf_idx_key(sf_idx)];
auto& record_list = pending_pcch.pcch_msg.msg.c1().paging().paging_record_list;
std::lock_guard<std::mutex> 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);
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)
{
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<std::mutex> 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);
if (not old_pcch.empty()) {
old_pcch.clear();
}
const pcch_info& pending_pcch = get_pcch_info(tti_tx_dl);
if (pending_pcch.empty()) {
return 0;
}
return pending_pcch.pdu->size();
}
template <typename Callable>
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<std::mutex> 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());
return false;
}
// Call callable for existing PCCH pdu
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;
}
return false;
}
} // namespace srsenb
#endif // SRSRAN_RRC_PAGING_H

@ -49,8 +49,9 @@ public:
std::string to_string(const activity_timeout_type_t& type); std::string to_string(const activity_timeout_type_t& type);
void set_activity_timeout(const activity_timeout_type_t type); void set_activity_timeout(const activity_timeout_type_t type);
void set_rlf_timeout();
void set_activity(); void set_activity();
void start_rlf_timer();
void stop_rlf_timer();
void set_radiolink_dl_state(bool crc_res); void set_radiolink_dl_state(bool crc_res);
void set_radiolink_ul_state(bool crc_res); void set_radiolink_ul_state(bool crc_res);
void activity_timer_expired(const activity_timeout_type_t type); void activity_timer_expired(const activity_timeout_type_t type);
@ -167,8 +168,8 @@ public:
private: private:
// args // args
srsran::timer_handler::unique_timer activity_timer; srsran::unique_timer activity_timer;
srsran::timer_handler::unique_timer rlf_timer; srsran::unique_timer rlf_release_timer;
/// cached ASN1 fields for RRC config update checking, and ease of context transfer during HO /// cached ASN1 fields for RRC config update checking, and ease of context transfer during HO
ue_var_cfg_t current_ue_cfg; ue_var_cfg_t current_ue_cfg;

@ -45,7 +45,6 @@ struct gtpu_header_t;
namespace srsenb { namespace srsenb {
class pdcp_interface_gtpu; class pdcp_interface_gtpu;
class stack_interface_gtpu_lte;
class gtpu_tunnel_manager class gtpu_tunnel_manager
{ {
@ -99,7 +98,7 @@ public:
using ue_lcid_tunnel_list = srsran::bounded_vector<lcid_tunnel, MAX_TUNNELS_PER_UE>; using ue_lcid_tunnel_list = srsran::bounded_vector<lcid_tunnel, MAX_TUNNELS_PER_UE>;
explicit gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger); 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); } bool has_teid(uint32_t teid) const { return tunnels.contains(teid); }
const tunnel* find_tunnel(uint32_t teid); const tunnel* find_tunnel(uint32_t teid);
@ -125,11 +124,12 @@ private:
using tunnel_ctxt_it = typename tunnel_list_t::iterator; using tunnel_ctxt_it = typename tunnel_list_t::iterator;
srsran::task_sched_handle task_sched; 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; srslog::basic_logger& logger;
srsran::static_circular_map<uint16_t, ue_lcid_tunnel_list, SRSENB_MAX_UES> ue_teidin_db; rnti_map_t<ue_lcid_tunnel_list> ue_teidin_db;
tunnel_list_t tunnels; tunnel_list_t tunnels;
}; };
using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state; using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state;
@ -143,12 +143,7 @@ public:
srsran::socket_manager_itf* rx_socket_handler_); srsran::socket_manager_itf* rx_socket_handler_);
~gtpu(); ~gtpu();
int init(std::string gtp_bind_addr_, int init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_);
std::string mme_addr_,
std::string m1u_multiaddr_,
std::string m1u_if_addr_,
pdcp_interface_gtpu* pdcp_,
bool enable_mbsfn = false);
void stop(); void stop();
// gtpu_interface_rrc // gtpu_interface_rrc
@ -177,7 +172,7 @@ private:
srsran::socket_manager_itf* rx_socket_handler = nullptr; srsran::socket_manager_itf* rx_socket_handler = nullptr;
srsran::task_queue_handle gtpu_queue; srsran::task_queue_handle gtpu_queue;
bool enable_mbsfn = false; gtpu_args_t args;
std::string gtp_bind_addr; std::string gtp_bind_addr;
std::string mme_addr; std::string mme_addr;
srsenb::pdcp_interface_gtpu* pdcp = nullptr; srsenb::pdcp_interface_gtpu* pdcp = nullptr;

@ -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<ng_setup_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<uint8_t> sdu, bool is_rx);
};
} // namespace srsenb
#endif

@ -74,7 +74,6 @@ public:
// rlc_interface_mac // rlc_interface_mac
int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); 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 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: private:
class user_interface : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc class user_interface : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc

@ -65,6 +65,7 @@ cell_list =
// target_pusch_sinr = -1; // target_pusch_sinr = -1;
// target_pucch_sinr = -1; // target_pucch_sinr = -1;
// allowed_meas_bw = 6; // allowed_meas_bw = 6;
// t304 = 2000; // in msec. possible values: 50, 100, 150, 200, 500, 1000, 2000
// CA cells // CA cells
scell_list = ( scell_list = (

@ -53,6 +53,17 @@ using namespace asn1::rrc;
namespace srsenb { namespace srsenb {
template <typename T>
bool contains_value(T value, const std::initializer_list<T>& 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) bool sib_is_present(const sched_info_list_l& l, sib_type_e sib_num)
{ {
for (uint32_t i = 0; i < l.size(); i++) { for (uint32_t i = 0; i < l.size(); i++) {
@ -763,6 +774,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)); 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); 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"); 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"]) { if (cellroot.exists("ho_active") and cellroot["ho_active"]) {
HANDLEPARSERCODE(parse_meas_cell_list(&cell_cfg.meas_cfg, cellroot["meas_cell_list"])); HANDLEPARSERCODE(parse_meas_cell_list(&cell_cfg.meas_cfg, cellroot["meas_cell_list"]));
@ -914,9 +927,9 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_
{ {
// Sanity checks // Sanity checks
ASSERT_VALID_CFG(not rrc_cfg_->cell_list.empty(), "No cell specified in rr.conf."); 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, ASSERT_VALID_CFG(args_->stack.mac.nof_prealloc_ues <= SRSENB_MAX_UES,
"mac.max_nof_ues=%d must be within [0, %d]", "mac.nof_prealloc_ues=%d must be within [0, %d]",
args_->stack.mac.max_nof_ues, args_->stack.mac.nof_prealloc_ues,
SRSENB_MAX_UES); SRSENB_MAX_UES);
// Check for a forced DL EARFCN or frequency (only valid for a single cell config (Xico's favorite feature)) // Check for a forced DL EARFCN or frequency (only valid for a single cell config (Xico's favorite feature))
@ -1156,8 +1169,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; rrc_cfg_->enb_id = args_->stack.s1ap.enb_id;
// Set max number of KOs // Set max number of KOs
rrc_cfg_->max_mac_dl_kos = args_->general.max_mac_dl_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_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 // Set sync queue capacity to 1 for ZMQ
if (args_->rf.device_name == "zmq") { if (args_->rf.device_name == "zmq") {

@ -23,6 +23,7 @@
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h> #include <unistd.h>
#include "srsran/common/common_helper.h" #include "srsran/common/common_helper.h"
@ -223,9 +224,11 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("expert.print_buffer_state", bpo::value<bool>(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds") ("expert.print_buffer_state", bpo::value<bool>(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds")
("expert.eea_pref_list", bpo::value<string>(&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.eea_pref_list", bpo::value<string>(&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<string>(&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.eia_pref_list", bpo::value<string>(&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<uint32_t>(&args->stack.mac.max_nof_ues)->default_value(8), "Maximum number of connected UEs") ("expert.nof_prealloc_ues", bpo::value<uint32_t>(&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<uint32_t>(&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_dl_kos", bpo::value<uint32_t>(&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<uint32_t>(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release") ("expert.max_mac_ul_kos", bpo::value<uint32_t>(&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<uint32_t>(&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<uint32_t>(&args->general.rlf_release_timer_ms)->default_value(4000), "Time taken by eNB to release UE context after it detects an RLF")
// eMBMS section // eMBMS section
@ -542,6 +545,10 @@ int main(int argc, char* argv[])
event_logger::configure(json_channel); event_logger::configure(json_channel);
} }
if (mlockall((uint32_t)MCL_CURRENT | (uint32_t)MCL_FUTURE) == -1) {
srsran::console("Failed to `mlockall`: {}", errno);
}
// Create eNB // Create eNB
unique_ptr<srsenb::enb> enb{new srsenb::enb(srslog::get_default_sink())}; unique_ptr<srsenb::enb> enb{new srsenb::enb(srslog::get_default_sink())};
if (enb->init(args) != SRSRAN_SUCCESS) { if (enb->init(args) != SRSRAN_SUCCESS) {

@ -24,7 +24,6 @@
#include <string.h> #include <string.h>
#include <string> #include <string>
#include <strings.h> #include <strings.h>
#include <sys/mman.h>
#include <unistd.h> #include <unistd.h>
#include "srsenb/hdr/phy/phy.h" #include "srsenb/hdr/phy/phy.h"
@ -114,8 +113,6 @@ int phy::init(const phy_args_t& args,
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
mlockall((uint32_t)MCL_CURRENT | (uint32_t)MCL_FUTURE);
// Add PHY lib log. // Add PHY lib log.
srslog::basic_levels log_lvl = srslog::str_to_basic_level(args.log.phy_lib_level); srslog::basic_levels log_lvl = srslog::str_to_basic_level(args.log.phy_lib_level);

@ -82,9 +82,9 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_)
rrc_cfg = rrc_cfg_; rrc_cfg = rrc_cfg_;
// Init RNTI and bearer memory pools // 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; 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 // setup logging for each layer
mac_logger.set_level(srslog::str_to_basic_level(args.log.mac_level)); mac_logger.set_level(srslog::str_to_basic_level(args.log.mac_level));
@ -142,12 +142,15 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_)
stack_logger.error("Couldn't initialize S1AP"); stack_logger.error("Couldn't initialize S1AP");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
if (gtpu.init(args.s1ap.gtp_bind_addr,
args.s1ap.mme_addr, gtpu_args_t gtpu_args;
args.embms.m1u_multiaddr, gtpu_args.embms_enable = args.embms.enable;
args.embms.m1u_if_addr, gtpu_args.embms_m1u_multiaddr = args.embms.m1u_multiaddr;
&pdcp, gtpu_args.embms_m1u_if_addr = args.embms.m1u_if_addr;
args.embms.enable)) { 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"); stack_logger.error("Couldn't initialize GTPU");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }

@ -93,7 +93,7 @@ bool mac::init(const mac_args_t& args_,
}; };
auto recycle_softbuffers = [](ue_cc_softbuffers& softbuffers) { softbuffers.clear(); }; auto recycle_softbuffers = [](ue_cc_softbuffers& softbuffers) { softbuffers.clear(); };
softbuffer_pool.reset(new srsran::background_obj_pool<ue_cc_softbuffers>( softbuffer_pool.reset(new srsran::background_obj_pool<ue_cc_softbuffers>(
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 // Pre-alloc UE objects for first attaching users
prealloc_ue(10); prealloc_ue(10);
@ -478,8 +478,8 @@ uint16_t mac::allocate_ue()
logger.info("RACH ignored as eNB is being shutdown"); logger.info("RACH ignored as eNB is being shutdown");
return SRSRAN_INVALID_RNTI; return SRSRAN_INVALID_RNTI;
} }
if (ue_db.size() >= 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", args.max_nof_ues); logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", SRSENB_MAX_UES);
return SRSRAN_INVALID_RNTI; return SRSRAN_INVALID_RNTI;
} }
auto ret = ue_db.insert(rnti, std::move(ue_ptr)); auto ret = ue_db.insert(rnti, std::move(ue_ptr));
@ -736,7 +736,7 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list)
} else { } else {
dl_sched_res->pdsch[n].softbuffer_tx[0] = &common_buffers[enb_cc_idx].pcch_softbuffer_tx; 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; 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) { 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); pcap->write_dl_pch(dl_sched_res->pdsch[n].data[0], sched_result.bc[i].tbs, true, tti_tx_dl, enb_cc_idx);

@ -116,14 +116,14 @@ int sched::ue_cfg(uint16_t rnti, const sched_interface::ue_cfg_t& ue_cfg)
// Add new user case // Add new user case
std::unique_ptr<sched_ue> ue{new sched_ue(rnti, sched_cell_params, ue_cfg)}; std::unique_ptr<sched_ue> ue{new sched_ue(rnti, sched_cell_params, ue_cfg)};
std::lock_guard<std::mutex> lock(sched_mutex); std::lock_guard<std::mutex> lock(sched_mutex);
ue_db.insert(std::make_pair(rnti, std::move(ue))); ue_db.insert(rnti, std::move(ue));
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int sched::ue_rem(uint16_t rnti) int sched::ue_rem(uint16_t rnti)
{ {
std::lock_guard<std::mutex> lock(sched_mutex); std::lock_guard<std::mutex> lock(sched_mutex);
if (ue_db.count(rnti) > 0) { if (ue_db.contains(rnti)) {
ue_db.erase(rnti); ue_db.erase(rnti);
} else { } else {
Error("User rnti=0x%x not found", rnti); Error("User rnti=0x%x not found", rnti);
@ -134,7 +134,8 @@ int sched::ue_rem(uint16_t rnti)
bool sched::ue_exists(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) void sched::phy_config_enabled(uint16_t rnti, bool enabled)
@ -360,17 +361,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 // Common way to access ue_db elements in a read locking way
template <typename Func> template <typename Func>
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<std::mutex> lock(sched_mutex); std::lock_guard<std::mutex> lock(sched_mutex);
auto it = ue_db.find(rnti); auto it = ue_db.find(rnti);
if (it != ue_db.end()) { if (it != ue_db.end()) {
f(*it->second); f(*it->second);
} else { } else {
if (func_name != nullptr) { if (log_fail) {
Error("User rnti=0x%x not found. Failed to call %s.", rnti, func_name); if (func_name != nullptr) {
} else { Error("SCHED: User rnti=0x%x not found. Failed to call %s.", rnti, func_name);
Error("User rnti=0x%x not found.", rnti); } else {
Error("SCHED: User rnti=0x%x not found.", rnti);
}
} }
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }

@ -671,7 +671,7 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t&
continue; continue;
} }
sched_ue* user = ue_it->second.get(); 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); 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(); bool is_newtx = dl_harq.is_empty();
@ -687,7 +687,7 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t&
data_alloc.pid, data_alloc.pid,
data_alloc.user_mask, data_alloc.user_mask,
tbs, 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)); logger.warning("%s", srsran::to_c_str(str_buffer));
continue; continue;
} }
@ -707,7 +707,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), dl_harq.nof_retx(0) + dl_harq.nof_retx(1),
tbs, tbs,
data_before, 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()); get_tti_tx_dl());
logger.info("%s", srsran::to_c_str(str_buffer)); logger.info("%s", srsran::to_c_str(str_buffer));
} }

@ -311,8 +311,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) { 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].tpc_fsm.set_snr(snr, ul_ch_code);
cells[enb_cc_idx].ul_cqi = srsran_cqi_from_snr(snr); if (ul_ch_code == tpc::PUSCH_CODE) {
cells[enb_cc_idx].ul_cqi_tti_rx = tti_rx; cells[enb_cc_idx].ul_cqi = srsran_cqi_from_snr(snr);
cells[enb_cc_idx].ul_cqi_tti_rx = tti_rx;
}
} else { } else {
logger.warning("Received SNR info for invalid cell index %d", enb_cc_idx); logger.warning("Received SNR info for invalid cell index %d", enb_cc_idx);
} }
@ -776,6 +778,11 @@ rbg_interval sched_ue::get_required_dl_rbgs(uint32_t enb_cc_idx)
return {min_pending_rbg, max_pending_rbg}; 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. * Returns the range (min,max) of possible MAC PDU sizes.
* - the lower boundary value is set based on the following conditions: * - the lower boundary value is set based on the following conditions:

@ -307,6 +307,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) uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes)
{ {
const static int MIN_ALLOC_BYTES = 10;
if (req_bytes == 0) { if (req_bytes == 0) {
return 0; return 0;
} }
@ -317,11 +318,20 @@ 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 // find nof prbs that lead to a tbs just above req_bytes
int target_tbs = static_cast<int>(req_bytes) + 4; int target_tbs = std::max(static_cast<int>(req_bytes) + 4, MIN_ALLOC_BYTES);
uint32_t max_prbs = std::min(cell.tpc_fsm.max_ul_prbs(), cell.cell_cfg->nof_prb()); uint32_t max_prbs = std::min(cell.tpc_fsm.max_ul_prbs(), cell.cell_cfg->nof_prb());
std::tuple<uint32_t, int, uint32_t, int> ret = std::tuple<uint32_t, int, uint32_t, int> ret =
false_position_method(1U, max_prbs, target_tbs, compute_tbs_approx, [](int y) { return y == SRSRAN_ERROR; }); 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. 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);
}
while (!srsran_dft_precoding_valid_prb(req_prbs) && req_prbs < cell.cell_cfg->nof_prb()) { while (!srsran_dft_precoding_valid_prb(req_prbs) && req_prbs < cell.cell_cfg->nof_prb()) {
req_prbs++; req_prbs++;
} }

@ -33,11 +33,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); fairness_coeff = std::stof(sched_args.sched_policy_args);
} }
std::vector<ue_ctxt *> dl_storage; std::vector<ue_ctxt*> dl_storage;
dl_storage.reserve(SRSENB_MAX_UES); dl_storage.reserve(SRSENB_MAX_UES);
dl_queue = ue_dl_queue_t(ue_dl_prio_compare{}, std::move(dl_storage)); dl_queue = ue_dl_queue_t(ue_dl_prio_compare{}, std::move(dl_storage));
std::vector<ue_ctxt *> ul_storage; std::vector<ue_ctxt*> ul_storage;
ul_storage.reserve(SRSENB_MAX_UES); ul_storage.reserve(SRSENB_MAX_UES);
ul_queue = ue_ul_queue_t(ue_ul_prio_compare{}, std::move(ul_storage)); ul_queue = ue_ul_queue_t(ue_ul_prio_compare{}, std::move(ul_storage));
} }
@ -47,7 +47,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()}; current_tti_rx = tti_point{tti_sched->get_tti_rx()};
// remove deleted users from history // remove deleted users from history
for (auto it = ue_history_db.begin(); it != ue_history_db.end();) { 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); it = ue_history_db.erase(it);
} else { } else {
++it; ++it;

@ -477,7 +477,11 @@ bool ue::process_ce(srsran::sch_subh* subh)
rrc->upd_user(rnti, old_rnti); rrc->upd_user(rnti, old_rnti);
rnti = old_rnti; rnti = old_rnti;
} else { } 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; break;
case srsran::ul_sch_lcid::TRUNC_BSR: case srsran::ul_sch_lcid::TRUNC_BSR:

@ -22,6 +22,7 @@
#include "srsenb/hdr/stack/rrc/rrc.h" #include "srsenb/hdr/stack/rrc/rrc.h"
#include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h" #include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h"
#include "srsenb/hdr/stack/rrc/rrc_mobility.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/asn1_utils.h"
#include "srsran/asn1/rrc_utils.h" #include "srsran/asn1/rrc_utils.h"
#include "srsran/common/bcd_helpers.h" #include "srsran/common/bcd_helpers.h"
@ -90,6 +91,9 @@ int32_t rrc::init(const rrc_cfg_t& cfg_,
logger.info("Inactivity timeout: %d ms", cfg.inactivity_timeout_ms); logger.info("Inactivity timeout: %d ms", cfg.inactivity_timeout_ms);
logger.info("Max consecutive MAC KOs: %d", cfg.max_mac_dl_kos); 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; running = true;
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
@ -457,132 +461,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) void rrc::add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id)
{ {
std::lock_guard<std::mutex> 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) { if (ue_paging_id.type().value == asn1::s1ap::ue_paging_id_c::types_opts::imsi) {
paging_elem.ue_id.set_imsi(); pending_paging->add_imsi_paging(ueid, ue_paging_id.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");
} else { } else {
paging_elem.ue_id.set_s_tmsi(); pending_paging->add_tmsi_paging(ueid, ue_paging_id.s_tmsi().mmec[0], ue_paging_id.s_tmsi().m_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);
} }
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) 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}}; *payload_len = pending_paging->pending_pcch_bytes(tti_point(tti));
return *payload_len > 0;
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<uint32_t> ue_to_remove;
{
std::lock_guard<std::mutex> 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);
}
}
if (paging_rec->paging_record_list.size() > 0) { void rrc::read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t buffer_size)
byte_buf_paging.clear(); {
asn1::bit_ref bref(byte_buf_paging.msg, byte_buf_paging.get_tailroom()); auto read_func = [this, payload, buffer_size](srsran::const_byte_span pdu, const pcch_msg_s& msg, bool first_tx) {
if (pcch_msg.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { // copy PCCH pdu to buffer
logger.error("Failed to pack PCCH"); 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; return false;
} }
byte_buf_paging.N_bytes = (uint32_t)bref.distance_bytes(); std::copy(pdu.begin(), pdu.end(), payload);
uint32_t N_bits = (uint32_t)bref.distance();
if (payload_len) { if (first_tx) {
*payload_len = byte_buf_paging.N_bytes; 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 true;
} };
return false;
}
void rrc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) pending_paging->read_pdu_pcch(tti_point(tti_tx_dl), read_func);
{
std::lock_guard<std::mutex> lock(paging_mutex);
if (byte_buf_paging.N_bytes <= buffer_size) {
memcpy(payload, byte_buf_paging.msg, byte_buf_paging.N_bytes);
}
} }
/******************************************************************************* /*******************************************************************************
@ -833,7 +744,7 @@ uint32_t rrc::generate_sibs()
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
asn1::bit_ref bref(sib_buffer->msg, sib_buffer->get_tailroom()); 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); logger.error("Failed to pack SIB message %d", msg_index);
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -987,7 +898,10 @@ int rrc::pack_mcch()
const int rlc_header_len = 1; const int rlc_header_len = 1;
asn1::bit_ref bref(&mcch_payload_buffer[rlc_header_len], sizeof(mcch_payload_buffer) - rlc_header_len); 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 = bref.distance_bytes(&mcch_payload_buffer[1]);
current_mcch_length = current_mcch_length + rlc_header_len; current_mcch_length = current_mcch_length + rlc_header_len;
return current_mcch_length; return current_mcch_length;

@ -114,6 +114,8 @@ bool security_cfg_handler::set_security_capabilities(const asn1::s1ap::ue_securi
case srsran::INTEGRITY_ALGORITHM_ID_EIA0: case srsran::INTEGRITY_ALGORITHM_ID_EIA0:
// Null integrity is not supported // Null integrity is not supported
logger.info("Skipping EIA0 as RRC integrity algorithm. 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; break;
case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1: case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1:
// “first bit” 128-EIA1, // “first bit” 128-EIA1,
@ -211,6 +213,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")) 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, int bearer_cfg_handler::add_erab(uint8_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos, const asn1::s1ap::erab_level_qos_params_s& qos,
const asn1::bounded_bitstring<1, 160, true, true>& addr, const asn1::bounded_bitstring<1, 160, true, true>& addr,
@ -311,6 +321,8 @@ int bearer_cfg_handler::release_erab(uint8_t erab_id)
srsran::rem_rrc_obj_id(current_drbs, drb_id); srsran::rem_rrc_obj_id(current_drbs, drb_id);
rem_gtpu_bearer(erab_id);
erabs.erase(it); erabs.erase(it);
erab_info_list.erase(erab_id); erab_info_list.erase(erab_id);
@ -319,8 +331,6 @@ int bearer_cfg_handler::release_erab(uint8_t erab_id)
void bearer_cfg_handler::release_erabs() void bearer_cfg_handler::release_erabs()
{ {
// TODO: notify GTPU layer for each ERAB
erabs.clear();
while (not erabs.empty()) { while (not erabs.empty()) {
release_erab(erabs.begin()->first); release_erab(erabs.begin()->first);
} }
@ -387,13 +397,7 @@ srsran::expected<uint32_t> bearer_cfg_handler::add_gtpu_bearer(uint32_t
void bearer_cfg_handler::rem_gtpu_bearer(uint32_t erab_id) void bearer_cfg_handler::rem_gtpu_bearer(uint32_t erab_id)
{ {
auto it = erabs.find(erab_id); gtpu->rem_bearer(rnti, erab_id - 2);
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);
}
} }
void bearer_cfg_handler::fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg) void bearer_cfg_handler::fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg)

@ -96,6 +96,8 @@ uint16_t compute_mac_i(uint16_t crnti,
// Compute MAC-I // Compute MAC-I
switch (integ_algo) { switch (integ_algo) {
case srsran::INTEGRITY_ALGORITHM_ID_EIA0:
return 0;
case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1: case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1:
srsran::security_128_eia1(&k_rrc_int[16], srsran::security_128_eia1(&k_rrc_int[16],
0xffffffff, // 32-bit all to ones 0xffffffff, // 32-bit all to ones
@ -115,7 +117,7 @@ uint16_t compute_mac_i(uint16_t crnti,
mac_key); mac_key);
break; break;
default: 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]); uint16_t short_mac_i = (((uint16_t)mac_key[2] << 8u) | (uint16_t)mac_key[3]);
@ -498,7 +500,7 @@ void rrc::ue::rrc_mobility::fill_mobility_reconf_common(asn1::rrc::dl_dcch_msg_s
recfg_r8.mob_ctrl_info_present = true; recfg_r8.mob_ctrl_info_present = true;
auto& mob_info = recfg_r8.mob_ctrl_info; auto& mob_info = recfg_r8.mob_ctrl_info;
mob_info.target_pci = target_cell.cell_cfg.pci; 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.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.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; mob_info.rr_cfg_common.prach_cfg.root_seq_idx = target_cell.sib2.rr_cfg_common.prach_cfg.root_seq_idx;

@ -224,7 +224,10 @@ int32_t rrc_nr::generate_sibs()
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
asn1::bit_ref bref(mib_buf->msg, mib_buf->get_tailroom()); 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(); mib_buf->N_bytes = bref.distance_bytes();
logger.debug(mib_buf->msg, mib_buf->N_bytes, "MIB payload (%d B)", mib_buf->N_bytes); logger.debug(mib_buf->msg, mib_buf->N_bytes, "MIB payload (%d B)", mib_buf->N_bytes);
mib_buffer = std::move(mib_buf); mib_buffer = std::move(mib_buf);
@ -262,7 +265,10 @@ int32_t rrc_nr::generate_sibs()
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
asn1::bit_ref bref(sib->msg, sib->get_tailroom()); 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->N_bytes = bref.distance_bytes();
sib_buffer.push_back(std::move(sib)); sib_buffer.push_back(std::move(sib));

@ -65,10 +65,14 @@ int rrc::ue::init()
// Configure // Configure
apply_setup_phy_common(parent->cfg.sibs[1].sib2().rr_cfg_common, true); apply_setup_phy_common(parent->cfg.sibs[1].sib2().rr_cfg_common, true);
rlf_timer = parent->task_sched.get_unique_timer(); rlf_release_timer = parent->task_sched.get_unique_timer();
activity_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_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<rrc_mobility>(rnti, this); mobility_handler = make_rnti_obj<rrc_mobility>(rnti, this);
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
@ -102,6 +106,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) void rrc::ue::set_radiolink_dl_state(bool crc_res)
{ {
parent->logger.debug( parent->logger.debug(
@ -111,13 +129,13 @@ void rrc::ue::set_radiolink_dl_state(bool crc_res)
if (crc_res) { if (crc_res) {
consecutive_kos_dl = 0; consecutive_kos_dl = 0;
consecutive_kos_ul = 0; consecutive_kos_ul = 0;
rlf_timer.stop(); stop_rlf_timer();
return; return;
} }
// Count KOs in MAC and trigger release if it goes above a certain value. // Count KOs in MAC and trigger release if it goes above a certain value.
// This is done to detect out-of-coverage UEs // 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 // RLF timer already running, no need to count KOs
return; return;
} }
@ -138,13 +156,13 @@ void rrc::ue::set_radiolink_ul_state(bool crc_res)
if (crc_res) { if (crc_res) {
consecutive_kos_dl = 0; consecutive_kos_dl = 0;
consecutive_kos_ul = 0; consecutive_kos_ul = 0;
rlf_timer.stop(); stop_rlf_timer();
return; return;
} }
// Count KOs in MAC and trigger release if it goes above a certain value. // Count KOs in MAC and trigger release if it goes above a certain value.
// This is done to detect out-of-coverage UEs // 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 // RLF timer already running, no need to count KOs
return; return;
} }
@ -158,7 +176,7 @@ void rrc::ue::set_radiolink_ul_state(bool crc_res)
void rrc::ue::activity_timer_expired(const activity_timeout_type_t type) void rrc::ue::activity_timer_expired(const activity_timeout_type_t type)
{ {
rlf_timer.stop(); stop_rlf_timer();
if (parent) { if (parent) {
parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed()); parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed());
@ -191,7 +209,7 @@ void rrc::ue::rlf_timer_expired()
{ {
activity_timer.stop(); activity_timer.stop();
if (parent) { 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)) { if (parent->s1ap->user_exists(rnti)) {
parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost); parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost);
@ -212,28 +230,12 @@ void rrc::ue::max_retx_reached()
parent->logger.info("Max retx reached for rnti=0x%x", rnti); parent->logger.info("Max retx reached for rnti=0x%x", rnti);
// Give UE time to start re-establishment // Give UE time to start re-establishment
rlf_timer.run(); start_rlf_timer();
mac_ctrl.handle_max_retx(); 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<uint32_t>((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) void rrc::ue::set_activity_timeout(const activity_timeout_type_t type)
{ {
uint32_t deadline_s = 0; uint32_t deadline_s = 0;
@ -523,10 +525,16 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
static_cast<unsigned>(rrc_event_type::con_reest_req), static_cast<unsigned>(rrc_event_type::con_reest_req),
static_cast<unsigned>(procedure_result_code::none), static_cast<unsigned>(procedure_result_code::none),
rnti); 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()) { if (not parent->s1ap->is_mme_connected()) {
parent->logger.error("MME isn't connected. Sending Connection Reject"); 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; return;
} }
parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s", parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s",
@ -535,7 +543,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(), (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()); msg->crit_exts.rrc_conn_reest_request_r8().reest_cause.to_string());
if (is_idle()) { 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; 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); const enb_cell_common* old_cell = parent->cell_common_list->get_pci(old_pci);
auto ue_it = parent->users.find(old_rnti); auto ue_it = parent->users.find(old_rnti);
@ -590,9 +597,13 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
} else { } else {
parent->logger.error("Received ConnectionReestablishment for rnti=0x%x without context", old_rnti); parent->logger.error("Received ConnectionReestablishment for rnti=0x%x without context", old_rnti);
send_connection_reest_rej(procedure_result_code::error_unknown_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 { } else {
parent->logger.error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE", rnti); 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);
} }
} }
@ -663,8 +674,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_integrity(rnti, srb_to_lcid(lte_srb::srb1));
parent->pdcp->enable_encryption(rnti, srb_to_lcid(lte_srb::srb1)); parent->pdcp->enable_encryption(rnti, srb_to_lcid(lte_srb::srb1));
// Reestablish current DRBs during ConnectionReconfiguration // Reestablish E-RABs of old rnti during ConnectionReconfiguration
bearer_list = std::move(parent->users.at(old_reest_rnti)->bearer_list); bearer_list.reestablish_bearers(std::move(parent->users.at(old_reest_rnti)->bearer_list));
// remove old RNTI // remove old RNTI
parent->rem_user_thread(old_reest_rnti); parent->rem_user_thread(old_reest_rnti);
@ -920,7 +931,10 @@ bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg)
dest.resize(bref2.distance_bytes()); dest.resize(bref2.distance_bytes());
memcpy(dest.data(), pdu->msg, bref2.distance_bytes()); memcpy(dest.data(), pdu->msg, bref2.distance_bytes());
bref2 = asn1::bit_ref{pdu->msg, pdu->get_tailroom()}; 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(); pdu->N_bytes = bref2.distance_bytes();
parent->s1ap->send_ue_cap_info_indication(rnti, std::move(pdu)); parent->s1ap->send_ue_cap_info_indication(rnti, std::move(pdu));
} }

@ -21,5 +21,5 @@
set(SOURCES gtpu.cc pdcp.cc rlc.cc s1ap.cc) set(SOURCES gtpu.cc pdcp.cc rlc.cc s1ap.cc)
add_library(srsenb_upper STATIC ${SOURCES}) 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}) add_library(srsgnb_upper STATIC ${SOURCES})

@ -45,9 +45,10 @@ gtpu_tunnel_manager::gtpu_tunnel_manager(srsran::task_sched_handle task_sched_,
logger(logger), task_sched(task_sched_), tunnels(1) 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) const gtpu_tunnel_manager::tunnel* gtpu_tunnel_manager::find_tunnel(uint32_t teid)
@ -99,7 +100,11 @@ const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid,
tun->spgw_addr = spgw_addr; tun->spgw_addr = spgw_addr;
if (not ue_teidin_db.contains(rnti)) { 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]; auto& ue_tunnels = ue_teidin_db[rnti];
@ -130,16 +135,20 @@ bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti)
auto* old_rnti_ptr = find_rnti_tunnels(old_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); logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti);
// Change RNTI bearers map // create new RNTI and update TEIDs of old rnti to reflect new rnti
ue_teidin_db.insert(new_rnti, std::move(*old_rnti_ptr)); if (not ue_teidin_db.insert(new_rnti, ue_lcid_tunnel_list())) {
ue_teidin_db.erase(old_rnti); logger.error("Failure to create new rnti=0x%x", new_rnti);
return false;
// Change TEID in existing tunnels }
auto* new_rnti_ptr = find_rnti_tunnels(new_rnti); std::swap(ue_teidin_db[new_rnti], *old_rnti_ptr);
for (lcid_tunnel& bearer : *new_rnti_ptr) { auto& new_rnti_obj = ue_teidin_db[new_rnti];
for (lcid_tunnel& bearer : new_rnti_obj) {
tunnels[bearer.teid].rnti = new_rnti; tunnels[bearer.teid].rnti = new_rnti;
} }
// Leave old_rnti as zombie to be removed later
old_rnti_ptr->clear();
return true; return true;
} }
@ -160,22 +169,20 @@ bool gtpu_tunnel_manager::remove_tunnel(uint32_t teidin)
bool gtpu_tunnel_manager::remove_bearer(uint16_t rnti, uint32_t lcid) bool gtpu_tunnel_manager::remove_bearer(uint16_t rnti, uint32_t lcid)
{ {
srsran::span<lcid_tunnel> to_rem = find_rnti_lcid_tunnels(rnti, lcid);
if (to_rem.empty()) {
return false;
}
logger.info("Removing rnti=0x%x,lcid=%d", rnti, lcid); logger.info("Removing rnti=0x%x,lcid=%d", rnti, lcid);
bool removed = false;
for (lcid_tunnel& lcid_tun : to_rem) { for (srsran::span<lcid_tunnel> to_rem = find_rnti_lcid_tunnels(rnti, lcid); not to_rem.empty();
bool ret = tunnels.erase(lcid_tun.teid); to_rem = find_rnti_lcid_tunnels(rnti, lcid)) {
uint32_t teid = to_rem.front().teid;
bool ret = remove_tunnel(teid);
srsran_expect(ret, srsran_expect(ret,
"Inconsistency detected between internal data structures for rnti=0x%x,lcid=%d," TEID_IN_FMT, "Inconsistency detected between internal data structures for rnti=0x%x,lcid=%d," TEID_IN_FMT,
rnti, rnti,
lcid, lcid,
lcid_tun.teid); teid);
removed |= ret;
} }
ue_teidin_db[rnti].erase(to_rem.begin(), to_rem.end()); return removed;
return true;
} }
bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti) bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti)
@ -248,13 +255,21 @@ 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
before_tun.rx_timer = task_sched.get_unique_timer(); // TS 36.300 - On detection of the "end marker", the target eNB may also initiate the release of the data forwarding
before_tun.rx_timer.set(500, [this, before_teid](uint32_t tid) { // resource. However, the release of the data forwarding resource is implementation dependent and could
// This will self-destruct the callback object // also be based on other mechanisms (e.g. timer-based mechanism).
remove_tunnel(before_teid); if (gtpu_args->indirect_tunnel_timeout_msec > 0) {
}); before_tun.rx_timer = task_sched.get_unique_timer();
before_tun.rx_timer.run(); 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) void gtpu_tunnel_manager::handle_rx_pdcp_sdu(uint32_t teid)
@ -332,18 +347,14 @@ gtpu::~gtpu()
stop(); stop();
} }
int gtpu::init(std::string gtp_bind_addr_, int gtpu::init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_)
std::string mme_addr_,
std::string m1u_multiaddr_,
std::string m1u_if_addr_,
srsenb::pdcp_interface_gtpu* pdcp_,
bool enable_mbsfn_)
{ {
args = gtpu_args;
pdcp = pdcp_; pdcp = pdcp_;
gtp_bind_addr = gtp_bind_addr_; gtp_bind_addr = gtpu_args.gtp_bind_addr;
mme_addr = mme_addr_; mme_addr = gtpu_args.mme_addr;
tunnels.init(pdcp); tunnels.init(args, pdcp);
char errbuf[128] = {}; char errbuf[128] = {};
@ -383,9 +394,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)); rx_socket_handler->add_socket_handler(fd, srsran::make_sdu_handler(logger, gtpu_queue, rx_callback));
// Start MCH socket if enabled // Start MCH socket if enabled
enable_mbsfn = enable_mbsfn_; if (args.embms_enable) {
if (enable_mbsfn) { if (not m1u.init(args.embms_m1u_multiaddr, args.embms_m1u_if_addr)) {
if (not m1u.init(m1u_multiaddr_, m1u_if_addr_)) {
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
} }

@ -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

@ -188,11 +188,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); 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 rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes)
{ {
int ret; int ret;

@ -1815,7 +1815,10 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt
return false; return false;
} }
asn1::bit_ref bref(buf->msg, buf->get_tailroom()); 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(); buf->N_bytes = bref.distance_bytes();
// Save message to PCAP // Save message to PCAP

@ -71,6 +71,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) target_link_libraries(sched_dci_test srsran_common srsenb_mac srsran_mac sched_test_common)
add_test(sched_dci_test sched_dci_test) 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) add_executable(sched_benchmark_test sched_benchmark.cc)
target_link_libraries(sched_benchmark_test srsran_common srsenb_mac srsran_mac sched_test_common) target_link_libraries(sched_benchmark_test srsran_common srsenb_mac srsran_mac sched_test_common)
add_test(sched_benchmark_test sched_benchmark_test) add_test(sched_benchmark_test sched_benchmark_test)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save