mirror of https://github.com/PentHertz/srsLTE.git
Merge branch 'next' into agpl_next
This commit is contained in:
commit
5fe9a14aa5
|
@ -37,9 +37,20 @@ class static_circular_map
|
|||
using obj_t = std::pair<K, T>;
|
||||
|
||||
public:
|
||||
using key_type = K;
|
||||
using mapped_type = T;
|
||||
using value_type = std::pair<K, T>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
class iterator
|
||||
{
|
||||
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(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;
|
||||
for (uint32_t i = 0; i < pool.size(); i++) {
|
||||
if (std::find(free_list.cbegin(), free_list.cend(), pool[i]) == free_list.cend()) {
|
||||
buffer_cnt[strlen(used[i]->debug_name) ? pool[i]->debug_name : "Undefined"]++;
|
||||
buffer_cnt[strlen(pool[i]->debug_name) ? pool[i]->debug_name : "Undefined"]++;
|
||||
}
|
||||
}
|
||||
std::map<std::string, uint32_t>::iterator it;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <cstdint>
|
||||
|
||||
//#define SRSRAN_BUFFER_POOL_LOG_ENABLED
|
||||
#define SRSRAN_BUFFER_POOL_LOG_NAME_LEN 128
|
||||
|
||||
namespace srsran {
|
||||
|
||||
|
|
|
@ -27,6 +27,15 @@
|
|||
|
||||
namespace srsenb {
|
||||
|
||||
struct gtpu_args_t {
|
||||
std::string gtp_bind_addr;
|
||||
std::string mme_addr;
|
||||
std::string embms_m1u_multiaddr;
|
||||
std::string embms_m1u_if_addr;
|
||||
bool embms_enable = false;
|
||||
uint32_t indirect_tunnel_timeout_msec = 0;
|
||||
};
|
||||
|
||||
// GTPU interface for 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
|
||||
sched_interface::sched_args_t sched;
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -35,8 +35,6 @@ public:
|
|||
* Segmentation happens in this function. RLC PDU is stored in payload. */
|
||||
virtual int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0;
|
||||
|
||||
virtual void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) = 0;
|
||||
|
||||
/* MAC calls RLC to push an RLC PDU. This function is called from an independent MAC thread.
|
||||
* PDU gets placed into the buffer and higher layer thread gets notified. */
|
||||
virtual void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0;
|
||||
|
|
|
@ -56,21 +56,22 @@ struct rrc_meas_cfg_t {
|
|||
|
||||
// Cell/Sector configuration
|
||||
struct cell_cfg_t {
|
||||
uint32_t rf_port;
|
||||
uint32_t cell_id;
|
||||
uint16_t tac;
|
||||
uint32_t pci;
|
||||
uint16_t root_seq_idx;
|
||||
uint32_t dl_earfcn;
|
||||
double dl_freq_hz;
|
||||
uint32_t ul_earfcn;
|
||||
double ul_freq_hz;
|
||||
int target_pucch_sinr_db;
|
||||
int target_pusch_sinr_db;
|
||||
uint32_t initial_dl_cqi;
|
||||
bool enable_phr_handling;
|
||||
std::vector<scell_cfg_t> scell_list;
|
||||
rrc_meas_cfg_t meas_cfg;
|
||||
uint32_t rf_port;
|
||||
uint32_t cell_id;
|
||||
uint16_t tac;
|
||||
uint32_t pci;
|
||||
uint16_t root_seq_idx;
|
||||
uint32_t dl_earfcn;
|
||||
double dl_freq_hz;
|
||||
uint32_t ul_earfcn;
|
||||
double ul_freq_hz;
|
||||
int target_pucch_sinr_db;
|
||||
int target_pusch_sinr_db;
|
||||
uint32_t initial_dl_cqi;
|
||||
bool enable_phr_handling;
|
||||
asn1::rrc::mob_ctrl_info_s::t304_e_ t304;
|
||||
std::vector<scell_cfg_t> scell_list;
|
||||
rrc_meas_cfg_t meas_cfg;
|
||||
};
|
||||
|
||||
typedef std::vector<cell_cfg_t> cell_list_t;
|
||||
|
|
|
@ -99,7 +99,6 @@ public:
|
|||
class rrc_interface_rlc
|
||||
{
|
||||
public:
|
||||
virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0;
|
||||
virtual void max_retx_attempted(uint16_t rnti) = 0;
|
||||
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0;
|
||||
};
|
||||
|
@ -109,12 +108,13 @@ class rrc_interface_mac
|
|||
{
|
||||
public:
|
||||
/* Radio Link failure */
|
||||
virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0;
|
||||
virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0;
|
||||
virtual void set_activity_user(uint16_t rnti) = 0;
|
||||
virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0;
|
||||
virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0;
|
||||
virtual bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) = 0;
|
||||
virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0;
|
||||
virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0;
|
||||
virtual void set_activity_user(uint16_t rnti) = 0;
|
||||
virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0;
|
||||
virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0;
|
||||
virtual bool is_paging_opportunity(uint32_t tti_tx_dl, uint32_t* payload_len) = 0;
|
||||
virtual void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t payload_size) = 0;
|
||||
|
||||
///< Provide packed SIB to MAC (buffer is managed by RRC)
|
||||
virtual uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) = 0;
|
||||
|
|
|
@ -168,18 +168,6 @@ class rrc_interface_pdcp_nr
|
|||
public:
|
||||
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
|
||||
};
|
||||
class rrc_interface_ngap_nr
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
/*****************************
|
||||
* NGAP INTERFACES
|
||||
****************************/
|
||||
class ngap_interface_rrc_nr
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
class phy_interface_stack_nr
|
||||
{
|
||||
|
|
|
@ -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.dynamic_dual_harq_ack_codebook = false;
|
||||
dci_cfg.pusch_tx_config_non_codebook = false;
|
||||
dci_cfg.pusch_dmrs_type2 = false;
|
||||
dci_cfg.pusch_dmrs_double = false;
|
||||
dci_cfg.pusch_ptrs = false;
|
||||
dci_cfg.pusch_dynamic_betas = false;
|
||||
dci_cfg.pusch_alloc_type = pusch.alloc;
|
||||
dci_cfg.pusch_dmrs_type = pusch.dmrs_type;
|
||||
dci_cfg.pusch_dmrs_max_len = pusch.dmrs_max_length;
|
||||
|
||||
// Format 1_1 specific configuration (for PDSCH only)
|
||||
dci_cfg.nof_dl_bwp = 0;
|
||||
|
@ -121,12 +121,12 @@ struct phy_cfg_nr_t {
|
|||
dci_cfg.pdsch_rm_pattern2 = false;
|
||||
dci_cfg.pdsch_2cw = false;
|
||||
dci_cfg.multiple_scell = false;
|
||||
dci_cfg.pdsch_dmrs_type2 = false;
|
||||
dci_cfg.pdsch_dmrs_double = false;
|
||||
dci_cfg.pdsch_tci = false;
|
||||
dci_cfg.pdsch_cbg_flush = false;
|
||||
dci_cfg.pdsch_dynamic_bundling = false;
|
||||
dci_cfg.pdsch_alloc_type = pdsch.alloc;
|
||||
dci_cfg.pdsch_dmrs_type = pdsch.dmrs_type;
|
||||
dci_cfg.pdsch_dmrs_max_len = pdsch.dmrs_max_length;
|
||||
|
||||
return dci_cfg;
|
||||
};
|
||||
|
|
|
@ -49,18 +49,24 @@
|
|||
#define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_OTHER 6
|
||||
|
||||
/**
|
||||
* @brief Measurement structure
|
||||
* @brief Describes a measurement for NZP-CSI-RS
|
||||
* @note Used for fine tracking RSRP, SNR, CFO, SFO, and so on
|
||||
* @note srsran_csi_channel_measurements_t is used for CSI report generation
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
float rsrp;
|
||||
float rsrp_dB;
|
||||
float epre;
|
||||
float epre_dB;
|
||||
float n0;
|
||||
float n0_dB;
|
||||
float snr_dB;
|
||||
uint32_t nof_re;
|
||||
} srsran_csi_rs_measure_t;
|
||||
float rsrp; ///< Linear scale RSRP
|
||||
float rsrp_dB; ///< Logarithm scale RSRP relative to full-scale
|
||||
float epre; ///< Linear scale EPRE
|
||||
float epre_dB; ///< Logarithm scale EPRE relative to full-scale
|
||||
float n0; ///< Linear noise level
|
||||
float n0_dB; ///< Logarithm scale noise level relative to full-scale
|
||||
float snr_dB; ///< Signal to noise ratio in decibels
|
||||
float cfo_hz; ///< Carrier frequency offset in Hz. Only set if more than 2 symbols are available in a TRS set
|
||||
float cfo_hz_max; ///< Maximum CFO in Hz that can be measured. It is set to 0 if CFO cannot be estimated
|
||||
float delay_us; ///< Average measured delay in microseconds
|
||||
uint32_t nof_re; ///< Number of available RE for the measurement, it can be used for weighting among different
|
||||
///< measurements
|
||||
} srsran_csi_trs_measurements_t;
|
||||
|
||||
/**
|
||||
* @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
|
||||
* reserved RE pattern for PDSCH transmission.
|
||||
* @param carrier Provides carrier configuration
|
||||
* @param resource Provides a CSI-RS resource
|
||||
* @param resource Provides any CSI-RS resource mapping
|
||||
* @param nof_resources Provides the number of ZP-CSI-RS resources
|
||||
* @param l Symbol index in the slot
|
||||
* @param[out] rvd_mask Provides the reserved mask
|
||||
|
@ -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,
|
||||
srsran_re_pattern_list_t* re_pattern_list);
|
||||
|
||||
SRSRAN_API int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_resource_t* resource,
|
||||
cf_t* grid);
|
||||
/**
|
||||
* @brief Puts in the provided resource grid NZP-CSI-RS signals given by a NZP-CSI-RS resource
|
||||
*
|
||||
* @note it does not check if the provided slot matches with the periodicity of the provided NZP-CSI-RS resource
|
||||
*
|
||||
* @param carrier Provides carrier configuration
|
||||
* @param slot_cfg Provides current slot configuration
|
||||
* @param resource Provides a NZP-CSI-RS resource
|
||||
* @param[out] grid Resource grid
|
||||
* @return SRSLTE_SUCCESS if the arguments and the resource are valid. SRSLTE_ERROR code otherwise.
|
||||
*/
|
||||
SRSRAN_API int srsran_csi_rs_nzp_put_resource(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_resource_t* resource,
|
||||
cf_t* grid);
|
||||
/**
|
||||
* @brief Puts in the provided resource grid NZP-CSI-RS signals given by a NZP-CSI-RS resource set if their periodicity
|
||||
* configuration matches with the provided slot
|
||||
*
|
||||
* @param carrier Provides carrier configuration
|
||||
* @param slot_cfg Provides current slot configuration
|
||||
* @param set Provides a NZP-CSI-RS resource set
|
||||
* @param[out] grid Resource grid
|
||||
* @return The number of NZP-CSI-RS resources that have been scheduled for this slot if the arguments and the resource
|
||||
* are valid. SRSLTE_ERROR code otherwise.
|
||||
*/
|
||||
SRSRAN_API int srsran_csi_rs_nzp_put_set(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_set_t* set,
|
||||
cf_t* grid);
|
||||
|
||||
SRSRAN_API int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_resource_t* resource,
|
||||
const cf_t* grid,
|
||||
srsran_csi_rs_measure_t* measure);
|
||||
srsran_csi_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_
|
||||
|
|
|
@ -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_apply_f(srsran_sequence_state_t* s, const float* in, float* out, uint32_t length);
|
||||
|
||||
SRSRAN_API void srsran_sequence_state_advance(srsran_sequence_state_t* s, uint32_t length);
|
||||
|
||||
typedef struct SRSRAN_API {
|
||||
|
|
|
@ -25,18 +25,34 @@
|
|||
#include "uci_cfg_nr.h"
|
||||
|
||||
/**
|
||||
* @brief Fills Uplink Control Information data with triggered reports for the given slot
|
||||
* @param cfg CSI report configuration
|
||||
* @brief Processes a new NZP-CSI-RS channel measurement, it maps the given measurement into the current measurements
|
||||
* applying an exponential moving average filter
|
||||
* @param csi_resources CSI Resource configuration, links NZP-CSI-RS resources with CSI Measurements
|
||||
* @param measurements Current CSI measurements
|
||||
* @param new_measure New NZP-CSI-RS channel measurement
|
||||
* @param nzp_csi_rs_id NZP-CSI-RS resource set identifier
|
||||
* @return SRSLTE_SUCCESS if the provided information is valid, SRSLTE_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int
|
||||
srsran_csi_new_nzp_csi_rs_measurement(const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES],
|
||||
srsran_csi_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 measurements CSI measurements
|
||||
* @param[out] uci_data Uplink Control Information data
|
||||
* @param measurements Filtered CSI measurements
|
||||
* @param[out] report_cfg Report configuration re
|
||||
* @return The number CSI reports for transmission if the provided data is valid, SRSRAN_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg,
|
||||
uint32_t slot_idx,
|
||||
const srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES],
|
||||
srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT],
|
||||
srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]);
|
||||
SRSRAN_API int
|
||||
srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg,
|
||||
uint32_t slot_idx,
|
||||
const srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES],
|
||||
srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT],
|
||||
srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]);
|
||||
|
||||
/**
|
||||
* @brief Compute number of CSI bits necessary to transmit all the CSI reports for a PUCCH transmission
|
||||
|
|
|
@ -36,6 +36,22 @@
|
|||
*/
|
||||
#define SRSRAN_CSI_MAX_NOF_RESOURCES 112
|
||||
|
||||
/**
|
||||
* @brief Maximum number of NZP-CSI-RS resources sets per config, defined in TS 38.331
|
||||
* maxNrofNZP-CSI-RS-ResourceSetsPerConfig
|
||||
*/
|
||||
#define SRSRAN_CSI_MAX_NOF_NZP_CSI_RS_RESOURCE_SETS_X_CONFIG 16
|
||||
|
||||
/**
|
||||
* @brief Maximum number of CSI-SSB resources sets per config, defined in TS 38.331 maxNrofCSI-SSB-ResourceSetsPerConfig
|
||||
*/
|
||||
#define SRSRAN_CSI_MAX_NOF_CSI_SSB_RESOURCE_SETS_X_CONFIG 1
|
||||
|
||||
/**
|
||||
* @brief Maximum number of CSI-SSB resources sets per config, defined in TS 38.331 maxNrofCSI-IM-ResourceSetsPerConfig
|
||||
*/
|
||||
#define SRSRAN_CSI_MAX_NOF_CSI_IM_RESOURCE_SETS_X_CONFIG 12
|
||||
|
||||
/**
|
||||
* @brief CSI report types defined in TS 38.331 CSI-ReportConfig
|
||||
*/
|
||||
|
@ -106,16 +122,41 @@ typedef struct SRSRAN_API {
|
|||
srsran_csi_report_freq_t freq_cfg; ///< Determine whether it is wideband or subband
|
||||
} srsran_csi_hl_report_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief CSI Resource configuration
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
enum {
|
||||
SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_NONE = 0,
|
||||
SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_NZP_CSI_RS_SSB,
|
||||
SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_IM
|
||||
} type;
|
||||
union {
|
||||
struct {
|
||||
uint32_t nzp_csi_rs_resource_set_id_list[SRSRAN_CSI_MAX_NOF_NZP_CSI_RS_RESOURCE_SETS_X_CONFIG];
|
||||
uint32_t nzp_csi_rs_resource_set_id_list_count;
|
||||
uint32_t csi_ssb_rs_resource_set_id_list[SRSRAN_CSI_MAX_NOF_CSI_SSB_RESOURCE_SETS_X_CONFIG];
|
||||
uint32_t csi_ssb_rs_resource_set_id_list_count;
|
||||
} nzp_csi_rs_ssb;
|
||||
struct {
|
||||
uint32_t resource_set_id_list[SRSRAN_CSI_MAX_NOF_CSI_IM_RESOURCE_SETS_X_CONFIG];
|
||||
uint32_t resource_set_id_list_count;
|
||||
} csi_im;
|
||||
};
|
||||
} srsran_csi_hl_resource_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief General CSI configuration provided by higher layers
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
srsran_csi_hl_report_cfg_t reports[SRSRAN_CSI_MAX_NOF_REPORT]; ///< CSI report configuration
|
||||
// ... add here physical CSI measurement sets
|
||||
srsran_csi_hl_report_cfg_t reports[SRSRAN_CSI_MAX_NOF_REPORT]; ///< CSI report configuration indexed by
|
||||
///< reportConfigId
|
||||
srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES]; ///< Configured CSI resource settings,
|
||||
///< indexed by csi-ResourceConfigId
|
||||
} srsran_csi_hl_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Generic measurement structure
|
||||
* @brief Generic CSI measurement structure, used for generating CSI reports
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
uint32_t cri; ///< CSI-RS Resource Indicator
|
||||
|
@ -126,7 +167,7 @@ typedef struct SRSRAN_API {
|
|||
// Resource set context
|
||||
uint32_t nof_ports; ///< Number of antenna ports
|
||||
uint32_t K_csi_rs; ///< Number of CSI-RS in the corresponding resource set
|
||||
} srsran_csi_measurements_t;
|
||||
} srsran_csi_channel_measurements_t;
|
||||
|
||||
/**
|
||||
* @brief CSI report configuration
|
||||
|
|
|
@ -66,11 +66,11 @@ typedef struct SRSRAN_API {
|
|||
bool enable_transform_precoding; ///< Set to true if PUSCH transform precoding is enabled
|
||||
bool dynamic_dual_harq_ack_codebook; ///< Set to true if HARQ-ACK codebook is set to dynamic with 2 sub-codebooks
|
||||
bool pusch_tx_config_non_codebook; ///< Set to true if PUSCH txConfig is set to non-codebook
|
||||
bool pusch_dmrs_type2; ///< Set to true if PUSCH DMRS are type 2
|
||||
bool pusch_dmrs_double; ///< Set to true if PUSCH DMRS are 2 symbol long
|
||||
bool pusch_ptrs; ///< Set to true if PT-RS are enabled for PUSCH transmission
|
||||
bool pusch_dynamic_betas; ///< Set to true if beta offsets operation is not semi-static
|
||||
srsran_resource_alloc_t pusch_alloc_type; ///< PUSCH resource allocation type
|
||||
srsran_dmrs_sch_type_t pusch_dmrs_type; ///< PUSCH DMRS type
|
||||
srsran_dmrs_sch_len_t pusch_dmrs_max_len; ///< PUSCH DMRS maximum length
|
||||
|
||||
/// Format 1_1 specific configuration (for PDSCH only)
|
||||
uint32_t nof_dl_bwp; ///< Number of DL BWPs excluding the initial UL BWP, mentioned in the TS as N_BWP_RRC
|
||||
|
@ -83,13 +83,12 @@ typedef struct SRSRAN_API {
|
|||
bool pdsch_rm_pattern2; ///< Set to true if rateMatchPatternGroup2 is configured
|
||||
bool pdsch_2cw; ///< Set to true if maxNrofCodeWordsScheduledByDCI is set to 2 in any BWP
|
||||
bool multiple_scell; ///< Set to true if configured with multiple serving cell
|
||||
bool pdsch_dmrs_type2; ///< Set to true if PDSCH DMRS are type 2
|
||||
bool pdsch_dmrs_double; ///< Set to true if PDSCH DMRS are 2 symbol long
|
||||
bool pdsch_tci; ///< Set to true if tci-PresentInDCI is enabled
|
||||
bool pdsch_cbg_flush; ///< Set to true if codeBlockGroupFlushIndicator is true
|
||||
bool pdsch_dynamic_bundling; ///< Set to true if prb-BundlingType is set to dynamicBundling
|
||||
srsran_resource_alloc_t pdsch_alloc_type; ///< PDSCH resource allocation type, set to 0 for default
|
||||
|
||||
srsran_resource_alloc_t pdsch_alloc_type; ///< PDSCH resource allocation type, set to 0 for default
|
||||
srsran_dmrs_sch_type_t pdsch_dmrs_type; ///< PDSCH DMRS type
|
||||
srsran_dmrs_sch_len_t pdsch_dmrs_max_len; ///< PDSCH DMRS maximum length
|
||||
} srsran_dci_cfg_nr_t;
|
||||
|
||||
/**
|
||||
|
|
|
@ -193,10 +193,11 @@ typedef struct SRSRAN_API {
|
|||
bool scrambling_id_present;
|
||||
uint32_t scambling_id; // Identifier used to initialize data scrambling (0-1023)
|
||||
|
||||
srsran_mcs_table_t mcs_table;
|
||||
srsran_dmrs_sch_type_t dmrs_type;
|
||||
srsran_dmrs_sch_len_t dmrs_max_length;
|
||||
struct {
|
||||
srsran_dmrs_sch_type_t type;
|
||||
srsran_dmrs_sch_add_pos_t additional_pos;
|
||||
srsran_dmrs_sch_len_t length;
|
||||
bool scrambling_id0_present;
|
||||
uint32_t scrambling_id0;
|
||||
bool scrambling_id1_present;
|
||||
|
@ -205,9 +206,7 @@ typedef struct SRSRAN_API {
|
|||
} dmrs_typeA;
|
||||
|
||||
struct {
|
||||
srsran_dmrs_sch_type_t type;
|
||||
srsran_dmrs_sch_add_pos_t additional_pos;
|
||||
srsran_dmrs_sch_len_t length;
|
||||
bool scrambling_id0_present;
|
||||
uint32_t scrambling_id0;
|
||||
bool scrambling_id1_present;
|
||||
|
@ -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];
|
||||
|
||||
/// PUSCH only
|
||||
srsran_beta_offsets_t beta_offsets; /// Semi-static only.
|
||||
srsran_beta_offsets_t beta_offsets; /// Semi-static only.
|
||||
bool enable_transform_precoder; /// Enables transform precoding
|
||||
float scaling; /// Indicates a scaling factor to limit the number of resource elements assigned to UCI on PUSCH.
|
||||
} srsran_sch_hl_cfg_nr_t;
|
||||
|
||||
|
|
|
@ -71,17 +71,32 @@ SRSRAN_API int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg,
|
|||
*/
|
||||
SRSRAN_API int
|
||||
srsran_ra_dl_nr_time_default_A(uint32_t m, srsran_dmrs_sch_typeA_pos_t dmrs_typeA_pos, srsran_sch_grant_nr_t* grant);
|
||||
|
||||
/**
|
||||
* @brief Calculates the number of PDSCH-DMRS CDM groups without data for DCI format 1_0
|
||||
* @brief Calculates the number of front load symbols
|
||||
*
|
||||
* @remark Defined by TS 38.214 V15.10.0 5.1.6.1.3 CSI-RS for mobility
|
||||
*
|
||||
* @param cfg PDSCH-DMRS NR configuration by upper layers
|
||||
* @param[out] grant Provides grant pointer to fill
|
||||
* @return Returns SRSRAN_SUCCESS if the provided data is valid, otherwise it returns SRSRAN_ERROR code
|
||||
* @param cfg PDSCH NR configuration by upper layers
|
||||
* @param dci Provides 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_dmrs_cdm_groups_without_data_format_1_0(const srsran_dmrs_sch_cfg_t* cfg,
|
||||
srsran_sch_grant_nr_t* grant);
|
||||
SRSRAN_API int srsran_ra_dl_nr_nof_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 DMRS CDM groups without data
|
||||
*
|
||||
* @remark Defined by TS 38.214 V15.10.0 5.1.6.2 DM-RS reception procedure
|
||||
*
|
||||
* @param cfg PDSCH NR configuration by upper layers
|
||||
* @param dci Provides PUSCH NR DCI
|
||||
* @return The number of DMRS CDM groups without data if the provided data is valid, otherwise it returns SRSRAN_ERROR
|
||||
* code
|
||||
*/
|
||||
SRSRAN_API int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg,
|
||||
const srsran_dci_dl_nr_t* dci,
|
||||
uint32_t L);
|
||||
|
||||
/**
|
||||
* @brief Calculates the PDSCH frequency resource allocation and stores it in the provided PDSCH NR grant.
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
* @brief Calculates the number of PUSCH-DMRS CDM groups without data for DCI format 0_0
|
||||
* @brief Calculates the number of front load symbols
|
||||
*
|
||||
* @param cfg PUSCH NR configuration by upper layers
|
||||
* @param dci Provides PUSCH NR DCI
|
||||
* @param[out] dmrs_duration
|
||||
* @return SRSRAN_SUCCESS if provided arguments are valid, SRSRAN_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_ra_ul_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg,
|
||||
const srsran_dci_ul_nr_t* dci,
|
||||
srsran_dmrs_sch_len_t* dmrs_duration);
|
||||
|
||||
/**
|
||||
* @brief Calculates the number of DMRS CDM groups without data
|
||||
*
|
||||
* @remark Defined by TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure
|
||||
*
|
||||
* @param cfg PUSCH NR configuration by upper layers
|
||||
* @param[out] grant Provides grant pointer to fill
|
||||
* @return Returns SRSRAN_SUCCESS if the provided data is valid, otherwise it returns SRSRAN_ERROR code
|
||||
* @param dci Provides PUSCH NR DCI
|
||||
* @return The number of DMRS CDM groups without data if the provided data is valid, otherwise it returns SRSRAN_ERROR
|
||||
* code
|
||||
*/
|
||||
SRSRAN_API int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(const srsran_sch_cfg_nr_t* cfg,
|
||||
srsran_sch_grant_nr_t* grant);
|
||||
SRSRAN_API int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg,
|
||||
const srsran_dci_ul_nr_t* dci,
|
||||
uint32_t L);
|
||||
|
||||
/**
|
||||
* @brief Calculates the ratio of PUSCH EPRE to DM-RS EPRE
|
||||
|
@ -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.
|
||||
*
|
||||
* @remark Defined by TS 38.214 V15.10.0 section 5.1.2.2
|
||||
* @remark Defined by TS 38.214 V15.10.0 section 6.1.2.2 Resource allocation in frequency domain (for DCI formats 0_0
|
||||
* and 0_1)
|
||||
* @remark Defined by TS 38.213 V15.10.0 section 8.3 for PUSCH scheduled by RAR UL grant
|
||||
* @param carrier Carrier information
|
||||
* @param cfg PDSCH NR configuration by upper layers
|
||||
* @param dci_dl Unpacked DCI used to schedule the PDSCH grant
|
||||
|
|
|
@ -22,9 +22,11 @@
|
|||
#ifndef SRSRAN_UE_DL_NR_H
|
||||
#define SRSRAN_UE_DL_NR_H
|
||||
|
||||
#include "srsran/phy/ch_estimation/csi_rs.h"
|
||||
#include "srsran/phy/ch_estimation/dmrs_pdcch.h"
|
||||
#include "srsran/phy/common/phy_common_nr.h"
|
||||
#include "srsran/phy/dft/ofdm.h"
|
||||
#include "srsran/phy/phch/csi.h"
|
||||
#include "srsran/phy/phch/dci_nr.h"
|
||||
#include "srsran/phy/phch/pdcch_cfg_nr.h"
|
||||
#include "srsran/phy/phch/pdcch_nr.h"
|
||||
|
@ -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
|
||||
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
|
||||
|
|
|
@ -53,6 +53,9 @@ extern "C" {
|
|||
#define SRSRAN_FLOOR(NUM, DEN) ((NUM) / (DEN))
|
||||
#define SRSRAN_ROUND(NUM, DEN) ((uint32_t)round((double)(NUM) / (double)(DEN)))
|
||||
|
||||
// Complex squared absolute value
|
||||
#define SRSRAN_CSQABS(X) (__real__(X) * __real__(X) + __imag__(X) * __imag__(X))
|
||||
|
||||
// Cumulative moving average
|
||||
#define SRSRAN_VEC_CMA(data, average, n) ((average) + ((data) - (average)) / ((n) + 1))
|
||||
|
||||
|
@ -62,6 +65,9 @@ extern "C" {
|
|||
// Exponential moving average
|
||||
#define SRSRAN_VEC_EMA(data, average, alpha) ((alpha) * (data) + (1 - alpha) * (average))
|
||||
|
||||
// Safe exponential moving average
|
||||
#define SRSRAN_VEC_SAFE_EMA(data, average, alpha) (isnormal(average) ? SRSRAN_VEC_EMA(data, average, alpha) : (data))
|
||||
|
||||
static inline float srsran_convert_amplitude_to_dB(float v)
|
||||
{
|
||||
return 20.0f * log10f(v);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#define SRSLOG_DETAIL_LOG_BACKEND_H
|
||||
|
||||
#include "srsran/srslog/bundled/fmt/printf.h"
|
||||
#include "srsran/srslog/shared_types.h"
|
||||
|
||||
namespace srslog {
|
||||
|
||||
|
@ -40,7 +41,7 @@ public:
|
|||
|
||||
/// Starts the processing of incoming log entries.
|
||||
/// NOTE: Calling this function more than once has no side effects.
|
||||
virtual void start() = 0;
|
||||
virtual void start(backend_priority priority = backend_priority::normal) = 0;
|
||||
|
||||
/// Allocates a dyn_arg_store and returns a pointer to it on success, otherwise returns nullptr.
|
||||
virtual fmt::dynamic_format_arg_store<fmt::printf_context>* alloc_arg_store() = 0;
|
||||
|
|
|
@ -37,7 +37,7 @@ template <typename T, size_t capacity = SRSLOG_QUEUE_CAPACITY>
|
|||
class work_queue
|
||||
{
|
||||
srsran::dyn_circular_buffer<T> queue;
|
||||
mutable condition_variable cond_var;
|
||||
mutable mutex m;
|
||||
static constexpr size_t threshold = capacity * 0.98;
|
||||
|
||||
public:
|
||||
|
@ -50,15 +50,14 @@ public:
|
|||
/// queue is full, otherwise true.
|
||||
bool push(const T& value)
|
||||
{
|
||||
cond_var.lock();
|
||||
m.lock();
|
||||
// Discard the new element if we reach the maximum capacity.
|
||||
if (queue.full()) {
|
||||
cond_var.unlock();
|
||||
m.unlock();
|
||||
return false;
|
||||
}
|
||||
queue.push(value);
|
||||
cond_var.unlock();
|
||||
cond_var.signal();
|
||||
m.unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -67,56 +66,26 @@ public:
|
|||
/// queue is full, otherwise true.
|
||||
bool push(T&& value)
|
||||
{
|
||||
cond_var.lock();
|
||||
m.lock();
|
||||
// Discard the new element if we reach the maximum capacity.
|
||||
if (queue.full()) {
|
||||
cond_var.unlock();
|
||||
m.unlock();
|
||||
return false;
|
||||
}
|
||||
queue.push(std::move(value));
|
||||
cond_var.unlock();
|
||||
cond_var.signal();
|
||||
m.unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Extracts the top most element from the queue.
|
||||
/// NOTE: This method blocks while the queue is empty.
|
||||
T pop()
|
||||
/// Extracts the top most element from the queue if it exists.
|
||||
/// Returns a pair with a bool indicating if the pop has been successful.
|
||||
std::pair<bool, T> try_pop()
|
||||
{
|
||||
cond_var.lock();
|
||||
m.lock();
|
||||
|
||||
while (queue.empty()) {
|
||||
cond_var.wait();
|
||||
}
|
||||
|
||||
T elem = std::move(queue.top());
|
||||
queue.pop();
|
||||
|
||||
cond_var.unlock();
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
/// Extracts the top most element from the queue.
|
||||
/// NOTE: This method blocks while the queue is empty or or until the
|
||||
/// programmed timeout expires. Returns a pair with a bool indicating if the
|
||||
/// pop has been successful.
|
||||
std::pair<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();
|
||||
if (queue.empty()) {
|
||||
m.unlock();
|
||||
return {false, T()};
|
||||
}
|
||||
|
||||
|
@ -124,7 +93,7 @@ public:
|
|||
T Item = std::move(queue.top());
|
||||
queue.pop();
|
||||
|
||||
cond_var.unlock();
|
||||
m.unlock();
|
||||
|
||||
return {true, std::move(Item)};
|
||||
}
|
||||
|
@ -135,7 +104,7 @@ public:
|
|||
/// Returns true when the queue is almost full, otherwise returns false.
|
||||
bool is_almost_full() const
|
||||
{
|
||||
cond_var_scoped_lock lock(cond_var);
|
||||
scoped_lock lock(m);
|
||||
|
||||
return queue.size() > threshold;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,17 @@ namespace srslog {
|
|||
/// Generic error handler callback.
|
||||
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
|
||||
|
||||
#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
|
||||
/// any log entry is generated.
|
||||
/// NOTE: Calling this function more than once has no side effects.
|
||||
void init();
|
||||
void init(backend_priority priority = backend_priority::normal);
|
||||
|
||||
/// Flushes the contents of all the registered sinks. The caller thread will
|
||||
/// block until the operation is completed.
|
||||
|
|
|
@ -48,6 +48,7 @@ extern "C" {
|
|||
|
||||
#include "srsran/phy/ch_estimation/chest_dl.h"
|
||||
#include "srsran/phy/ch_estimation/chest_ul.h"
|
||||
#include "srsran/phy/ch_estimation/csi_rs.h"
|
||||
#include "srsran/phy/ch_estimation/dmrs_pdcch.h"
|
||||
#include "srsran/phy/ch_estimation/dmrs_sch.h"
|
||||
#include "srsran/phy/ch_estimation/refsignal_dl.h"
|
||||
|
|
|
@ -65,7 +65,7 @@ static int csi_rs_location_f(const srsran_csi_rs_resource_mapping_t* resource, u
|
|||
}
|
||||
|
||||
if (count == i) {
|
||||
return j * mul;
|
||||
return (int)(j * mul);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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_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)
|
||||
|
@ -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:
|
||||
return nprb * 3;
|
||||
case srsran_csi_rs_resource_mapping_density_dot5_even:
|
||||
return nprb / 2;
|
||||
case srsran_csi_rs_resource_mapping_density_dot5_odd:
|
||||
return nprb / 2;
|
||||
case srsran_csi_rs_resource_mapping_density_one:
|
||||
|
@ -348,12 +347,13 @@ int srsran_csi_rs_append_resource_to_pattern(const srsran_carrier_nr_t*
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_resource_t* resource,
|
||||
cf_t* grid)
|
||||
int srsran_csi_rs_nzp_put_resource(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_resource_t* resource,
|
||||
cf_t* grid)
|
||||
{
|
||||
if (carrier == NULL || resource == NULL || grid == NULL) {
|
||||
// Verify inputs
|
||||
if (carrier == NULL || slot_cfg == NULL || resource == NULL || grid == NULL) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
|
@ -421,25 +421,76 @@ int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier,
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_resource_t* resource,
|
||||
const cf_t* grid,
|
||||
srsran_csi_rs_measure_t* measure)
|
||||
int srsran_csi_rs_nzp_put_set(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_set_t* set,
|
||||
cf_t* grid)
|
||||
{
|
||||
if (carrier == NULL || resource == NULL || grid == NULL) {
|
||||
// Verify inputs
|
||||
if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
uint32_t count = 0;
|
||||
|
||||
// Iterate all resources in set
|
||||
for (uint32_t i = 0; i < set->count; i++) {
|
||||
// Skip resource
|
||||
if (!srsran_csi_rs_send(&set->data[i].periodicity, slot_cfg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Put resource
|
||||
if (srsran_csi_rs_nzp_put_resource(carrier, slot_cfg, &set->data[i], grid) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error putting NZP-CSI-RS resource");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return (int)count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Internal NZP-CSI-RS measurement structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t cri; ///< CSI-RS resource identifier
|
||||
uint32_t l0; ///< First OFDM symbol carrying CSI-RS
|
||||
float epre; ///< Linear EPRE
|
||||
cf_t corr; ///< Correlation
|
||||
float delay_us; ///< Estimated average delay
|
||||
uint32_t nof_re; ///< Total number of resource elements
|
||||
} csi_rs_nzp_resource_measure_t;
|
||||
|
||||
static int csi_rs_nzp_measure_resource(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_resource_t* resource,
|
||||
const cf_t* grid,
|
||||
csi_rs_nzp_resource_measure_t* measure)
|
||||
{
|
||||
// Force CDM group to 0
|
||||
uint32_t j = 0;
|
||||
|
||||
// Get subcarrier indexes
|
||||
uint32_t k_list[CSI_RS_MAX_SUBC_PRB];
|
||||
int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, j, k_list);
|
||||
if (nof_k <= 0) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Calculate average CSI-RS RE stride
|
||||
float avg_k_stride = (float)((k_list[0] + SRSRAN_NRE) - k_list[nof_k - 1]);
|
||||
for (uint32_t i = 1; i < (uint32_t)nof_k; i++) {
|
||||
avg_k_stride += (float)(k_list[i] - k_list[i - 1]);
|
||||
}
|
||||
avg_k_stride /= (float)nof_k;
|
||||
if (!isnormal(avg_k_stride)) {
|
||||
ERROR("Invalid avg_k_stride");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Get symbol indexes
|
||||
uint32_t l_list[CSI_RS_MAX_SYMBOLS_SLOT];
|
||||
int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, j, l_list);
|
||||
if (nof_l <= 0) {
|
||||
|
@ -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_stride = csi_rs_rb_stride(&resource->resource_mapping);
|
||||
|
||||
// Accumulators
|
||||
float epre_acc = 0.0f;
|
||||
cf_t rsrp_acc = 0.0f;
|
||||
uint32_t count = 0;
|
||||
// Calculate ideal number of RE per symbol
|
||||
uint32_t nof_re = csi_rs_count(resource->resource_mapping.density, rb_end - rb_begin);
|
||||
|
||||
// Accumulators
|
||||
float epre_acc = 0.0f;
|
||||
cf_t corr_acc = 0.0f;
|
||||
float delay_acc = 0.0f;
|
||||
|
||||
// Initialise measurement
|
||||
SRSRAN_MEM_ZERO(measure, csi_rs_nzp_resource_measure_t, 1);
|
||||
|
||||
// Iterate time symbols
|
||||
for (int l_idx = 0; l_idx < nof_l; l_idx++) {
|
||||
// Get symbol index
|
||||
uint32_t l = l_list[l_idx];
|
||||
|
@ -468,61 +526,461 @@ int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier,
|
|||
// Skip unallocated RB
|
||||
srsran_sequence_state_advance(&sequence_state, 2 * csi_rs_count(resource->resource_mapping.density, rb_begin));
|
||||
|
||||
// Temporal R sequence
|
||||
cf_t r[64];
|
||||
uint32_t r_idx = 64;
|
||||
// Temporal Least Square Estimates
|
||||
cf_t lse[CSI_RS_MAX_SUBC_PRB * SRSRAN_MAX_PRB_NR];
|
||||
uint32_t count_re = 0;
|
||||
|
||||
// Iterate over frequency domain
|
||||
// Extract RE
|
||||
for (uint32_t n = rb_begin; n < rb_end; n += rb_stride) {
|
||||
for (uint32_t k_idx = 0; k_idx < nof_k; k_idx++) {
|
||||
// Calculate sub-carrier index k
|
||||
uint32_t k = SRSRAN_NRE * n + k_list[k_idx];
|
||||
|
||||
// Do we need more r?
|
||||
if (r_idx >= 64) {
|
||||
// ... Generate a bunch of it!
|
||||
srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)r, 64 * 2);
|
||||
r_idx = 0;
|
||||
}
|
||||
|
||||
// Take CSI-RS from grid and measure
|
||||
cf_t tmp = grid[l * SRSRAN_NRE * carrier->nof_prb + k] * conjf(r[r_idx++]);
|
||||
rsrp_acc += tmp;
|
||||
epre_acc += __real__ tmp * __real__ tmp + __imag__ tmp * __imag__ tmp;
|
||||
count++;
|
||||
lse[count_re++] = grid[l * SRSRAN_NRE * carrier->nof_prb + k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count) {
|
||||
measure->epre = epre_acc / (float)count;
|
||||
rsrp_acc /= (float)count;
|
||||
measure->rsrp = (__real__ rsrp_acc * __real__ rsrp_acc + __imag__ rsrp_acc * __imag__ rsrp_acc);
|
||||
if (measure->epre > measure->rsrp) {
|
||||
measure->n0 = measure->epre - measure->rsrp;
|
||||
} else {
|
||||
measure->n0 = 0.0f;
|
||||
// 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;
|
||||
}
|
||||
|
||||
measure->rsrp_dB = srsran_convert_power_to_dB(measure->rsrp);
|
||||
measure->epre_dB = srsran_convert_power_to_dB(measure->epre);
|
||||
measure->n0_dB = srsran_convert_power_to_dB(measure->n0);
|
||||
measure->snr_dB = measure->rsrp_dB - measure->n0_dB;
|
||||
measure->nof_re = count;
|
||||
// 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;
|
||||
}
|
||||
|
||||
uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_measure_t* measure, char* str, uint32_t str_len)
|
||||
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])
|
||||
{
|
||||
return srsran_print_check(str,
|
||||
str_len,
|
||||
0,
|
||||
"rsrp=%+.1f, epre=%+.1f, n0=%+.1f, snr=%+.1f, nof_re=%d",
|
||||
measure->rsrp_dB,
|
||||
measure->epre_dB,
|
||||
measure->n0_dB,
|
||||
measure->snr_dB,
|
||||
measure->nof_re);
|
||||
}
|
||||
uint32_t count = 0;
|
||||
|
||||
// Iterate all resources in set
|
||||
for (uint32_t i = 0; i < set->count; i++) {
|
||||
// Skip resource
|
||||
if (!srsran_csi_rs_send(&set->data[i].periodicity, slot_cfg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Perform measurement
|
||||
if (csi_rs_nzp_measure_resource(carrier, slot_cfg, &set->data[i], grid, &measurements[count]) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error measuring NZP-CSI-RS resource");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_resource_t* resource,
|
||||
const cf_t* grid,
|
||||
srsran_csi_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->epre_dB = srsran_convert_power_to_dB(measure->epre);
|
||||
measure->n0_dB = srsran_convert_power_to_dB(measure->n0);
|
||||
measure->snr_dB = measure->rsrp_dB - measure->n0_dB;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_set_t* set,
|
||||
const cf_t* grid,
|
||||
srsran_csi_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;
|
||||
}
|
||||
|
||||
static int csi_rs_zp_measure_set(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_zp_set_t* set,
|
||||
const cf_t* grid,
|
||||
csi_rs_zp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET])
|
||||
{
|
||||
uint32_t count = 0;
|
||||
|
||||
// Iterate all resources in set
|
||||
for (uint32_t i = 0; i < set->count; i++) {
|
||||
// Skip resource
|
||||
if (!srsran_csi_rs_send(&set->data[i].periodicity, slot_cfg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Perform measurement
|
||||
if (csi_rs_zp_measure_resource(carrier, slot_cfg, &set->data[i], grid, &measurements[count]) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error measuring NZP-CSI-RS resource");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_zp_set_t* set,
|
||||
const cf_t* grid,
|
||||
srsran_csi_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 first_symbol = UINT32_MAX;
|
||||
|
||||
static int test(const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_resource_t* resource,
|
||||
srsran_channel_awgn_t* awgn,
|
||||
cf_t* grid)
|
||||
static int nzp_test_case(const srsran_slot_cfg_t* slot_cfg,
|
||||
const srsran_csi_rs_nzp_resource_t* resource,
|
||||
srsran_channel_awgn_t* awgn,
|
||||
cf_t* grid)
|
||||
{
|
||||
srsran_csi_rs_measure_t measure = {};
|
||||
srsran_csi_trs_measurements_t measure = {};
|
||||
|
||||
// Put NZP-CSI-RS
|
||||
TESTASSERT(srsran_csi_rs_nzp_put(&carrier, slot_cfg, resource, grid) == SRSRAN_SUCCESS);
|
||||
TESTASSERT(srsran_csi_rs_nzp_put_resource(&carrier, slot_cfg, resource, grid) == SRSRAN_SUCCESS);
|
||||
|
||||
// Configure N0 and add Noise
|
||||
TESTASSERT(srsran_channel_awgn_set_n0(awgn, (float)resource->power_control_offset - snr_dB) == SRSRAN_SUCCESS);
|
||||
|
@ -78,6 +78,267 @@ static int test(const srsran_slot_cfg_t* slot_cfg,
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int nzp_test_brute(srsran_channel_awgn_t* awgn, cf_t* grid)
|
||||
{
|
||||
// Slot configuration
|
||||
srsran_slot_cfg_t slot_cfg = {};
|
||||
|
||||
// Initialise NZP-CSI-RS fix parameters, other params are not implemented
|
||||
srsran_csi_rs_nzp_resource_t resource = {};
|
||||
resource.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm;
|
||||
resource.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three;
|
||||
resource.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1;
|
||||
resource.resource_mapping.nof_ports = 1;
|
||||
|
||||
// Row 1 supported only!
|
||||
uint32_t nof_freq_dom_alloc = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1;
|
||||
|
||||
uint32_t first_symbol_begin = (first_symbol != UINT32_MAX) ? first_symbol : 0;
|
||||
uint32_t first_symbol_end = (first_symbol != UINT32_MAX) ? first_symbol : 13;
|
||||
for (resource.resource_mapping.first_symbol_idx = first_symbol_begin;
|
||||
resource.resource_mapping.first_symbol_idx <= first_symbol_end;
|
||||
resource.resource_mapping.first_symbol_idx++) {
|
||||
// Iterate over possible power control offset
|
||||
float power_control_offset_begin = isnormal(power_control_offset) ? power_control_offset : -8.0f;
|
||||
float power_control_offset_end = isnormal(power_control_offset) ? power_control_offset : 15.0f;
|
||||
for (resource.power_control_offset = power_control_offset_begin;
|
||||
resource.power_control_offset <= power_control_offset_end;
|
||||
resource.power_control_offset += 1.0f) {
|
||||
// Iterate over all possible starting number of PRB
|
||||
uint32_t start_rb_begin = (start_rb != UINT32_MAX) ? start_rb : 0;
|
||||
uint32_t start_rb_end = (start_rb != UINT32_MAX) ? start_rb : carrier.nof_prb - 24;
|
||||
for (resource.resource_mapping.freq_band.start_rb = start_rb_begin;
|
||||
resource.resource_mapping.freq_band.start_rb <= start_rb_end;
|
||||
resource.resource_mapping.freq_band.start_rb += 4) {
|
||||
// Iterate over all possible number of PRB
|
||||
uint32_t nof_rb_begin = (nof_rb != UINT32_MAX) ? nof_rb : 24;
|
||||
uint32_t nof_rb_end =
|
||||
(nof_rb != UINT32_MAX) ? nof_rb : (carrier.nof_prb - resource.resource_mapping.freq_band.start_rb);
|
||||
for (resource.resource_mapping.freq_band.nof_rb = nof_rb_begin;
|
||||
resource.resource_mapping.freq_band.nof_rb <= nof_rb_end;
|
||||
resource.resource_mapping.freq_band.nof_rb += 4) {
|
||||
// Iterate for all slot numbers
|
||||
for (slot_cfg.idx = 0; slot_cfg.idx < SRSRAN_NSLOTS_PER_FRAME_NR(carrier.scs); slot_cfg.idx++) {
|
||||
// Steer Frequency allocation
|
||||
for (uint32_t freq_dom_alloc = 0; freq_dom_alloc < nof_freq_dom_alloc; freq_dom_alloc++) {
|
||||
for (uint32_t i = 0; i < nof_freq_dom_alloc; i++) {
|
||||
resource.resource_mapping.frequency_domain_alloc[i] = i == freq_dom_alloc;
|
||||
}
|
||||
|
||||
// Call actual test
|
||||
TESTASSERT(nzp_test_case(&slot_cfg, &resource, awgn, grid) == SRSRAN_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int nzp_test_trs(srsran_channel_awgn_t* awgn, cf_t* grid)
|
||||
{
|
||||
// Slot configuration
|
||||
srsran_slot_cfg_t slot_cfg = {};
|
||||
|
||||
// Item 1
|
||||
// NZP-CSI-RS-Resource
|
||||
// nzp-CSI-RS-ResourceId: 1
|
||||
// resourceMapping
|
||||
// frequencyDomainAllocation: row1 (0)
|
||||
// row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1]
|
||||
// nrofPorts: p1 (0)
|
||||
// firstOFDMSymbolInTimeDomain: 4
|
||||
// cdm-Type: noCDM (0)
|
||||
// density: three (2)
|
||||
// three: NULL
|
||||
// freqBand
|
||||
// startingRB: 0
|
||||
// nrofRBs: 52
|
||||
// powerControlOffset: 0dB
|
||||
// powerControlOffsetSS: db0 (1)
|
||||
// scramblingID: 0
|
||||
// periodicityAndOffset: slots40 (7)
|
||||
// slots40: 11
|
||||
// qcl-InfoPeriodicCSI-RS: 0
|
||||
srsran_csi_rs_nzp_resource_t resource1 = {};
|
||||
resource1.id = 1;
|
||||
resource1.resource_mapping.frequency_domain_alloc[0] = 0;
|
||||
resource1.resource_mapping.frequency_domain_alloc[1] = 0;
|
||||
resource1.resource_mapping.frequency_domain_alloc[2] = 0;
|
||||
resource1.resource_mapping.frequency_domain_alloc[3] = 1;
|
||||
resource1.resource_mapping.nof_ports = 1;
|
||||
resource1.resource_mapping.first_symbol_idx = 4;
|
||||
resource1.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm;
|
||||
resource1.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three;
|
||||
resource1.resource_mapping.freq_band.start_rb = 0;
|
||||
resource1.resource_mapping.freq_band.nof_rb = carrier.nof_prb;
|
||||
resource1.power_control_offset = 0;
|
||||
resource1.power_control_offset_ss = 0;
|
||||
resource1.periodicity.period = 40;
|
||||
resource1.periodicity.offset = 11;
|
||||
|
||||
// Item 2
|
||||
// NZP-CSI-RS-Resource
|
||||
// nzp-CSI-RS-ResourceId: 2
|
||||
// resourceMapping
|
||||
// frequencyDomainAllocation: row1 (0)
|
||||
// row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1]
|
||||
// nrofPorts: p1 (0)
|
||||
// firstOFDMSymbolInTimeDomain: 8
|
||||
// cdm-Type: noCDM (0)
|
||||
// density: three (2)
|
||||
// three: NULL
|
||||
// freqBand
|
||||
// startingRB: 0
|
||||
// nrofRBs: 52
|
||||
// powerControlOffset: 0dB
|
||||
// powerControlOffsetSS: db0 (1)
|
||||
// scramblingID: 0
|
||||
// periodicityAndOffset: slots40 (7)
|
||||
// slots40: 11
|
||||
// qcl-InfoPeriodicCSI-RS: 0
|
||||
srsran_csi_rs_nzp_resource_t resource2 = {};
|
||||
resource2.id = 1;
|
||||
resource2.resource_mapping.frequency_domain_alloc[0] = 0;
|
||||
resource2.resource_mapping.frequency_domain_alloc[1] = 0;
|
||||
resource2.resource_mapping.frequency_domain_alloc[2] = 0;
|
||||
resource2.resource_mapping.frequency_domain_alloc[3] = 1;
|
||||
resource2.resource_mapping.nof_ports = 1;
|
||||
resource2.resource_mapping.first_symbol_idx = 8;
|
||||
resource2.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm;
|
||||
resource2.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three;
|
||||
resource2.resource_mapping.freq_band.start_rb = 0;
|
||||
resource2.resource_mapping.freq_band.nof_rb = carrier.nof_prb;
|
||||
resource2.power_control_offset = 0;
|
||||
resource2.power_control_offset_ss = 0;
|
||||
resource2.periodicity.period = 40;
|
||||
resource2.periodicity.offset = 11;
|
||||
|
||||
// Item 3
|
||||
// NZP-CSI-RS-Resource
|
||||
// nzp-CSI-RS-ResourceId: 3
|
||||
// resourceMapping
|
||||
// frequencyDomainAllocation: row1 (0)
|
||||
// row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1]
|
||||
// nrofPorts: p1 (0)
|
||||
// firstOFDMSymbolInTimeDomain: 4
|
||||
// cdm-Type: noCDM (0)
|
||||
// density: three (2)
|
||||
// three: NULL
|
||||
// freqBand
|
||||
// startingRB: 0
|
||||
// nrofRBs: 52
|
||||
// powerControlOffset: 0dB
|
||||
// powerControlOffsetSS: db0 (1)
|
||||
// scramblingID: 0
|
||||
// periodicityAndOffset: slots40 (7)
|
||||
// slots40: 12
|
||||
// qcl-InfoPeriodicCSI-RS: 0
|
||||
srsran_csi_rs_nzp_resource_t resource3 = {};
|
||||
resource3.id = 1;
|
||||
resource3.resource_mapping.frequency_domain_alloc[0] = 0;
|
||||
resource3.resource_mapping.frequency_domain_alloc[1] = 0;
|
||||
resource3.resource_mapping.frequency_domain_alloc[2] = 0;
|
||||
resource3.resource_mapping.frequency_domain_alloc[3] = 1;
|
||||
resource3.resource_mapping.nof_ports = 1;
|
||||
resource3.resource_mapping.first_symbol_idx = 4;
|
||||
resource3.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm;
|
||||
resource3.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three;
|
||||
resource3.resource_mapping.freq_band.start_rb = 0;
|
||||
resource3.resource_mapping.freq_band.nof_rb = carrier.nof_prb;
|
||||
resource3.power_control_offset = 0;
|
||||
resource3.power_control_offset_ss = 0;
|
||||
resource3.periodicity.period = 40;
|
||||
resource3.periodicity.offset = 12;
|
||||
|
||||
// Item 4
|
||||
// NZP-CSI-RS-Resource
|
||||
// nzp-CSI-RS-ResourceId: 4
|
||||
// resourceMapping
|
||||
// frequencyDomainAllocation: row1 (0)
|
||||
// row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1]
|
||||
// nrofPorts: p1 (0)
|
||||
// firstOFDMSymbolInTimeDomain: 8
|
||||
// cdm-Type: noCDM (0)
|
||||
// density: three (2)
|
||||
// three: NULL
|
||||
// freqBand
|
||||
// startingRB: 0
|
||||
// nrofRBs: 52
|
||||
// powerControlOffset: 0dB
|
||||
// powerControlOffsetSS: db0 (1)
|
||||
// scramblingID: 0
|
||||
// periodicityAndOffset: slots40 (7)
|
||||
// slots40: 12
|
||||
// qcl-InfoPeriodicCSI-RS: 0
|
||||
srsran_csi_rs_nzp_resource_t resource4 = {};
|
||||
resource4.id = 1;
|
||||
resource4.resource_mapping.frequency_domain_alloc[0] = 0;
|
||||
resource4.resource_mapping.frequency_domain_alloc[1] = 0;
|
||||
resource4.resource_mapping.frequency_domain_alloc[2] = 0;
|
||||
resource4.resource_mapping.frequency_domain_alloc[3] = 1;
|
||||
resource4.resource_mapping.nof_ports = 1;
|
||||
resource4.resource_mapping.first_symbol_idx = 8;
|
||||
resource4.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm;
|
||||
resource4.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three;
|
||||
resource4.resource_mapping.freq_band.start_rb = 0;
|
||||
resource4.resource_mapping.freq_band.nof_rb = carrier.nof_prb;
|
||||
resource4.power_control_offset = 0;
|
||||
resource4.power_control_offset_ss = 0;
|
||||
resource4.periodicity.period = 40;
|
||||
resource4.periodicity.offset = 12;
|
||||
|
||||
// NZP-CSI-RS-ResourceSet
|
||||
// nzp-CSI-ResourceSetId: 1
|
||||
// nzp-CSI-RS-Resources: 4 items
|
||||
// Item 0
|
||||
// NZP-CSI-RS-ResourceId: 1
|
||||
// Item 1
|
||||
// NZP-CSI-RS-ResourceId: 2
|
||||
// Item 2
|
||||
// NZP-CSI-RS-ResourceId: 3
|
||||
// Item 3
|
||||
// NZP-CSI-RS-ResourceId: 4
|
||||
// trs-Info: true (0)
|
||||
srsran_csi_rs_nzp_set_t set = {};
|
||||
set.data[set.count++] = resource1;
|
||||
set.data[set.count++] = resource2;
|
||||
set.data[set.count++] = resource3;
|
||||
set.data[set.count++] = resource4;
|
||||
set.trs_info = true;
|
||||
|
||||
for (slot_cfg.idx = 0; slot_cfg.idx < resource1.periodicity.period; slot_cfg.idx++) {
|
||||
// Put NZP-CSI-RS TRS signals
|
||||
int ret = srsran_csi_rs_nzp_put_set(&carrier, &slot_cfg, &set, grid);
|
||||
|
||||
// Check return
|
||||
if (slot_cfg.idx == 11 || slot_cfg.idx == 12) {
|
||||
TESTASSERT(ret == 2);
|
||||
} else {
|
||||
TESTASSERT(ret == 0);
|
||||
}
|
||||
|
||||
// Configure N0 and add Noise
|
||||
TESTASSERT(srsran_channel_awgn_set_n0(awgn, (float)set.data[0].power_control_offset - snr_dB) == SRSRAN_SUCCESS);
|
||||
srsran_channel_awgn_run_c(awgn, grid, grid, SRSRAN_SLOT_LEN_RE_NR(carrier.nof_prb));
|
||||
|
||||
// Measure
|
||||
srsran_csi_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)
|
||||
{
|
||||
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 ret = SRSRAN_ERROR;
|
||||
srsran_slot_cfg_t slot_cfg = {};
|
||||
srsran_csi_rs_nzp_resource_t resource = {};
|
||||
srsran_channel_awgn_t awgn = {};
|
||||
int ret = SRSRAN_ERROR;
|
||||
srsran_channel_awgn_t awgn = {};
|
||||
|
||||
parse_args(argc, argv);
|
||||
|
||||
|
@ -147,56 +406,12 @@ int main(int argc, char** argv)
|
|||
goto clean_exit;
|
||||
}
|
||||
|
||||
// Fixed parameters, other params are not implemented
|
||||
resource.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm;
|
||||
resource.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three;
|
||||
resource.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1;
|
||||
resource.resource_mapping.nof_ports = 1;
|
||||
if (nzp_test_brute(&awgn, grid) < SRSRAN_SUCCESS) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
// 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 (test(&slot_cfg, &resource, &awgn, grid) < SRSRAN_SUCCESS) {
|
||||
goto clean_exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nzp_test_trs(&awgn, grid) < SRSRAN_SUCCESS) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
ret = SRSRAN_SUCCESS;
|
||||
|
@ -215,4 +430,4 @@ clean_exit:
|
|||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology)
|
||||
float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, srsran_subcarrier_spacing_t scs)
|
||||
{
|
||||
// l0 must be smaller than l1
|
||||
if (l0 >= l1) {
|
||||
|
@ -172,7 +172,7 @@ float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology)
|
|||
uint32_t count = l1 - l0;
|
||||
|
||||
// Compute at what symbol there is a longer CP
|
||||
uint32_t cp_boundary = 7U << numerology;
|
||||
uint32_t cp_boundary = 7U << (uint32_t)scs;
|
||||
|
||||
// Select whether extra CP shall be added
|
||||
uint32_t extra_cp = 0;
|
||||
|
@ -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;
|
||||
|
||||
// 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)
|
||||
|
|
|
@ -220,10 +220,17 @@ void srsran_sequence_state_init(srsran_sequence_state_t* s, uint32_t seed)
|
|||
s->x2 = sequence_get_x2_init(seed);
|
||||
}
|
||||
|
||||
#define FLOAT_U32_XOR(DST, SRC, U32_MASK) \
|
||||
do { \
|
||||
uint32_t temp_u32; \
|
||||
memcpy(&temp_u32, &(SRC), 4); \
|
||||
temp_u32 ^= (U32_MASK); \
|
||||
memcpy(&(DST), &temp_u32, 4); \
|
||||
} while (false)
|
||||
|
||||
void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* out, uint32_t length)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
const float xor [2] = {+0.0F, -0.0F};
|
||||
uint32_t i = 0;
|
||||
|
||||
if (length >= SEQUENCE_PAR_BITS) {
|
||||
for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) {
|
||||
|
@ -255,7 +262,7 @@ void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float*
|
|||
#endif
|
||||
// Finish the parallel bits with generic code
|
||||
for (; j < SEQUENCE_PAR_BITS; j++) {
|
||||
*((uint32_t*)&out[i + j]) = *((uint32_t*)&value) ^ *((uint32_t*)&xor[(c >> j) & 1U]);
|
||||
FLOAT_U32_XOR(out[i + j], value, (c << (31U - j)) & 0x80000000);
|
||||
}
|
||||
|
||||
// Step sequences
|
||||
|
@ -265,7 +272,59 @@ void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float*
|
|||
}
|
||||
|
||||
for (; i < length; i++) {
|
||||
*((uint32_t*)&out[i]) = *((uint32_t*)&value) ^ *((uint32_t*)&xor[(s->x1 ^ s->x2) & 1U]);
|
||||
FLOAT_U32_XOR(out[i], value, (s->x1 ^ s->x2) << 31U);
|
||||
|
||||
// Step sequences
|
||||
s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1);
|
||||
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
|
||||
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], in[i], (s->x1 ^ s->x2) << 31U);
|
||||
|
||||
// Step sequences
|
||||
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)
|
||||
{
|
||||
uint32_t x1 = sequence_x1_init; // X1 initial state is fix
|
||||
uint32_t x2 = sequence_get_x2_init(seed); // loads x2 initial state
|
||||
srsran_sequence_state_t seq = {};
|
||||
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
|
||||
__m128 v = _mm_loadu_ps(in + i + j);
|
||||
|
||||
// Loads input and perform sign XOR
|
||||
v = _mm_xor_ps((__m128)mask, v);
|
||||
|
||||
_mm_storeu_ps(out + i + j, v);
|
||||
}
|
||||
#endif
|
||||
for (; j < SEQUENCE_PAR_BITS; j++) {
|
||||
((uint32_t*)out)[i + j] = ((uint32_t*)in)[i] ^ (((c >> j) & 1U) << 31U);
|
||||
}
|
||||
|
||||
// Step sequences
|
||||
x1 = sequence_gen_LTE_pr_memless_step_par_x1(x1);
|
||||
x2 = sequence_gen_LTE_pr_memless_step_par_x2(x2);
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < length; i++) {
|
||||
((uint32_t*)out)[i] = ((uint32_t*)in)[i] ^ (((x1 ^ x2) & 1U) << 31U);
|
||||
|
||||
// Step sequences
|
||||
x1 = sequence_gen_LTE_pr_memless_step_x1(x1);
|
||||
x2 = sequence_gen_LTE_pr_memless_step_x2(x2);
|
||||
}
|
||||
srsran_sequence_state_apply_f(&seq, in, out, length);
|
||||
}
|
||||
|
||||
void srsran_sequence_apply_s(const int16_t* in, int16_t* out, uint32_t length, uint32_t seed)
|
||||
|
@ -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];
|
||||
}
|
||||
#else // SEQUENCE_PAR_BITS % 8 == 0
|
||||
#else // SEQUENCE_PAR_BITS % 8 == 0
|
||||
while (i < (length / 8 - (SEQUENCE_PAR_BITS - 1) / 8)) {
|
||||
uint32_t c = (uint32_t)(x1 ^ x2);
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#define CSI_WIDEBAND_CSI_NOF_BITS 4
|
||||
|
||||
#define CSI_DEFAULT_ALPHA 0.5f
|
||||
|
||||
/// Implements SNRI to CQI conversion
|
||||
uint32_t csi_snri_db_to_cqi(srsran_csi_cqi_table_t table, float snri_db)
|
||||
{
|
||||
|
@ -43,11 +45,11 @@ static bool csi_report_trigger(const srsran_csi_hl_report_cfg_t* cfg, uint32_t s
|
|||
return false;
|
||||
}
|
||||
|
||||
static void csi_wideband_cri_ri_pmi_cqi_quantify(const srsran_csi_hl_report_cfg_t* cfg,
|
||||
const srsran_csi_measurements_t* channel_meas,
|
||||
const srsran_csi_measurements_t* interf_meas,
|
||||
srsran_csi_report_cfg_t* report_cfg,
|
||||
srsran_csi_report_value_t* report_value)
|
||||
static void csi_wideband_cri_ri_pmi_cqi_quantify(const srsran_csi_hl_report_cfg_t* cfg,
|
||||
const srsran_csi_channel_measurements_t* channel_meas,
|
||||
const srsran_csi_channel_measurements_t* interf_meas,
|
||||
srsran_csi_report_cfg_t* report_cfg,
|
||||
srsran_csi_report_value_t* report_value)
|
||||
{
|
||||
// Take SNR by default
|
||||
float wideband_sinr_db = channel_meas->wideband_snr_db;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg,
|
||||
uint32_t slot_idx,
|
||||
const srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES],
|
||||
srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT],
|
||||
srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT])
|
||||
int srsran_csi_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)
|
||||
{
|
||||
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;
|
||||
|
||||
|
@ -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);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
const srsran_csi_measurements_t* channel_meas = &measurements[cfg->reports->channel_meas_id];
|
||||
const srsran_csi_channel_measurements_t* channel_meas = &measurements[cfg->reports->channel_meas_id];
|
||||
|
||||
// Select interference measurement
|
||||
const srsran_csi_measurements_t* interf_meas = NULL;
|
||||
const srsran_csi_channel_measurements_t* interf_meas = NULL;
|
||||
if (cfg->reports->interf_meas_present) {
|
||||
if (cfg->reports->interf_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) {
|
||||
ERROR("Interference measurement ID (%d) is out of range", cfg->reports->interf_meas_id);
|
||||
|
|
|
@ -93,6 +93,65 @@ static uint32_t dci_nr_ptrs_size(const srsran_dci_cfg_nr_t* cfg)
|
|||
return 2;
|
||||
}
|
||||
|
||||
static uint32_t dci_nr_dl_ports_size(const srsran_dci_cfg_nr_t* cfg)
|
||||
{
|
||||
uint32_t ret = 4;
|
||||
|
||||
if (cfg->pdsch_dmrs_type == srsran_dmrs_sch_type_2) {
|
||||
ret++;
|
||||
}
|
||||
if (cfg->pdsch_dmrs_max_len == srsran_dmrs_sch_len_2) {
|
||||
ret++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t dci_nr_ul_ports_size(const srsran_dci_cfg_nr_t* cfg)
|
||||
{
|
||||
// 2 bits as defined by Tables 7.3.1.1.2-6, if transform precoder is enabled, dmrs-Type=1, and maxLength=1;
|
||||
if (cfg->enable_transform_precoding == true && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 &&
|
||||
cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
// 4 bits as defined by Tables 7.3.1.1.2-7, if transform precoder is enabled, dmrs-Type=1, and maxLength=2;
|
||||
if (cfg->enable_transform_precoding == true && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 &&
|
||||
cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_2) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
// 3 bits as defined by Tables 7.3.1.1.2-8/9/10/11, if transform precoder is disabled, dmrs-Type=1, and maxLength=1
|
||||
if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 &&
|
||||
cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 4 bits as defined by Tables 7.3.1.1.2-12/13/14/15, if transform precoder is disabled, dmrs-Type=1, and
|
||||
// maxLength=2
|
||||
if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 &&
|
||||
cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_2) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
// 4 bits as defined by Tables 7.3.1.1.2-16/17/18/19, if transform precoder is disabled, dmrs-Type=2, and
|
||||
// maxLength=1
|
||||
if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_2 &&
|
||||
cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
// 5 bits as defined by Tables 7.3.1.1.2-20/21/22/23, if transform precoder is disabled, dmrs-Type=2, and
|
||||
// maxLength=2
|
||||
if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_2 &&
|
||||
cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_2) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
ERROR("Unhandled configuration");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t dci_nr_srs_id_size(const srsran_dci_cfg_nr_t* cfg)
|
||||
{
|
||||
uint32_t N_srs = SRSRAN_MIN(1, cfg->nof_srs);
|
||||
|
@ -402,12 +461,7 @@ static uint32_t dci_nr_format_0_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_
|
|||
}
|
||||
|
||||
// Antenna ports
|
||||
if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) {
|
||||
count += 3;
|
||||
} else {
|
||||
ERROR("Not implemented");
|
||||
return 0;
|
||||
}
|
||||
count += dci_nr_ul_ports_size(cfg);
|
||||
|
||||
// SRS request - 2 or 3 bits
|
||||
count += cfg->enable_sul ? 3 : 2;
|
||||
|
@ -511,12 +565,7 @@ static int dci_nr_format_0_1_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_
|
|||
}
|
||||
|
||||
// Antenna ports
|
||||
if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) {
|
||||
srsran_bit_unpack(dci->ports, &y, 3);
|
||||
} else {
|
||||
ERROR("Not implemented");
|
||||
return 0;
|
||||
}
|
||||
srsran_bit_unpack(dci->ports, &y, dci_nr_ul_ports_size(cfg));
|
||||
|
||||
// SRS request - 2 or 3 bits
|
||||
srsran_bit_unpack(dci->srs_request, &y, cfg->enable_sul ? 3 : 2);
|
||||
|
@ -628,7 +677,7 @@ static int dci_nr_format_0_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_
|
|||
}
|
||||
|
||||
// Antenna ports
|
||||
if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) {
|
||||
if (!cfg->enable_transform_precoding && cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) {
|
||||
dci->ports = srsran_bit_pack(&y, 3);
|
||||
} else {
|
||||
ERROR("Not implemented");
|
||||
|
@ -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
|
||||
if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) {
|
||||
if (dci_nr_ul_ports_size(cfg)) {
|
||||
len = srsran_print_check(str, str_len, len, "ports=%d ", dci->ports);
|
||||
} else {
|
||||
ERROR("Not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// SRS request - 2 bits
|
||||
|
@ -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_search_space_type_t ss_type = dci->ctx.ss_type;
|
||||
uint32_t N_DL_BWP_RB = SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)
|
||||
? (q->cfg.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw
|
||||
: q->cfg.bwp_dl_active_bw;
|
||||
? (q->cfg.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw
|
||||
: q->cfg.bwp_dl_active_bw;
|
||||
|
||||
// Identifier for DCI formats – 1 bits
|
||||
if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) {
|
||||
|
@ -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_search_space_type_t ss_type = msg->ctx.ss_type;
|
||||
uint32_t N_DL_BWP_RB = SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)
|
||||
? (q->cfg.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw
|
||||
: q->cfg.bwp_dl_active_bw;
|
||||
? (q->cfg.coreset0_bw == 0) ? q->cfg.bwp_dl_initial_bw : q->cfg.coreset0_bw
|
||||
: q->cfg.bwp_dl_active_bw;
|
||||
|
||||
uint32_t nof_bits = srsran_dci_nr_size(q, ss_type, srsran_dci_format_nr_1_0);
|
||||
if (msg->nof_bits != nof_bits) {
|
||||
|
@ -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);
|
||||
|
||||
// Antenna port(s) – 4, 5, or 6 bits
|
||||
count += 4;
|
||||
if (cfg->pdsch_dmrs_type2) {
|
||||
count++;
|
||||
}
|
||||
if (cfg->pdsch_dmrs_double) {
|
||||
count++;
|
||||
}
|
||||
count += dci_nr_dl_ports_size(cfg);
|
||||
|
||||
// Transmission configuration indication – 0 or 3 bits
|
||||
if (cfg->pdsch_tci) {
|
||||
|
@ -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));
|
||||
|
||||
// Antenna port(s) – 4, 5, or 6 bits
|
||||
srsran_bit_unpack(dci->ports, &y, 4);
|
||||
if (cfg->pdsch_dmrs_type2) {
|
||||
y++;
|
||||
}
|
||||
if (cfg->pdsch_dmrs_double) {
|
||||
y++;
|
||||
}
|
||||
srsran_bit_unpack(dci->ports, &y, dci_nr_dl_ports_size(cfg));
|
||||
|
||||
// Transmission configuration indication – 0 or 3 bits
|
||||
if (cfg->pdsch_tci) {
|
||||
|
@ -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));
|
||||
|
||||
// Antenna port(s) – 4, 5, or 6 bits
|
||||
dci->ports = srsran_bit_pack(&y, 4);
|
||||
if (cfg->pdsch_dmrs_type2) {
|
||||
y++;
|
||||
}
|
||||
if (cfg->pdsch_dmrs_double) {
|
||||
y++;
|
||||
}
|
||||
dci->ports = srsran_bit_pack(&y, dci_nr_dl_ports_size(cfg));
|
||||
|
||||
// Transmission configuration indication – 0 or 3 bits
|
||||
if (cfg->pdsch_tci) {
|
||||
|
@ -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
|
||||
len = srsran_print_check(str, str_len, len, "ports=%d ", dci->ports);
|
||||
if (dci_nr_dl_ports_size(cfg) > 0) {
|
||||
len = srsran_print_check(str, str_len, len, "ports=%d ", dci->ports);
|
||||
}
|
||||
|
||||
// Transmission configuration indication – 0 or 3 bits
|
||||
if (cfg->pdsch_tci) {
|
||||
|
|
|
@ -198,69 +198,98 @@ int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg,
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(const srsran_dmrs_sch_cfg_t* cfg,
|
||||
srsran_sch_grant_nr_t* grant)
|
||||
int srsran_ra_dl_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg,
|
||||
const srsran_dci_dl_nr_t* dci,
|
||||
srsran_dmrs_sch_len_t* dmrs_duration)
|
||||
{
|
||||
if (cfg == NULL || grant == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
// Table 7.3.1.2.2-1: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=1
|
||||
// Table 7.3.1.2.2-3: Antenna port(s) (1000 + DMRS port), dmrs-Type=2, maxLength=1
|
||||
if (cfg->dmrs_max_length == srsran_dmrs_sch_len_1) {
|
||||
*dmrs_duration = srsran_dmrs_sch_len_1;
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
/* According to TS 38.214 V15.10.0 5.1.6.1.3 CSI-RS for mobility:
|
||||
* When receiving PDSCH scheduled by DCI format 1_0, the UE shall assume the number of DM-RS CDM groups without data
|
||||
* is 1 which corresponds to CDM group 0 for the case of PDSCH with allocation duration of 2 symbols, and the UE
|
||||
* shall assume that the number of DM-RS CDM groups without data is 2 which corresponds to CDM group {0,1} for all
|
||||
* other cases.
|
||||
*/
|
||||
if (cfg->length == srsran_dmrs_sch_len_2) {
|
||||
grant->nof_dmrs_cdm_groups_without_data = 1;
|
||||
} else {
|
||||
grant->nof_dmrs_cdm_groups_without_data = 2;
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
/* RBG size for type0 scheduling as in table 5.1.2.2.1-1 of 36.214 */
|
||||
uint32_t srsran_ra_dl_nr_type0_P(uint32_t bwp_size, bool config_is_1)
|
||||
{
|
||||
if (bwp_size <= 36) {
|
||||
return config_is_1 ? 2 : 4;
|
||||
} else if (bwp_size <= 72) {
|
||||
return config_is_1 ? 4 : 8;
|
||||
} else if (bwp_size <= 144) {
|
||||
return config_is_1 ? 8 : 16;
|
||||
} else {
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
|
||||
static int ra_freq_type0(const srsran_carrier_nr_t* carrier,
|
||||
const srsran_sch_hl_cfg_nr_t* cfg,
|
||||
const srsran_dci_dl_nr_t* dci_dl,
|
||||
srsran_sch_grant_nr_t* grant)
|
||||
{
|
||||
uint32_t P = srsran_ra_dl_nr_type0_P(carrier->nof_prb, cfg->rbg_size_cfg_1);
|
||||
|
||||
uint32_t N_rbg = (int)ceilf((float)(carrier->nof_prb + (carrier->start % P)) / P);
|
||||
uint32_t rbg_offset = 0;
|
||||
for (uint32_t i = 0; i < N_rbg; i++) {
|
||||
uint32_t rbg_size = P;
|
||||
if (i == 0) {
|
||||
rbg_size -= (carrier->start % P);
|
||||
} else if ((i == N_rbg - 1) && ((carrier->nof_prb + carrier->start) % P) > 0) {
|
||||
rbg_size = (carrier->nof_prb + carrier->start) % P;
|
||||
// Table 7.3.1.2.2-2: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=2
|
||||
if (cfg->dmrs_type == srsran_dmrs_sch_type_1 && cfg->dmrs_max_length == srsran_dmrs_sch_len_2) {
|
||||
// Only one codeword supported!
|
||||
if (dci->ports < 12) {
|
||||
*dmrs_duration = srsran_dmrs_sch_len_1;
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
if (dci_dl->freq_domain_assigment & (1 << (N_rbg - i - 1))) {
|
||||
for (uint32_t j = 0; j < rbg_size; j++) {
|
||||
if (rbg_offset + j < carrier->nof_prb) {
|
||||
grant->prb_idx[rbg_offset + j] = true;
|
||||
grant->nof_prb++;
|
||||
}
|
||||
|
||||
if (dci->ports < 31) {
|
||||
*dmrs_duration = srsran_dmrs_sch_len_2;
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
ERROR("reserved value for ports (%d)", dci->ports);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (dci->ports < 24) {
|
||||
*dmrs_duration = srsran_dmrs_sch_len_2;
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
ERROR("reserved value for ports (%d)", dci->ports);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
ERROR("Unhandled case");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg,
|
||||
const srsran_dci_dl_nr_t* dci,
|
||||
uint32_t L)
|
||||
{
|
||||
// According to TS 38.214 5.1.6.2 DM-RS reception procedure
|
||||
switch (dci->ctx.format) {
|
||||
case srsran_dci_format_nr_1_0:
|
||||
// When receiving PDSCH scheduled by DCI format 1_0, the UE shall assume the number of DM-RS CDM groups
|
||||
// without data is 1 which corresponds to CDM group 0 for the case of PDSCH with allocation duration of 2 symbols,
|
||||
// and the UE shall assume that the number of DM-RS CDM groups without data is 2 which corresponds to CDM group
|
||||
// {0,1} for all other cases.
|
||||
if (L == 2) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
rbg_offset += rbg_size;
|
||||
return SRSRAN_SUCCESS;
|
||||
case srsran_dci_format_nr_1_1:
|
||||
// When receiving PDSCH scheduled by DCI format 1_1, the UE shall assume that the CDM groups indicated in the
|
||||
// configured index from Tables 7.3.1.2.2-1, 7.3.1.2.2-2, 7.3.1.2.2-3, 7.3.1.2.2-4 of [5, TS. 38.212] contain
|
||||
// potential co- scheduled downlink DM-RS and are not used for data transmission, where "1", "2" and "3" for the
|
||||
// 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;
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -268,17 +297,34 @@ int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier,
|
|||
const srsran_dci_dl_nr_t* dci_dl,
|
||||
srsran_sch_grant_nr_t* grant)
|
||||
{
|
||||
if (cfg == NULL || grant == NULL || dci_dl == NULL) {
|
||||
if (carrier == NULL || cfg == NULL || grant == NULL || dci_dl == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
// RA scheme
|
||||
// The UE shall assume that when the scheduling grant is received with DCI format 1_0 , then downlink resource
|
||||
// allocation type 1 is used.
|
||||
if (dci_dl->ctx.format == srsran_dci_format_nr_1_0) {
|
||||
// when the scheduling grant is received with DCI format 1_0 , then downlink resource allocation type 1 is used.
|
||||
return ra_helper_freq_type1(carrier->nof_prb, dci_dl->freq_domain_assigment, grant);
|
||||
}
|
||||
|
||||
ra_freq_type0(carrier, cfg, dci_dl, grant);
|
||||
ERROR("Only DCI Format 1_0 is supported");
|
||||
// If the scheduling DCI is configured to indicate the downlink resource allocation type as part of the Frequency
|
||||
// domain resource assignment field by setting a higher layer parameter resourceAllocation in pdsch-Config to
|
||||
// 'dynamicswitch', the UE shall use downlink resource allocation type 0 or type 1 as defined by this DCI field.
|
||||
if (cfg->alloc == srsran_resource_alloc_dynamic) {
|
||||
ERROR("Unsupported dynamic resource allocation");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Otherwise the UE shall use the downlink frequency resource allocation type as defined by the higher layer parameter
|
||||
// resourceAllocation.
|
||||
if (cfg->alloc == srsran_resource_alloc_type1) {
|
||||
return ra_helper_freq_type1(carrier->nof_prb, dci_dl->freq_domain_assigment, grant);
|
||||
}
|
||||
|
||||
if (cfg->alloc == srsran_resource_alloc_type0) {
|
||||
return ra_helper_freq_type0(carrier, cfg, dci_dl->freq_domain_assigment, grant);
|
||||
}
|
||||
|
||||
ERROR("Unhandled case");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,52 @@
|
|||
#define SRSRAN_RA_HELPER_H
|
||||
|
||||
#include "srsran/phy/utils/debug.h"
|
||||
#include "srsran/phy/utils/vector.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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* hl_cfg, srsran_sch_grant_nr_t* grant, srsran_sch_cfg_nr_t* cfg)
|
||||
static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* hl_cfg, const srsran_dci_dl_nr_t* dci, srsran_sch_cfg_nr_t* cfg)
|
||||
{
|
||||
const bool dedicated_dmrs_present =
|
||||
(grant->mapping == srsran_sch_mapping_type_A) ? hl_cfg->dmrs_typeA.present : hl_cfg->dmrs_typeB.present;
|
||||
(cfg->grant.mapping == srsran_sch_mapping_type_A) ? hl_cfg->dmrs_typeA.present : hl_cfg->dmrs_typeB.present;
|
||||
|
||||
if (grant->dci_format == srsran_dci_format_nr_1_0 || !dedicated_dmrs_present) {
|
||||
if (grant->mapping == srsran_sch_mapping_type_A) {
|
||||
if (dci->ctx.format == srsran_dci_format_nr_1_0 || !dedicated_dmrs_present) {
|
||||
if (cfg->grant.mapping == srsran_sch_mapping_type_A) {
|
||||
// Absent default values are defined is TS 38.331 - DMRS-DownlinkConfig
|
||||
cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2;
|
||||
cfg->dmrs.type = srsran_dmrs_sch_type_1;
|
||||
|
@ -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;
|
||||
}
|
||||
} else {
|
||||
if (grant->mapping == srsran_sch_mapping_type_A) {
|
||||
// Load DMRS duration
|
||||
if (srsran_ra_dl_nr_nof_front_load_symbols(hl_cfg, dci, &cfg->dmrs.length) < SRSRAN_SUCCESS) {
|
||||
ERROR("Loading number of front-load symbols");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// DMRS Type
|
||||
cfg->dmrs.type = hl_cfg->dmrs_type;
|
||||
|
||||
// Other DMRS configuration
|
||||
if (cfg->grant.mapping == srsran_sch_mapping_type_A) {
|
||||
cfg->dmrs.additional_pos = hl_cfg->dmrs_typeA.additional_pos;
|
||||
cfg->dmrs.type = hl_cfg->dmrs_typeA.type;
|
||||
cfg->dmrs.length = hl_cfg->dmrs_typeA.length;
|
||||
cfg->dmrs.scrambling_id0_present = false;
|
||||
cfg->dmrs.scrambling_id1_present = false;
|
||||
} else {
|
||||
cfg->dmrs.additional_pos = hl_cfg->dmrs_typeB.additional_pos;
|
||||
cfg->dmrs.type = hl_cfg->dmrs_typeB.type;
|
||||
cfg->dmrs.length = hl_cfg->dmrs_typeB.length;
|
||||
cfg->dmrs.scrambling_id0_present = false;
|
||||
cfg->dmrs.scrambling_id1_present = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Set number of DMRS CDM groups without data
|
||||
if (grant->dci_format == srsran_dci_format_nr_1_0) {
|
||||
if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&cfg->dmrs, grant) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error loading number of DMRS CDM groups");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
} else {
|
||||
ERROR("Invalid case");
|
||||
int n = srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(hl_cfg, dci, cfg->grant.L);
|
||||
if (n < SRSRAN_SUCCESS) {
|
||||
ERROR("Error loading number of DMRS CDM groups");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
cfg->grant.nof_dmrs_cdm_groups_without_data = (uint32_t)n;
|
||||
|
||||
// Set DMRS power offset Table 6.2.2-1: The ratio of PUSCH EPRE to DM-RS EPRE
|
||||
if (ra_nr_dmrs_power_offset(grant) < SRSRAN_SUCCESS) {
|
||||
if (ra_nr_dmrs_power_offset(&cfg->grant) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error setting DMRS power offset");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
@ -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
|
||||
// ...
|
||||
|
||||
pdsch_grant->nof_layers = 1;
|
||||
pdsch_grant->dci_format = dci_dl->ctx.format;
|
||||
pdsch_grant->rnti = dci_dl->ctx.rnti;
|
||||
pdsch_grant->rnti_type = dci_dl->ctx.rnti_type;
|
||||
pdsch_grant->tb[0].rv = dci_dl->rv;
|
||||
pdsch_grant->tb[0].mcs = dci_dl->mcs;
|
||||
pdsch_grant->tb[0].ndi = dci_dl->ndi;
|
||||
pdsch_grant->nof_layers = 1;
|
||||
pdsch_grant->dci_format = dci_dl->ctx.format;
|
||||
pdsch_grant->rnti = dci_dl->ctx.rnti;
|
||||
pdsch_grant->rnti_type = dci_dl->ctx.rnti_type;
|
||||
pdsch_grant->tb[0].rv = dci_dl->rv;
|
||||
pdsch_grant->tb[0].mcs = dci_dl->mcs;
|
||||
pdsch_grant->tb[0].ndi = dci_dl->ndi;
|
||||
pdsch_cfg->sch_cfg.mcs_table = pdsch_hl_cfg->mcs_table;
|
||||
|
||||
// 5.1.4 PDSCH resource mapping
|
||||
if (ra_dl_resource_mapping(carrier, slot, pdsch_hl_cfg, pdsch_cfg) < SRSRAN_SUCCESS) {
|
||||
|
@ -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
|
||||
if (ra_dl_dmrs(pdsch_hl_cfg, pdsch_grant, pdsch_cfg) < SRSRAN_SUCCESS) {
|
||||
if (ra_dl_dmrs(pdsch_hl_cfg, dci_dl, pdsch_cfg) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error selecting DMRS configuration");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
@ -706,15 +710,15 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier,
|
|||
}
|
||||
|
||||
static int
|
||||
ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, srsran_sch_grant_nr_t* pusch_grant, srsran_sch_cfg_nr_t* cfg)
|
||||
ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, const srsran_dci_ul_nr_t* dci, srsran_sch_cfg_nr_t* cfg)
|
||||
{
|
||||
const bool dedicated_dmrs_present = (pusch_grant->mapping == srsran_sch_mapping_type_A)
|
||||
const bool dedicated_dmrs_present = (cfg->grant.mapping == srsran_sch_mapping_type_A)
|
||||
? pusch_hl_cfg->dmrs_typeA.present
|
||||
: pusch_hl_cfg->dmrs_typeB.present;
|
||||
|
||||
if (pusch_grant->dci_format == srsran_dci_format_nr_0_0 || pusch_grant->dci_format == srsran_dci_format_nr_rar ||
|
||||
if (dci->ctx.format == srsran_dci_format_nr_0_0 || dci->ctx.format == srsran_dci_format_nr_rar ||
|
||||
!dedicated_dmrs_present) {
|
||||
if (pusch_grant->mapping == srsran_sch_mapping_type_A) {
|
||||
if (cfg->grant.mapping == srsran_sch_mapping_type_A) {
|
||||
// Absent default values are defined is TS 38.331 - DMRS-DownlinkConfig
|
||||
cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2;
|
||||
cfg->dmrs.type = srsran_dmrs_sch_type_1;
|
||||
|
@ -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;
|
||||
}
|
||||
} else {
|
||||
if (pusch_grant->mapping == srsran_sch_mapping_type_A) {
|
||||
// DMRS duration
|
||||
if (srsran_ra_ul_nr_nof_front_load_symbols(pusch_hl_cfg, dci, &cfg->dmrs.length) < SRSRAN_SUCCESS) {
|
||||
ERROR("Loading number of front-load symbols");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// DMRS type
|
||||
cfg->dmrs.type = pusch_hl_cfg->dmrs_type;
|
||||
|
||||
if (cfg->grant.mapping == srsran_sch_mapping_type_A) {
|
||||
cfg->dmrs.additional_pos = pusch_hl_cfg->dmrs_typeA.additional_pos;
|
||||
cfg->dmrs.type = pusch_hl_cfg->dmrs_typeA.type;
|
||||
cfg->dmrs.length = pusch_hl_cfg->dmrs_typeA.length;
|
||||
cfg->dmrs.scrambling_id0_present = false;
|
||||
cfg->dmrs.scrambling_id1_present = false;
|
||||
} else {
|
||||
cfg->dmrs.additional_pos = pusch_hl_cfg->dmrs_typeB.additional_pos;
|
||||
cfg->dmrs.type = pusch_hl_cfg->dmrs_typeB.type;
|
||||
cfg->dmrs.length = pusch_hl_cfg->dmrs_typeB.length;
|
||||
cfg->dmrs.scrambling_id0_present = false;
|
||||
cfg->dmrs.scrambling_id1_present = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Set number of DMRS CDM groups without data
|
||||
if (pusch_grant->dci_format == srsran_dci_format_nr_0_0 || pusch_grant->dci_format == srsran_dci_format_nr_rar) {
|
||||
if (srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(cfg, pusch_grant) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error loading number of DMRS CDM groups");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
} else {
|
||||
ERROR("DCI format not implemented %s", srsran_dci_format_nr_string(pusch_grant->dci_format));
|
||||
int n = srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(pusch_hl_cfg, dci, cfg->grant.L);
|
||||
if (n < SRSRAN_SUCCESS) {
|
||||
ERROR("Error getting number of DMRS CDM groups without data");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
cfg->grant.nof_dmrs_cdm_groups_without_data = (uint32_t)n;
|
||||
|
||||
// Set DMRS power offset Table 6.2.2-1: The ratio of PUSCH EPRE to DM-RS EPRE
|
||||
if (ra_nr_dmrs_power_offset(pusch_grant) < SRSRAN_SUCCESS) {
|
||||
if (ra_nr_dmrs_power_offset(&cfg->grant) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error setting DMRS power offset");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
@ -787,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
|
||||
// ...
|
||||
|
||||
pusch_grant->nof_layers = 1;
|
||||
pusch_grant->dci_format = dci_ul->ctx.format;
|
||||
pusch_grant->rnti = dci_ul->ctx.rnti;
|
||||
pusch_grant->rnti_type = dci_ul->ctx.rnti_type;
|
||||
pusch_grant->tb[0].rv = dci_ul->rv;
|
||||
pusch_grant->tb[0].mcs = dci_ul->mcs;
|
||||
pusch_grant->tb[0].ndi = dci_ul->ndi;
|
||||
pusch_grant->nof_layers = 1;
|
||||
pusch_grant->dci_format = dci_ul->ctx.format;
|
||||
pusch_grant->rnti = dci_ul->ctx.rnti;
|
||||
pusch_grant->rnti_type = dci_ul->ctx.rnti_type;
|
||||
pusch_grant->tb[0].rv = dci_ul->rv;
|
||||
pusch_grant->tb[0].mcs = dci_ul->mcs;
|
||||
pusch_grant->tb[0].ndi = dci_ul->ndi;
|
||||
pusch_cfg->sch_cfg.mcs_table = pusch_hl_cfg->mcs_table;
|
||||
|
||||
// 5.1.6.2 DM-RS reception procedure
|
||||
if (ra_ul_dmrs(pusch_hl_cfg, pusch_grant, pusch_cfg) < SRSRAN_SUCCESS) {
|
||||
if (ra_ul_dmrs(pusch_hl_cfg, dci_ul, pusch_cfg) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error selecting DMRS configuration");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
|
|
@ -205,31 +205,95 @@ int srsran_ra_ul_nr_time(const srsran_sch_hl_cfg_nr_t* cfg,
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(const srsran_sch_cfg_nr_t* cfg,
|
||||
srsran_sch_grant_nr_t* grant)
|
||||
int srsran_ra_ul_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg,
|
||||
const srsran_dci_ul_nr_t* dci,
|
||||
srsran_dmrs_sch_len_t* dmrs_duration)
|
||||
{
|
||||
if (cfg == NULL || grant == NULL) {
|
||||
if (cfg == NULL || dci == NULL || dmrs_duration == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
/* According to TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure:
|
||||
* For PUSCH scheduled by DCI format 0_0 or by activation DCI format 0_0 with CRC scrambled by CS-RNTI, the UE
|
||||
* shall assume the number of DM-RS CDM groups without data is 1 which corresponds to CDM group 0 for the case of
|
||||
* PUSCH with allocation duration of 2 or less OFDM symbols with transform precoding disabled, the UE shall assume
|
||||
* that the number of DM-RS CDM groups without data is 3 which corresponds to CDM group {0,1,2} for the case of PUSCH
|
||||
* scheduled by activation DCI format 0_0 and the dmrs-Type in cg-DMRS-Configuration equal to 'type2' and the PUSCH
|
||||
* allocation duration being more than 2 OFDM symbols, and the UE shall assume that the number of DM-RS CDM groups
|
||||
* without data is 2 which corresponds to CDM group {0,1} for all other cases.
|
||||
*/
|
||||
if (grant->L <= 2 && !cfg->enable_transform_precoder) {
|
||||
grant->nof_dmrs_cdm_groups_without_data = 1;
|
||||
// } else if (grant->L > 2 && cfg->dmrs_cg.type == srsran_dmrs_sch_type_2){
|
||||
// grant->nof_dmrs_cdm_groups_without_data = 3;
|
||||
} else {
|
||||
grant->nof_dmrs_cdm_groups_without_data = 2;
|
||||
// Table 7.3.1.1.2-6: Antenna port(s), transform precoder is enabled, dmrs-Type=1, maxLength=1
|
||||
// Table 7.3.1.1.2-8: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=1
|
||||
// Table 7.3.1.1.2-9: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=2
|
||||
// Table 7.3.1.1.2-10: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=3
|
||||
// Table 7.3.1.1.2-11: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=4
|
||||
// Table 7.3.1.1.2-16: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=1
|
||||
// Table 7.3.1.1.2-17: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=2
|
||||
// Table 7.3.1.1.2-18: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=3
|
||||
// Table 7.3.1.1.2-19: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=4
|
||||
if (cfg->dmrs_max_length == srsran_dmrs_sch_len_1) {
|
||||
*dmrs_duration = srsran_dmrs_sch_len_1;
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
// Other tables are not implemented
|
||||
ERROR("Unhandled case");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg,
|
||||
const srsran_dci_ul_nr_t* dci,
|
||||
uint32_t L)
|
||||
{
|
||||
if (cfg == NULL || dci == NULL || L == 0) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
uint32_t rank = 1; // No other supported
|
||||
|
||||
// According to TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure:
|
||||
switch (dci->ctx.format) {
|
||||
case srsran_dci_format_nr_0_0:
|
||||
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
|
||||
|
@ -379,17 +443,39 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier,
|
|||
const srsran_dci_ul_nr_t* dci_ul,
|
||||
srsran_sch_grant_nr_t* grant)
|
||||
{
|
||||
if (cfg == NULL || grant == NULL || dci_ul == NULL) {
|
||||
if (carrier == NULL || cfg == NULL || grant == NULL || dci_ul == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
// RA scheme
|
||||
if (dci_ul->ctx.format == srsran_dci_format_nr_0_0 || dci_ul->ctx.format == srsran_dci_format_nr_rar) {
|
||||
// when the scheduling grant is received with DCI format 1_0 , then downlink resource allocation type 1 is used.
|
||||
// TS 38.213 PUSCH scheduled by RAR UL grant
|
||||
if (dci_ul->ctx.format == srsran_dci_format_nr_rar) {
|
||||
return ra_helper_freq_type1(carrier->nof_prb, dci_ul->freq_domain_assigment, grant);
|
||||
}
|
||||
|
||||
ERROR("Unhandled DCI Format %s", srsran_dci_format_nr_string(dci_ul->ctx.format));
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -655,6 +655,8 @@ static int sch_nr_decode(srsran_sch_nr_t* q,
|
|||
|
||||
input_ptr += E;
|
||||
}
|
||||
// Set average number of iterations
|
||||
res->avg_iter = (float)nof_iter_sum / (float)cfg.C;
|
||||
|
||||
// Set average number of iterations
|
||||
if (cfg.C > 0) {
|
||||
|
|
|
@ -64,11 +64,11 @@ static int test_52prb_base()
|
|||
cfg.enable_transform_precoding = false;
|
||||
cfg.dynamic_dual_harq_ack_codebook = false;
|
||||
cfg.pusch_tx_config_non_codebook = false;
|
||||
cfg.pusch_dmrs_type2 = false;
|
||||
cfg.pusch_dmrs_double = false;
|
||||
cfg.pusch_ptrs = false;
|
||||
cfg.pusch_dynamic_betas = false;
|
||||
cfg.pusch_alloc_type = srsran_resource_alloc_type1;
|
||||
cfg.pusch_dmrs_type = srsran_dmrs_sch_type_1;
|
||||
cfg.pusch_dmrs_max_len = srsran_dmrs_sch_len_1;
|
||||
|
||||
// DCI 1_1 parameters
|
||||
cfg.nof_dl_bwp = 0;
|
||||
|
@ -81,12 +81,12 @@ static int test_52prb_base()
|
|||
cfg.pdsch_rm_pattern2 = false;
|
||||
cfg.pdsch_2cw = false;
|
||||
cfg.multiple_scell = false;
|
||||
cfg.pdsch_dmrs_type2 = false;
|
||||
cfg.pdsch_dmrs_double = false;
|
||||
cfg.pdsch_tci = false;
|
||||
cfg.pdsch_cbg_flush = false;
|
||||
cfg.pdsch_dynamic_bundling = false;
|
||||
cfg.pdsch_alloc_type = srsran_resource_alloc_type1;
|
||||
cfg.pdsch_dmrs_type = srsran_dmrs_sch_type_1;
|
||||
cfg.pdsch_dmrs_max_len = srsran_dmrs_sch_len_1;
|
||||
|
||||
// Configure DCI
|
||||
srsran_dci_nr_t dci = {};
|
||||
|
|
|
@ -169,15 +169,11 @@ int main(int argc, char** argv)
|
|||
goto clean_exit;
|
||||
}
|
||||
|
||||
// Load number of DMRS CDM groups without data
|
||||
if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&pdsch_cfg.dmrs, &pdsch_cfg.grant) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error loading number of DMRS CDM groups without data");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers;
|
||||
pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0;
|
||||
pdsch_cfg.grant.rnti = rnti;
|
||||
// Set PDSCH grant without considering any procedure
|
||||
pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO
|
||||
pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers;
|
||||
pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0;
|
||||
pdsch_cfg.grant.rnti = rnti;
|
||||
|
||||
uint32_t n_prb_start = 1;
|
||||
uint32_t n_prb_end = carrier.nof_prb + 1;
|
||||
|
|
|
@ -175,15 +175,11 @@ int main(int argc, char** argv)
|
|||
goto clean_exit;
|
||||
}
|
||||
|
||||
// Load number of DMRS CDM groups without data
|
||||
if (srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(&pusch_cfg, &pusch_cfg.grant) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error loading number of DMRS CDM groups without data");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
pusch_cfg.grant.nof_layers = carrier.max_mimo_layers;
|
||||
pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0;
|
||||
pusch_cfg.grant.rnti = rnti;
|
||||
// Set PDSCH grant without considering any procedure
|
||||
pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO
|
||||
pusch_cfg.grant.nof_layers = carrier.max_mimo_layers;
|
||||
pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0;
|
||||
pusch_cfg.grant.rnti = rnti;
|
||||
|
||||
uint32_t n_prb_start = 1;
|
||||
uint32_t n_prb_end = carrier.nof_prb + 1;
|
||||
|
|
|
@ -188,12 +188,7 @@ int main(int argc, char** argv)
|
|||
for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) {
|
||||
pdsch_cfg.grant.prb_idx[n] = (n < n_prb);
|
||||
}
|
||||
|
||||
if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&pdsch_cfg.dmrs, &pdsch_cfg.grant) <
|
||||
SRSRAN_SUCCESS) {
|
||||
ERROR("Error calculating number of DMRS CDM groups");
|
||||
goto clean_exit;
|
||||
}
|
||||
pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO
|
||||
|
||||
srsran_sch_tb_t tb = {};
|
||||
tb.rv = rv;
|
||||
|
|
|
@ -847,3 +847,27 @@ uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* s
|
|||
|
||||
return len;
|
||||
}
|
||||
|
||||
int srsran_ue_dl_nr_csi_measure_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");
|
||||
|
||||
std::thread t([this]() {
|
||||
std::thread t([this, priority]() {
|
||||
running_flag = true;
|
||||
set_thread_priority(priority);
|
||||
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.
|
||||
std::call_once(start_once_flag, [this]() { create_worker(); });
|
||||
std::call_once(start_once_flag, [this, priority]() { create_worker(priority); });
|
||||
}
|
||||
|
||||
void backend_worker::do_work()
|
||||
{
|
||||
assert(running_flag && "Thread entry function called without running thread");
|
||||
|
||||
while (running_flag) {
|
||||
auto item = queue.timed_pop(sleep_period_ms);
|
||||
/// 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};
|
||||
|
||||
// Spin again when the timeout expires.
|
||||
while (running_flag) {
|
||||
auto item = queue.try_pop();
|
||||
|
||||
// Spin while there are no new entries to process.
|
||||
if (!item.first) {
|
||||
std::this_thread::sleep_for(sleep_period);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -122,7 +163,7 @@ void backend_worker::process_outstanding_entries()
|
|||
assert(!running_flag && "Cannot process outstanding entries while thread is running");
|
||||
|
||||
while (true) {
|
||||
auto item = queue.timed_pop(1);
|
||||
auto item = queue.try_pop();
|
||||
|
||||
// Check if the queue is empty.
|
||||
if (!item.first) {
|
||||
|
|
|
@ -35,11 +35,6 @@ namespace srslog {
|
|||
/// log entries from a work queue and dispatches them to the selected sinks.
|
||||
class backend_worker
|
||||
{
|
||||
/// This period defines the maximum time the worker will sleep while waiting
|
||||
/// for new entries. This is required to check the termination variable
|
||||
/// periodically.
|
||||
static constexpr unsigned sleep_period_ms = 500;
|
||||
|
||||
public:
|
||||
backend_worker(detail::work_queue<detail::log_entry>& queue, detail::dyn_arg_store_pool& arg_pool) :
|
||||
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
|
||||
/// secondary thread is ensured to be running. Calling this function more than
|
||||
/// once has no effect.
|
||||
void start();
|
||||
void start(backend_priority priority);
|
||||
|
||||
/// Stops the backend worker thread if it is running, otherwise the call has
|
||||
/// no effect. After returning from this function the secondary thread is
|
||||
|
@ -87,7 +82,7 @@ public:
|
|||
private:
|
||||
/// Creates the worker thread.
|
||||
/// NOTE: This function should be only called once.
|
||||
void create_worker();
|
||||
void create_worker(backend_priority priority);
|
||||
|
||||
/// Entry function used by the secondary thread.
|
||||
void do_work();
|
||||
|
@ -112,6 +107,9 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
/// Establishes the specified thread priority for the calling thread.
|
||||
void set_thread_priority(backend_priority priority) const;
|
||||
|
||||
private:
|
||||
detail::work_queue<detail::log_entry>& queue;
|
||||
detail::dyn_arg_store_pool& arg_pool;
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
log_backend_impl(const log_backend_impl& other) = delete;
|
||||
log_backend_impl& operator=(const log_backend_impl& other) = delete;
|
||||
|
||||
void start() override { worker.start(); }
|
||||
void start(backend_priority priority = backend_priority::normal) override { worker.start(priority); }
|
||||
|
||||
bool push(detail::log_entry&& entry) override
|
||||
{
|
||||
|
|
|
@ -190,9 +190,9 @@ bool srslog::install_custom_sink(const std::string& id, std::unique_ptr<sink> s)
|
|||
/// Framework configuration and control function implementations.
|
||||
///
|
||||
|
||||
void srslog::init()
|
||||
void srslog::init(backend_priority priority)
|
||||
{
|
||||
srslog_instance::get().get_backend().start();
|
||||
srslog_instance::get().get_backend().start(priority);
|
||||
}
|
||||
|
||||
void srslog::flush()
|
||||
|
|
|
@ -69,3 +69,7 @@ add_test(fsm_test fsm_test)
|
|||
add_executable(optional_test optional_test.cc)
|
||||
target_link_libraries(optional_test srsran_common)
|
||||
add_test(optional_test optional_test)
|
||||
|
||||
add_executable(cached_alloc_test cached_alloc_test.cc)
|
||||
target_link_libraries(cached_alloc_test srsran_common)
|
||||
add_test(cached_alloc_test cached_alloc_test)
|
||||
|
|
|
@ -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/.
|
||||
#
|
||||
|
||||
add_executable(srslog_frontend_latency benchmarks/frontend_latency.cpp)
|
||||
target_link_libraries(srslog_frontend_latency srslog)
|
||||
|
||||
add_executable(srslog_test srslog_test.cpp)
|
||||
target_link_libraries(srslog_test srslog)
|
||||
add_test(srslog_test srslog_test)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
public:
|
||||
void start() override {}
|
||||
void start(srslog::backend_priority priority) override {}
|
||||
|
||||
bool push(detail::log_entry&& entry) override
|
||||
{
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace {
|
|||
class backend_spy : public detail::log_backend
|
||||
{
|
||||
public:
|
||||
void start() override {}
|
||||
void start(srslog::backend_priority priority) override {}
|
||||
|
||||
bool push(detail::log_entry&& entry) override
|
||||
{
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#define TEST_DUMMIES
|
||||
|
||||
#include "srsran/srslog/detail/log_backend.h"
|
||||
#include "srsran/srslog/shared_types.h"
|
||||
#include "srsran/srslog/sink.h"
|
||||
|
||||
namespace test_dummies {
|
||||
|
@ -76,7 +77,7 @@ public:
|
|||
class backend_dummy : public srslog::detail::log_backend
|
||||
{
|
||||
public:
|
||||
void start() override {}
|
||||
void start(srslog::backend_priority priority) override {}
|
||||
|
||||
bool push(srslog::detail::log_entry&& entry) override { return true; }
|
||||
|
||||
|
|
|
@ -304,8 +304,11 @@ enable = false
|
|||
# tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR)
|
||||
# rrc_inactivity_timer Inactivity timeout used to remove UE context from RRC (in milliseconds).
|
||||
# max_prach_offset_us: Maximum allowed RACH offset (in us)
|
||||
# nof_prealloc_ues: Number of UE memory resources to preallocate during eNB initialization for faster UE creation (Default 8)
|
||||
# rlf_release_timer_ms: Time taken by eNB to release UE context after it detects an RLF
|
||||
# eea_pref_list: Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).
|
||||
# eia_pref_list: Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).
|
||||
# gtpu_tunnel_timeout: Time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU (0 for no timer).
|
||||
#
|
||||
#####################################################################
|
||||
[expert]
|
||||
|
@ -327,5 +330,8 @@ enable = false
|
|||
#rrc_inactivity_timer = 30000
|
||||
#max_nof_kos = 100
|
||||
#max_prach_offset_us = 30
|
||||
#nof_prealloc_ues = 8
|
||||
#rlf_release_timer_ms = 4000
|
||||
#eea_pref_list = EEA0, EEA2, EEA1
|
||||
#eia_pref_list = EIA2, EIA1, EIA0
|
||||
#gtpu_tunnel_timeout = 0
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
INCLUDES
|
||||
*******************************************************************************/
|
||||
|
||||
#include "srsran/adt/circular_map.h"
|
||||
#include "srsran/common/common_lte.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_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
|
||||
|
||||
#endif // SRSENB_COMMON_ENB_H
|
||||
|
|
|
@ -104,6 +104,8 @@ struct general_args_t {
|
|||
std::string eea_pref_list;
|
||||
uint32_t max_mac_dl_kos;
|
||||
uint32_t max_mac_ul_kos;
|
||||
uint32_t gtpu_indirect_tunnel_timeout;
|
||||
uint32_t rlf_release_timer_ms;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 srsenb
|
||||
|
|
|
@ -79,6 +79,7 @@ typedef struct {
|
|||
typedef struct {
|
||||
std::string type;
|
||||
uint32_t sync_queue_size; // Max allowed difference between PHY and Stack clocks (in TTI)
|
||||
uint32_t gtpu_indirect_tunnel_timeout_msec;
|
||||
mac_args_t mac;
|
||||
s1ap_args_t s1ap;
|
||||
pcap_args_t mac_pcap;
|
||||
|
|
|
@ -145,9 +145,9 @@ private:
|
|||
sched_interface::dl_pdu_mch_t mch = {};
|
||||
|
||||
/* Map of active UEs */
|
||||
srsran::static_circular_map<uint16_t, std::unique_ptr<ue>, 64> ue_db;
|
||||
std::map<uint16_t, std::unique_ptr<ue> > ues_to_rem;
|
||||
uint16_t last_rnti = 70;
|
||||
rnti_map_t<std::unique_ptr<ue> > ue_db;
|
||||
std::map<uint16_t, std::unique_ptr<ue> > ues_to_rem;
|
||||
uint16_t last_rnti = 70;
|
||||
|
||||
srsran::static_blocking_queue<std::unique_ptr<ue>, 32> ue_pool; ///< Pool of pre-allocated UE objects
|
||||
void prealloc_ue(uint32_t nof_ue);
|
||||
|
|
|
@ -24,11 +24,11 @@
|
|||
|
||||
#include "sched_grid.h"
|
||||
#include "sched_ue.h"
|
||||
#include "srsenb/hdr/common/common_enb.h"
|
||||
#include "srsran/interfaces/sched_interface.h"
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
|
@ -93,14 +93,14 @@ protected:
|
|||
bool is_generated(srsran::tti_point, uint32_t enb_cc_idx) const;
|
||||
// Helper methods
|
||||
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
|
||||
rrc_interface_mac* rrc = nullptr;
|
||||
sched_args_t sched_cfg = {};
|
||||
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
|
||||
std::vector<std::unique_ptr<carrier_sched> > carrier_schedulers;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "sched.h"
|
||||
#include "schedulers/sched_base.h"
|
||||
#include "srsran/adt/pool/cached_alloc.h"
|
||||
#include "srsran/srslog/srslog.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
@ -34,10 +35,10 @@ class ra_sched;
|
|||
class sched::carrier_sched
|
||||
{
|
||||
public:
|
||||
explicit carrier_sched(rrc_interface_mac* rrc_,
|
||||
std::map<uint16_t, std::unique_ptr<sched_ue> >* ue_db_,
|
||||
uint32_t enb_cc_idx_,
|
||||
sched_result_ringbuffer* sched_results_);
|
||||
explicit carrier_sched(rrc_interface_mac* rrc_,
|
||||
sched_ue_list* ue_db_,
|
||||
uint32_t enb_cc_idx_,
|
||||
sched_result_ringbuffer* sched_results_);
|
||||
~carrier_sched();
|
||||
void reset();
|
||||
void carrier_cfg(const sched_cell_params_t& sched_params_);
|
||||
|
@ -59,11 +60,11 @@ private:
|
|||
sf_sched* get_sf_sched(srsran::tti_point tti_rx);
|
||||
|
||||
// args
|
||||
const sched_cell_params_t* cc_cfg = nullptr;
|
||||
srslog::basic_logger& logger;
|
||||
rrc_interface_mac* rrc = nullptr;
|
||||
std::map<uint16_t, std::unique_ptr<sched_ue> >* ue_db = nullptr;
|
||||
const uint32_t enb_cc_idx;
|
||||
const sched_cell_params_t* cc_cfg = nullptr;
|
||||
srslog::basic_logger& logger;
|
||||
rrc_interface_mac* rrc = nullptr;
|
||||
sched_ue_list* ue_db = nullptr;
|
||||
const uint32_t enb_cc_idx;
|
||||
|
||||
// Subframe scheduling logic
|
||||
srsran::circular_array<sf_sched, TTIMOD_SZ> sf_scheds;
|
||||
|
@ -131,9 +132,9 @@ private:
|
|||
const sched_cell_params_t* cc_cfg = nullptr;
|
||||
sched_ue_list* ue_db = nullptr;
|
||||
|
||||
std::deque<pending_rar_t> pending_rars;
|
||||
uint32_t rar_aggr_level = 2;
|
||||
static const uint32_t PRACH_RAR_OFFSET = 3; // TS 36.321 Sec. 5.1.4
|
||||
srsran::deque<pending_rar_t> pending_rars;
|
||||
uint32_t rar_aggr_level = 2;
|
||||
static const uint32_t PRACH_RAR_OFFSET = 3; // TS 36.321 Sec. 5.1.4
|
||||
};
|
||||
|
||||
} // namespace srsenb
|
||||
|
|
|
@ -23,15 +23,14 @@
|
|||
#define SRSENB_SCHEDULER_UE_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_ue_cell.h"
|
||||
#include "sched_ue_ctrl/tpc.h"
|
||||
#include "srsenb/hdr/common/common_enb.h"
|
||||
#include "srsran/srslog/srslog.h"
|
||||
#include <bitset>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
|
@ -90,10 +89,12 @@ public:
|
|||
|
||||
uint32_t get_required_prb_ul(uint32_t enb_cc_idx, uint32_t req_bytes);
|
||||
|
||||
rbg_interval get_required_dl_rbgs(uint32_t enb_cc_idx);
|
||||
srsran::interval<uint32_t> get_requested_dl_bytes(uint32_t enb_cc_idx);
|
||||
uint32_t get_pending_dl_rlc_data() const;
|
||||
uint32_t get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs = -1) const;
|
||||
/// Get total pending bytes to be transmitted in DL.
|
||||
/// The amount of CEs to transmit depends on whether enb_cc_idx is UE's PCell
|
||||
uint32_t get_pending_dl_bytes(uint32_t enb_cc_idx);
|
||||
rbg_interval get_required_dl_rbgs(uint32_t enb_cc_idx);
|
||||
uint32_t get_pending_dl_rlc_data() const;
|
||||
uint32_t get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs = -1) const;
|
||||
|
||||
uint32_t get_pending_ul_data_total(tti_point tti_tx_ul, int this_enb_cc_idx);
|
||||
uint32_t get_pending_ul_new_data(tti_point tti_tx_ul, int this_enb_cc_idx);
|
||||
|
@ -147,6 +148,8 @@ public:
|
|||
bool pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_pdcch) const;
|
||||
|
||||
private:
|
||||
srsran::interval<uint32_t> get_requested_dl_bytes(uint32_t enb_cc_idx);
|
||||
|
||||
bool is_sr_triggered();
|
||||
|
||||
tbs_info allocate_new_dl_mac_pdu(sched_interface::dl_sched_data_t* data,
|
||||
|
@ -216,7 +219,7 @@ private:
|
|||
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
|
||||
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
#ifndef SRSRAN_SCHED_LCH_H
|
||||
#define SRSRAN_SCHED_LCH_H
|
||||
|
||||
#include "srsran/adt/pool/cached_alloc.h"
|
||||
#include "srsran/interfaces/sched_interface.h"
|
||||
#include "srsran/mac/pdu.h"
|
||||
#include "srsran/srslog/srslog.h"
|
||||
#include <deque>
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
|
@ -68,7 +68,7 @@ public:
|
|||
|
||||
// Control Element Command queue
|
||||
using ce_cmd = srsran::dl_sch_lcid;
|
||||
std::deque<ce_cmd> pending_ces;
|
||||
srsran::deque<ce_cmd> pending_ces;
|
||||
|
||||
private:
|
||||
struct ue_bearer_t {
|
||||
|
|
|
@ -119,14 +119,14 @@ public:
|
|||
* @remark See TS 36.213 Section 5.1.1
|
||||
* @return accumulated TPC value {-1, 0, 1, 3}
|
||||
*/
|
||||
uint8_t encode_pusch_tpc() { return enconde_tpc(PUSCH_CODE); }
|
||||
uint8_t encode_pusch_tpc() { return encode_tpc(PUSCH_CODE); }
|
||||
|
||||
/**
|
||||
* Called during DCI format1/2A/A encoding to set PUCCH TPC command
|
||||
* @remark See TS 36.213 Section 5.1.2
|
||||
* @return accumulated TPC value {-1, 0, 1, 3}
|
||||
*/
|
||||
uint8_t encode_pucch_tpc() { return enconde_tpc(PUCCH_CODE); }
|
||||
uint8_t encode_pucch_tpc() { return encode_tpc(PUCCH_CODE); }
|
||||
|
||||
uint32_t max_ul_prbs() const { return max_prbs_cached; }
|
||||
|
||||
|
@ -147,18 +147,14 @@ private:
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
uint8_t enconde_tpc(uint32_t cc)
|
||||
uint8_t encode_tpc(uint32_t cc)
|
||||
{
|
||||
float target_snr_dB = cc == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB;
|
||||
auto& ch_snr = snr_estim_list[cc];
|
||||
assert(ch_snr.pending_delta == 0); // ensure called once per {cc,tti}
|
||||
if (target_snr_dB < 0) {
|
||||
// undefined target SINR case. Increase Tx power once per PHR, considering the number of allocable PRBs remains
|
||||
// unchanged
|
||||
if (not ch_snr.phr_flag) {
|
||||
ch_snr.pending_delta = (max_prbs_cached == nof_prb) ? 1 : (last_phr < 0 ? -1 : 0);
|
||||
ch_snr.phr_flag = true;
|
||||
}
|
||||
// undefined target sinr case.
|
||||
ch_snr.pending_delta = 0;
|
||||
} else {
|
||||
// target SINR is finite and there is power headroom
|
||||
float diff = target_snr_dB - ch_snr.snr_avg.value();
|
||||
|
|
|
@ -73,7 +73,7 @@ private:
|
|||
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 {
|
||||
bool operator()(const ue_ctxt* lhs, const ue_ctxt* rhs) const;
|
||||
|
|
|
@ -45,6 +45,8 @@ class rlc_interface_rrc;
|
|||
class mac_interface_rrc;
|
||||
class phy_interface_rrc_lte;
|
||||
|
||||
class paging_manager;
|
||||
|
||||
static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE",
|
||||
"WAIT FOR CON SETUP COMPLETE",
|
||||
"WAIT FOR SECURITY MODE COMPLETE",
|
||||
|
@ -84,7 +86,7 @@ public:
|
|||
uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override;
|
||||
|
||||
// rrc_interface_rlc
|
||||
void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) override;
|
||||
void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t buffer_size) override;
|
||||
void max_retx_attempted(uint16_t rnti) override;
|
||||
|
||||
// rrc_interface_s1ap
|
||||
|
@ -109,7 +111,7 @@ public:
|
|||
asn1::s1ap::cause_c& cause) override;
|
||||
bool release_erabs(uint32_t rnti) override;
|
||||
int release_erab(uint16_t rnti, uint16_t erab_id) override;
|
||||
void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& UEPagingID) override;
|
||||
void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id) override;
|
||||
void ho_preparation_complete(uint16_t rnti,
|
||||
rrc::ho_prep_result result,
|
||||
const asn1::s1ap::ho_cmd_s& msg,
|
||||
|
@ -172,9 +174,9 @@ private:
|
|||
std::unique_ptr<enb_cell_common_list> cell_common_list;
|
||||
|
||||
// state
|
||||
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<uint32_t, asn1::rrc::paging_record_s> pending_paging;
|
||||
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::unique_ptr<paging_manager> pending_paging;
|
||||
|
||||
void process_release_complete(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 send_rrc_connection_reject(uint16_t rnti);
|
||||
|
||||
uint32_t paging_tti = INVALID_TTI;
|
||||
srsran::byte_buffer_t byte_buf_paging;
|
||||
const static int mcch_payload_len = 3000;
|
||||
int current_mcch_length = 0;
|
||||
uint8_t mcch_payload_buffer[mcch_payload_len] = {};
|
||||
const static int mcch_payload_len = 3000;
|
||||
int current_mcch_length = 0;
|
||||
uint8_t mcch_payload_buffer[mcch_payload_len] = {};
|
||||
typedef struct {
|
||||
uint16_t rnti;
|
||||
uint32_t lcid;
|
||||
|
@ -199,13 +199,13 @@ private:
|
|||
srsran::unique_byte_buffer_t pdu;
|
||||
} rrc_pdu;
|
||||
|
||||
const static uint32_t LCID_EXIT = 0xffff0000;
|
||||
const static uint32_t LCID_REM_USER = 0xffff0001;
|
||||
const static uint32_t LCID_REL_USER = 0xffff0002;
|
||||
const static uint32_t LCID_ACT_USER = 0xffff0004;
|
||||
const static uint32_t LCID_RTX_USER = 0xffff0005;
|
||||
const static uint32_t LCID_RADLINK_DL = 0xffff0006;
|
||||
const static uint32_t LCID_RADLINK_UL = 0xffff0007;
|
||||
const static uint32_t LCID_EXIT = 0xffff0000;
|
||||
const static uint32_t LCID_REM_USER = 0xffff0001;
|
||||
const static uint32_t LCID_REL_USER = 0xffff0002;
|
||||
const static uint32_t LCID_ACT_USER = 0xffff0004;
|
||||
const static uint32_t LCID_RTX_USER = 0xffff0005;
|
||||
const static uint32_t LCID_RADLINK_DL = 0xffff0006;
|
||||
const static uint32_t LCID_RADLINK_UL = 0xffff0007;
|
||||
|
||||
bool running = false;
|
||||
srsran::dyn_blocking_queue<rrc_pdu> rx_pdu_queue;
|
||||
|
@ -217,8 +217,6 @@ private:
|
|||
asn1::rrc::sib_type7_s sib7;
|
||||
|
||||
void rem_user_thread(uint16_t rnti);
|
||||
|
||||
std::mutex paging_mutex;
|
||||
};
|
||||
|
||||
} // namespace srsenb
|
||||
|
|
|
@ -88,6 +88,9 @@ public:
|
|||
|
||||
bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_, gtpu_interface_rrc* gtpu_);
|
||||
|
||||
/// Called after RRCReestablishmentComplete, to add E-RABs of old rnti
|
||||
void reestablish_bearers(bearer_cfg_handler&& old_rnti_bearers);
|
||||
|
||||
int add_erab(uint8_t erab_id,
|
||||
const asn1::s1ap::erab_level_qos_params_s& qos,
|
||||
const asn1::bounded_bitstring<1, 160, true, true>& addr,
|
||||
|
|
|
@ -70,6 +70,7 @@ struct rrc_cfg_t {
|
|||
cell_list_t cell_list_nr;
|
||||
uint32_t max_mac_dl_kos;
|
||||
uint32_t max_mac_ul_kos;
|
||||
uint32_t rlf_release_timer_ms;
|
||||
};
|
||||
|
||||
constexpr uint32_t UE_PCELL_CC_IDX = 0;
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "srsran/common/threads.h"
|
||||
#include "srsran/common/timeout.h"
|
||||
#include "srsran/interfaces/gnb_interfaces.h"
|
||||
#include "srsran/interfaces/gnb_ngap_interfaces.h"
|
||||
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
|
||||
#include <map>
|
||||
#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);
|
||||
void set_activity_timeout(const activity_timeout_type_t type);
|
||||
void set_rlf_timeout();
|
||||
void set_activity();
|
||||
void start_rlf_timer();
|
||||
void stop_rlf_timer();
|
||||
void set_radiolink_dl_state(bool crc_res);
|
||||
void set_radiolink_ul_state(bool crc_res);
|
||||
void activity_timer_expired(const activity_timeout_type_t type);
|
||||
|
@ -167,8 +168,8 @@ public:
|
|||
|
||||
private:
|
||||
// args
|
||||
srsran::timer_handler::unique_timer activity_timer;
|
||||
srsran::timer_handler::unique_timer rlf_timer;
|
||||
srsran::unique_timer activity_timer;
|
||||
srsran::unique_timer rlf_release_timer;
|
||||
|
||||
/// cached ASN1 fields for RRC config update checking, and ease of context transfer during HO
|
||||
ue_var_cfg_t current_ue_cfg;
|
||||
|
|
|
@ -45,7 +45,6 @@ struct gtpu_header_t;
|
|||
namespace srsenb {
|
||||
|
||||
class pdcp_interface_gtpu;
|
||||
class stack_interface_gtpu_lte;
|
||||
|
||||
class gtpu_tunnel_manager
|
||||
{
|
||||
|
@ -99,7 +98,7 @@ public:
|
|||
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);
|
||||
void init(pdcp_interface_gtpu* pdcp_);
|
||||
void init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_);
|
||||
|
||||
bool has_teid(uint32_t teid) const { return tunnels.contains(teid); }
|
||||
const tunnel* find_tunnel(uint32_t teid);
|
||||
|
@ -125,11 +124,12 @@ private:
|
|||
using tunnel_ctxt_it = typename tunnel_list_t::iterator;
|
||||
|
||||
srsran::task_sched_handle task_sched;
|
||||
pdcp_interface_gtpu* pdcp = nullptr;
|
||||
const gtpu_args_t* gtpu_args = nullptr;
|
||||
pdcp_interface_gtpu* pdcp = nullptr;
|
||||
srslog::basic_logger& logger;
|
||||
|
||||
srsran::static_circular_map<uint16_t, ue_lcid_tunnel_list, SRSENB_MAX_UES> ue_teidin_db;
|
||||
tunnel_list_t tunnels;
|
||||
rnti_map_t<ue_lcid_tunnel_list> ue_teidin_db;
|
||||
tunnel_list_t tunnels;
|
||||
};
|
||||
|
||||
using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state;
|
||||
|
@ -143,12 +143,7 @@ public:
|
|||
srsran::socket_manager_itf* rx_socket_handler_);
|
||||
~gtpu();
|
||||
|
||||
int init(std::string gtp_bind_addr_,
|
||||
std::string mme_addr_,
|
||||
std::string m1u_multiaddr_,
|
||||
std::string m1u_if_addr_,
|
||||
pdcp_interface_gtpu* pdcp_,
|
||||
bool enable_mbsfn = false);
|
||||
int init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_);
|
||||
void stop();
|
||||
|
||||
// gtpu_interface_rrc
|
||||
|
@ -177,7 +172,7 @@ private:
|
|||
srsran::socket_manager_itf* rx_socket_handler = nullptr;
|
||||
srsran::task_queue_handle gtpu_queue;
|
||||
|
||||
bool enable_mbsfn = false;
|
||||
gtpu_args_t args;
|
||||
std::string gtp_bind_addr;
|
||||
std::string mme_addr;
|
||||
srsenb::pdcp_interface_gtpu* pdcp = nullptr;
|
||||
|
|
|
@ -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
|
||||
int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);
|
||||
void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);
|
||||
void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size);
|
||||
|
||||
private:
|
||||
class user_interface : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc
|
||||
|
|
|
@ -65,6 +65,7 @@ cell_list =
|
|||
// target_pusch_sinr = -1;
|
||||
// target_pucch_sinr = -1;
|
||||
// allowed_meas_bw = 6;
|
||||
// t304 = 2000; // in msec. possible values: 50, 100, 150, 200, 500, 1000, 2000
|
||||
|
||||
// CA cells
|
||||
scell_list = (
|
||||
|
|
|
@ -53,6 +53,17 @@ using namespace asn1::rrc;
|
|||
|
||||
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)
|
||||
{
|
||||
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));
|
||||
parse_default_field(cell_cfg.meas_cfg.allowed_meas_bw, cellroot, "allowed_meas_bw", 6u);
|
||||
srsran_assert(srsran::is_lte_cell_nof_prb(cell_cfg.meas_cfg.allowed_meas_bw), "Invalid measurement Bandwidth");
|
||||
HANDLEPARSERCODE(asn1_parsers::default_number_to_enum(
|
||||
cell_cfg.t304, cellroot, "t304", asn1::rrc::mob_ctrl_info_s::t304_opts::ms2000));
|
||||
|
||||
if (cellroot.exists("ho_active") and cellroot["ho_active"]) {
|
||||
HANDLEPARSERCODE(parse_meas_cell_list(&cell_cfg.meas_cfg, cellroot["meas_cell_list"]));
|
||||
|
@ -914,9 +927,9 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_
|
|||
{
|
||||
// Sanity checks
|
||||
ASSERT_VALID_CFG(not rrc_cfg_->cell_list.empty(), "No cell specified in rr.conf.");
|
||||
ASSERT_VALID_CFG(args_->stack.mac.max_nof_ues <= SRSENB_MAX_UES and args_->stack.mac.max_nof_ues > 0,
|
||||
"mac.max_nof_ues=%d must be within [0, %d]",
|
||||
args_->stack.mac.max_nof_ues,
|
||||
ASSERT_VALID_CFG(args_->stack.mac.nof_prealloc_ues <= SRSENB_MAX_UES,
|
||||
"mac.nof_prealloc_ues=%d must be within [0, %d]",
|
||||
args_->stack.mac.nof_prealloc_ues,
|
||||
SRSENB_MAX_UES);
|
||||
|
||||
// Check for a forced DL EARFCN or frequency (only valid for a single cell config (Xico's favorite feature))
|
||||
|
@ -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;
|
||||
|
||||
// Set max number of KOs
|
||||
rrc_cfg_->max_mac_dl_kos = args_->general.max_mac_dl_kos;
|
||||
rrc_cfg_->max_mac_ul_kos = args_->general.max_mac_ul_kos;
|
||||
rrc_cfg_->max_mac_dl_kos = args_->general.max_mac_dl_kos;
|
||||
rrc_cfg_->max_mac_ul_kos = args_->general.max_mac_ul_kos;
|
||||
rrc_cfg_->rlf_release_timer_ms = args_->general.rlf_release_timer_ms;
|
||||
|
||||
// Set sync queue capacity to 1 for ZMQ
|
||||
if (args_->rf.device_name == "zmq") {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.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.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.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_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
|
||||
|
@ -542,6 +545,10 @@ int main(int argc, char* argv[])
|
|||
event_logger::configure(json_channel);
|
||||
}
|
||||
|
||||
if (mlockall((uint32_t)MCL_CURRENT | (uint32_t)MCL_FUTURE) == -1) {
|
||||
srsran::console("Failed to `mlockall`: {}", errno);
|
||||
}
|
||||
|
||||
// Create eNB
|
||||
unique_ptr<srsenb::enb> enb{new srsenb::enb(srslog::get_default_sink())};
|
||||
if (enb->init(args) != SRSRAN_SUCCESS) {
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <string.h>
|
||||
#include <string>
|
||||
#include <strings.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "srsenb/hdr/phy/phy.h"
|
||||
|
@ -114,8 +113,6 @@ int phy::init(const phy_args_t& args,
|
|||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
mlockall((uint32_t)MCL_CURRENT | (uint32_t)MCL_FUTURE);
|
||||
|
||||
// Add PHY lib log.
|
||||
srslog::basic_levels log_lvl = srslog::str_to_basic_level(args.log.phy_lib_level);
|
||||
|
||||
|
|
|
@ -82,9 +82,9 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_)
|
|||
rrc_cfg = rrc_cfg_;
|
||||
|
||||
// Init RNTI and bearer memory pools
|
||||
reserve_rnti_memblocks(args.mac.max_nof_ues);
|
||||
reserve_rnti_memblocks(args.mac.nof_prealloc_ues);
|
||||
uint32_t min_nof_bearers_per_ue = 4;
|
||||
reserve_rlc_memblocks(args.mac.max_nof_ues * min_nof_bearers_per_ue);
|
||||
reserve_rlc_memblocks(args.mac.nof_prealloc_ues * min_nof_bearers_per_ue);
|
||||
|
||||
// setup logging for each layer
|
||||
mac_logger.set_level(srslog::str_to_basic_level(args.log.mac_level));
|
||||
|
@ -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");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
if (gtpu.init(args.s1ap.gtp_bind_addr,
|
||||
args.s1ap.mme_addr,
|
||||
args.embms.m1u_multiaddr,
|
||||
args.embms.m1u_if_addr,
|
||||
&pdcp,
|
||||
args.embms.enable)) {
|
||||
|
||||
gtpu_args_t gtpu_args;
|
||||
gtpu_args.embms_enable = args.embms.enable;
|
||||
gtpu_args.embms_m1u_multiaddr = args.embms.m1u_multiaddr;
|
||||
gtpu_args.embms_m1u_if_addr = args.embms.m1u_if_addr;
|
||||
gtpu_args.mme_addr = args.s1ap.mme_addr;
|
||||
gtpu_args.gtp_bind_addr = args.s1ap.gtp_bind_addr;
|
||||
gtpu_args.indirect_tunnel_timeout_msec = args.gtpu_indirect_tunnel_timeout_msec;
|
||||
if (gtpu.init(gtpu_args, &pdcp) != SRSRAN_SUCCESS) {
|
||||
stack_logger.error("Couldn't initialize GTPU");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ bool mac::init(const mac_args_t& args_,
|
|||
};
|
||||
auto recycle_softbuffers = [](ue_cc_softbuffers& softbuffers) { softbuffers.clear(); };
|
||||
softbuffer_pool.reset(new srsran::background_obj_pool<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
|
||||
prealloc_ue(10);
|
||||
|
@ -478,8 +478,8 @@ uint16_t mac::allocate_ue()
|
|||
logger.info("RACH ignored as eNB is being shutdown");
|
||||
return SRSRAN_INVALID_RNTI;
|
||||
}
|
||||
if (ue_db.size() >= args.max_nof_ues) {
|
||||
logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", args.max_nof_ues);
|
||||
if (ue_db.size() >= SRSENB_MAX_UES) {
|
||||
logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", SRSENB_MAX_UES);
|
||||
return SRSRAN_INVALID_RNTI;
|
||||
}
|
||||
auto ret = ue_db.insert(rnti, std::move(ue_ptr));
|
||||
|
@ -736,7 +736,7 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list)
|
|||
} else {
|
||||
dl_sched_res->pdsch[n].softbuffer_tx[0] = &common_buffers[enb_cc_idx].pcch_softbuffer_tx;
|
||||
dl_sched_res->pdsch[n].data[0] = common_buffers[enb_cc_idx].pcch_payload_buffer;
|
||||
rlc_h->read_pdu_pcch(common_buffers[enb_cc_idx].pcch_payload_buffer, pcch_payload_buffer_len);
|
||||
rrc_h->read_pdu_pcch(tti_tx_dl, common_buffers[enb_cc_idx].pcch_payload_buffer, pcch_payload_buffer_len);
|
||||
|
||||
if (pcap) {
|
||||
pcap->write_dl_pch(dl_sched_res->pdsch[n].data[0], sched_result.bc[i].tbs, true, tti_tx_dl, enb_cc_idx);
|
||||
|
|
|
@ -116,14 +116,14 @@ int sched::ue_cfg(uint16_t rnti, const sched_interface::ue_cfg_t& ue_cfg)
|
|||
// Add new user case
|
||||
std::unique_ptr<sched_ue> ue{new sched_ue(rnti, sched_cell_params, ue_cfg)};
|
||||
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;
|
||||
}
|
||||
|
||||
int sched::ue_rem(uint16_t rnti)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sched_mutex);
|
||||
if (ue_db.count(rnti) > 0) {
|
||||
if (ue_db.contains(rnti)) {
|
||||
ue_db.erase(rnti);
|
||||
} else {
|
||||
Error("User rnti=0x%x not found", rnti);
|
||||
|
@ -134,7 +134,8 @@ int sched::ue_rem(uint16_t rnti)
|
|||
|
||||
bool sched::ue_exists(uint16_t rnti)
|
||||
{
|
||||
return ue_db_access_locked(rnti, [](sched_ue& ue) {}) >= 0;
|
||||
return ue_db_access_locked(
|
||||
rnti, [](sched_ue& ue) {}, nullptr, false) >= 0;
|
||||
}
|
||||
|
||||
void sched::phy_config_enabled(uint16_t rnti, bool enabled)
|
||||
|
@ -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
|
||||
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);
|
||||
auto it = ue_db.find(rnti);
|
||||
if (it != ue_db.end()) {
|
||||
f(*it->second);
|
||||
} else {
|
||||
if (func_name != nullptr) {
|
||||
Error("User rnti=0x%x not found. Failed to call %s.", rnti, func_name);
|
||||
} else {
|
||||
Error("User rnti=0x%x not found.", rnti);
|
||||
if (log_fail) {
|
||||
if (func_name != nullptr) {
|
||||
Error("SCHED: User rnti=0x%x not found. Failed to call %s.", rnti, func_name);
|
||||
} else {
|
||||
Error("SCHED: User rnti=0x%x not found.", rnti);
|
||||
}
|
||||
}
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
|
|
@ -671,7 +671,7 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t&
|
|||
continue;
|
||||
}
|
||||
sched_ue* user = ue_it->second.get();
|
||||
uint32_t data_before = user->get_requested_dl_bytes(cc_cfg->enb_cc_idx).stop();
|
||||
uint32_t data_before = user->get_pending_dl_bytes(cc_cfg->enb_cc_idx);
|
||||
const dl_harq_proc& dl_harq = user->get_dl_harq(data_alloc.pid, cc_cfg->enb_cc_idx);
|
||||
bool is_newtx = dl_harq.is_empty();
|
||||
|
||||
|
@ -687,7 +687,7 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t&
|
|||
data_alloc.pid,
|
||||
data_alloc.user_mask,
|
||||
tbs,
|
||||
user->get_requested_dl_bytes(cc_cfg->enb_cc_idx).stop());
|
||||
user->get_pending_dl_bytes(cc_cfg->enb_cc_idx));
|
||||
logger.warning("%s", srsran::to_c_str(str_buffer));
|
||||
continue;
|
||||
}
|
||||
|
@ -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),
|
||||
tbs,
|
||||
data_before,
|
||||
user->get_requested_dl_bytes(cc_cfg->enb_cc_idx).stop(),
|
||||
user->get_pending_dl_bytes(cc_cfg->enb_cc_idx),
|
||||
get_tti_tx_dl());
|
||||
logger.info("%s", srsran::to_c_str(str_buffer));
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
cells[enb_cc_idx].tpc_fsm.set_snr(snr, ul_ch_code);
|
||||
cells[enb_cc_idx].ul_cqi = srsran_cqi_from_snr(snr);
|
||||
cells[enb_cc_idx].ul_cqi_tti_rx = tti_rx;
|
||||
if (ul_ch_code == tpc::PUSCH_CODE) {
|
||||
cells[enb_cc_idx].ul_cqi = srsran_cqi_from_snr(snr);
|
||||
cells[enb_cc_idx].ul_cqi_tti_rx = tti_rx;
|
||||
}
|
||||
} else {
|
||||
logger.warning("Received SNR info for invalid cell index %d", enb_cc_idx);
|
||||
}
|
||||
|
@ -776,6 +778,11 @@ rbg_interval sched_ue::get_required_dl_rbgs(uint32_t enb_cc_idx)
|
|||
return {min_pending_rbg, max_pending_rbg};
|
||||
}
|
||||
|
||||
uint32_t sched_ue::get_pending_dl_bytes(uint32_t enb_cc_idx)
|
||||
{
|
||||
return get_requested_dl_bytes(enb_cc_idx).stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the range (min,max) of possible MAC PDU sizes.
|
||||
* - the lower boundary value is set based on the following conditions:
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
const static int MIN_ALLOC_BYTES = 10;
|
||||
if (req_bytes == 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
|
||||
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());
|
||||
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; });
|
||||
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()) {
|
||||
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);
|
||||
}
|
||||
|
||||
std::vector<ue_ctxt *> dl_storage;
|
||||
std::vector<ue_ctxt*> dl_storage;
|
||||
dl_storage.reserve(SRSENB_MAX_UES);
|
||||
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_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()};
|
||||
// remove deleted users from history
|
||||
for (auto it = ue_history_db.begin(); it != ue_history_db.end();) {
|
||||
if (not ue_db.count(it->first)) {
|
||||
if (not ue_db.contains(it->first)) {
|
||||
it = ue_history_db.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
|
|
|
@ -477,7 +477,11 @@ bool ue::process_ce(srsran::sch_subh* subh)
|
|||
rrc->upd_user(rnti, old_rnti);
|
||||
rnti = old_rnti;
|
||||
} else {
|
||||
logger.error("Updating user C-RNTI: rnti=0x%x already released", old_rnti);
|
||||
logger.warning("Updating user C-RNTI: rnti=0x%x already released.", old_rnti);
|
||||
// Disable scheduling for all bearers. The new rnti will be removed on msg3 timer expiry in the RRC
|
||||
for (uint32_t lcid = 0; lcid < sched_interface::MAX_LC; ++lcid) {
|
||||
sched->bearer_ue_rem(rnti, lcid);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case srsran::ul_sch_lcid::TRUNC_BSR:
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "srsenb/hdr/stack/rrc/rrc.h"
|
||||
#include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h"
|
||||
#include "srsenb/hdr/stack/rrc/rrc_mobility.h"
|
||||
#include "srsenb/hdr/stack/rrc/rrc_paging.h"
|
||||
#include "srsran/asn1/asn1_utils.h"
|
||||
#include "srsran/asn1/rrc_utils.h"
|
||||
#include "srsran/common/bcd_helpers.h"
|
||||
|
@ -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("Max consecutive MAC KOs: %d", cfg.max_mac_dl_kos);
|
||||
|
||||
pending_paging.reset(new paging_manager(cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.default_paging_cycle.to_number(),
|
||||
cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.nb.to_number()));
|
||||
|
||||
running = true;
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
|
@ -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)
|
||||
{
|
||||
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) {
|
||||
paging_elem.ue_id.set_imsi();
|
||||
paging_elem.ue_id.imsi().resize(ue_paging_id.imsi().size());
|
||||
memcpy(paging_elem.ue_id.imsi().data(), ue_paging_id.imsi().data(), ue_paging_id.imsi().size());
|
||||
srsran::console("Warning IMSI paging not tested\n");
|
||||
pending_paging->add_imsi_paging(ueid, ue_paging_id.imsi());
|
||||
} else {
|
||||
paging_elem.ue_id.set_s_tmsi();
|
||||
paging_elem.ue_id.s_tmsi().mmec.from_number(ue_paging_id.s_tmsi().mmec[0]);
|
||||
uint32_t m_tmsi = 0;
|
||||
uint32_t nof_octets = ue_paging_id.s_tmsi().m_tmsi.size();
|
||||
for (uint32_t i = 0; i < nof_octets; i++) {
|
||||
m_tmsi |= ue_paging_id.s_tmsi().m_tmsi[i] << (8u * (nof_octets - i - 1u));
|
||||
}
|
||||
paging_elem.ue_id.s_tmsi().m_tmsi.from_number(m_tmsi);
|
||||
pending_paging->add_tmsi_paging(ueid, ue_paging_id.s_tmsi().mmec[0], ue_paging_id.s_tmsi().m_tmsi);
|
||||
}
|
||||
paging_elem.cn_domain = paging_record_s::cn_domain_e_::ps;
|
||||
|
||||
pending_paging.insert(std::make_pair(ueid, paging_elem));
|
||||
}
|
||||
|
||||
// Described in Section 7 of 36.304
|
||||
bool rrc::is_paging_opportunity(uint32_t tti, uint32_t* payload_len)
|
||||
{
|
||||
constexpr static int sf_pattern[4][4] = {{9, 4, -1, 0}, {-1, 9, -1, 4}, {-1, -1, -1, 5}, {-1, -1, -1, 9}};
|
||||
|
||||
if (tti == paging_tti) {
|
||||
*payload_len = byte_buf_paging.N_bytes;
|
||||
logger.debug("Sending paging to extra carriers. Payload len=%d, TTI=%d", *payload_len, tti);
|
||||
return true;
|
||||
} else {
|
||||
paging_tti = INVALID_TTI;
|
||||
}
|
||||
|
||||
if (pending_paging.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
asn1::rrc::pcch_msg_s pcch_msg;
|
||||
pcch_msg.msg.set_c1();
|
||||
paging_s* paging_rec = &pcch_msg.msg.c1().paging();
|
||||
|
||||
// Default paging cycle, should get DRX from user
|
||||
uint32_t T = cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.default_paging_cycle.to_number();
|
||||
uint32_t Nb = T * cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.nb.to_number();
|
||||
|
||||
uint32_t N = T < Nb ? T : Nb;
|
||||
uint32_t Ns = Nb / T > 1 ? Nb / T : 1;
|
||||
uint32_t sfn = tti / 10;
|
||||
|
||||
std::vector<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) {
|
||||
byte_buf_paging.clear();
|
||||
asn1::bit_ref bref(byte_buf_paging.msg, byte_buf_paging.get_tailroom());
|
||||
if (pcch_msg.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) {
|
||||
logger.error("Failed to pack PCCH");
|
||||
return false;
|
||||
}
|
||||
byte_buf_paging.N_bytes = (uint32_t)bref.distance_bytes();
|
||||
uint32_t N_bits = (uint32_t)bref.distance();
|
||||
|
||||
if (payload_len) {
|
||||
*payload_len = byte_buf_paging.N_bytes;
|
||||
}
|
||||
logger.info("Assembling PCCH payload with %d UE identities, payload_len=%d bytes, nbits=%d",
|
||||
paging_rec->paging_record_list.size(),
|
||||
byte_buf_paging.N_bytes,
|
||||
N_bits);
|
||||
log_rrc_message("PCCH-Message", Tx, &byte_buf_paging, pcch_msg, pcch_msg.msg.c1().type().to_string());
|
||||
|
||||
paging_tti = tti; // Store paging tti for other carriers
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
*payload_len = pending_paging->pending_pcch_bytes(tti_point(tti));
|
||||
return *payload_len > 0;
|
||||
}
|
||||
|
||||
void rrc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size)
|
||||
void rrc::read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t buffer_size)
|
||||
{
|
||||
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);
|
||||
}
|
||||
auto read_func = [this, payload, buffer_size](srsran::const_byte_span pdu, const pcch_msg_s& msg, bool first_tx) {
|
||||
// copy PCCH pdu to buffer
|
||||
if (pdu.size() > buffer_size) {
|
||||
logger.warning("byte buffer with size=%zd is too small to fit pcch msg with size=%zd", buffer_size, pdu.size());
|
||||
return false;
|
||||
}
|
||||
std::copy(pdu.begin(), pdu.end(), payload);
|
||||
|
||||
if (first_tx) {
|
||||
logger.info("Assembling PCCH payload with %d UE identities, payload_len=%d bytes",
|
||||
msg.msg.c1().paging().paging_record_list.size(),
|
||||
pdu.size());
|
||||
log_rrc_message("PCCH-Message", Tx, pdu, msg, msg.msg.c1().type().to_string());
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
pending_paging->read_pdu_pcch(tti_point(tti_tx_dl), read_func);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -833,7 +744,7 @@ uint32_t rrc::generate_sibs()
|
|||
return SRSRAN_ERROR;
|
||||
}
|
||||
asn1::bit_ref bref(sib_buffer->msg, sib_buffer->get_tailroom());
|
||||
if (msg[msg_index].pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) {
|
||||
if (msg[msg_index].pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||
logger.error("Failed to pack SIB message %d", msg_index);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
@ -987,7 +898,10 @@ int rrc::pack_mcch()
|
|||
|
||||
const int rlc_header_len = 1;
|
||||
asn1::bit_ref bref(&mcch_payload_buffer[rlc_header_len], sizeof(mcch_payload_buffer) - rlc_header_len);
|
||||
mcch.pack(bref);
|
||||
if (mcch.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||
logger.error("Failed to pack MCCH message");
|
||||
}
|
||||
|
||||
current_mcch_length = bref.distance_bytes(&mcch_payload_buffer[1]);
|
||||
current_mcch_length = current_mcch_length + rlc_header_len;
|
||||
return current_mcch_length;
|
||||
|
|
|
@ -114,6 +114,8 @@ bool security_cfg_handler::set_security_capabilities(const asn1::s1ap::ue_securi
|
|||
case srsran::INTEGRITY_ALGORITHM_ID_EIA0:
|
||||
// Null integrity is not supported
|
||||
logger.info("Skipping EIA0 as RRC integrity algorithm. Null integrity is not supported.");
|
||||
sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_EIA0;
|
||||
integ_algo_found = true;
|
||||
break;
|
||||
case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1:
|
||||
// “first bit” – 128-EIA1,
|
||||
|
@ -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"))
|
||||
{}
|
||||
|
||||
void bearer_cfg_handler::reestablish_bearers(bearer_cfg_handler&& old_rnti_bearers)
|
||||
{
|
||||
erab_info_list = std::move(old_rnti_bearers.erab_info_list);
|
||||
erabs = std::move(old_rnti_bearers.erabs);
|
||||
current_drbs = std::move(old_rnti_bearers.current_drbs);
|
||||
old_rnti_bearers.current_drbs.clear();
|
||||
}
|
||||
|
||||
int bearer_cfg_handler::add_erab(uint8_t erab_id,
|
||||
const asn1::s1ap::erab_level_qos_params_s& qos,
|
||||
const asn1::bounded_bitstring<1, 160, true, true>& addr,
|
||||
|
@ -311,6 +321,8 @@ int bearer_cfg_handler::release_erab(uint8_t erab_id)
|
|||
|
||||
srsran::rem_rrc_obj_id(current_drbs, drb_id);
|
||||
|
||||
rem_gtpu_bearer(erab_id);
|
||||
|
||||
erabs.erase(it);
|
||||
erab_info_list.erase(erab_id);
|
||||
|
||||
|
@ -319,8 +331,6 @@ int bearer_cfg_handler::release_erab(uint8_t erab_id)
|
|||
|
||||
void bearer_cfg_handler::release_erabs()
|
||||
{
|
||||
// TODO: notify GTPU layer for each ERAB
|
||||
erabs.clear();
|
||||
while (not erabs.empty()) {
|
||||
release_erab(erabs.begin()->first);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
auto it = erabs.find(erab_id);
|
||||
if (it != erabs.end()) {
|
||||
// Map e.g. E-RAB 5 to LCID 3 (==DRB1)
|
||||
gtpu->rem_bearer(rnti, erab_id - 2);
|
||||
} else {
|
||||
logger->error("Removing erab_id=%d to GTPU\n", erab_id);
|
||||
}
|
||||
gtpu->rem_bearer(rnti, erab_id - 2);
|
||||
}
|
||||
|
||||
void bearer_cfg_handler::fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg)
|
||||
|
|
|
@ -96,6 +96,8 @@ uint16_t compute_mac_i(uint16_t crnti,
|
|||
|
||||
// Compute MAC-I
|
||||
switch (integ_algo) {
|
||||
case srsran::INTEGRITY_ALGORITHM_ID_EIA0:
|
||||
return 0;
|
||||
case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1:
|
||||
srsran::security_128_eia1(&k_rrc_int[16],
|
||||
0xffffffff, // 32-bit all to ones
|
||||
|
@ -115,7 +117,7 @@ uint16_t compute_mac_i(uint16_t crnti,
|
|||
mac_key);
|
||||
break;
|
||||
default:
|
||||
printf("Unsupported integrity algorithm %d.", integ_algo);
|
||||
srsran::console_stderr("ERROR: Unsupported integrity algorithm %d.\n", integ_algo);
|
||||
}
|
||||
|
||||
uint16_t short_mac_i = (((uint16_t)mac_key[2] << 8u) | (uint16_t)mac_key[3]);
|
||||
|
@ -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;
|
||||
auto& mob_info = recfg_r8.mob_ctrl_info;
|
||||
mob_info.target_pci = target_cell.cell_cfg.pci;
|
||||
mob_info.t304.value = mob_ctrl_info_s::t304_opts::ms2000; // TODO: make it reconfigurable
|
||||
mob_info.t304 = target_cell.cell_cfg.t304;
|
||||
mob_info.new_ue_id.from_number(rrc_ue->rnti);
|
||||
mob_info.rr_cfg_common.pusch_cfg_common = target_cell.sib2.rr_cfg_common.pusch_cfg_common;
|
||||
mob_info.rr_cfg_common.prach_cfg.root_seq_idx = target_cell.sib2.rr_cfg_common.prach_cfg.root_seq_idx;
|
||||
|
|
|
@ -224,7 +224,10 @@ int32_t rrc_nr::generate_sibs()
|
|||
return SRSRAN_ERROR;
|
||||
}
|
||||
asn1::bit_ref bref(mib_buf->msg, mib_buf->get_tailroom());
|
||||
mib_msg.pack(bref);
|
||||
if (mib_msg.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||
logger.error("Couldn't pack mib msg");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
mib_buf->N_bytes = bref.distance_bytes();
|
||||
logger.debug(mib_buf->msg, mib_buf->N_bytes, "MIB payload (%d B)", mib_buf->N_bytes);
|
||||
mib_buffer = std::move(mib_buf);
|
||||
|
@ -262,7 +265,10 @@ int32_t rrc_nr::generate_sibs()
|
|||
return SRSRAN_ERROR;
|
||||
}
|
||||
asn1::bit_ref bref(sib->msg, sib->get_tailroom());
|
||||
msg[msg_index].pack(bref);
|
||||
if (msg[msg_index].pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||
logger.error("Failed to pack SIB message %d", msg_index);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
sib->N_bytes = bref.distance_bytes();
|
||||
sib_buffer.push_back(std::move(sib));
|
||||
|
||||
|
|
|
@ -65,10 +65,14 @@ int rrc::ue::init()
|
|||
// Configure
|
||||
apply_setup_phy_common(parent->cfg.sibs[1].sib2().rr_cfg_common, true);
|
||||
|
||||
rlf_timer = parent->task_sched.get_unique_timer();
|
||||
activity_timer = parent->task_sched.get_unique_timer();
|
||||
rlf_release_timer = parent->task_sched.get_unique_timer();
|
||||
activity_timer = parent->task_sched.get_unique_timer();
|
||||
set_activity_timeout(MSG3_RX_TIMEOUT); // next UE response is Msg3
|
||||
set_rlf_timeout();
|
||||
|
||||
// Set timeout to release UE context after RLF detection
|
||||
uint32_t deadline_ms = parent->cfg.rlf_release_timer_ms;
|
||||
rlf_release_timer.set(deadline_ms, [this](uint32_t tid) { rlf_timer_expired(); });
|
||||
parent->logger.info("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline_ms);
|
||||
|
||||
mobility_handler = make_rnti_obj<rrc_mobility>(rnti, this);
|
||||
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)
|
||||
{
|
||||
parent->logger.debug(
|
||||
|
@ -111,13 +129,13 @@ void rrc::ue::set_radiolink_dl_state(bool crc_res)
|
|||
if (crc_res) {
|
||||
consecutive_kos_dl = 0;
|
||||
consecutive_kos_ul = 0;
|
||||
rlf_timer.stop();
|
||||
stop_rlf_timer();
|
||||
return;
|
||||
}
|
||||
|
||||
// Count KOs in MAC and trigger release if it goes above a certain value.
|
||||
// This is done to detect out-of-coverage UEs
|
||||
if (rlf_timer.is_running()) {
|
||||
if (rlf_release_timer.is_running()) {
|
||||
// RLF timer already running, no need to count KOs
|
||||
return;
|
||||
}
|
||||
|
@ -138,13 +156,13 @@ void rrc::ue::set_radiolink_ul_state(bool crc_res)
|
|||
if (crc_res) {
|
||||
consecutive_kos_dl = 0;
|
||||
consecutive_kos_ul = 0;
|
||||
rlf_timer.stop();
|
||||
stop_rlf_timer();
|
||||
return;
|
||||
}
|
||||
|
||||
// Count KOs in MAC and trigger release if it goes above a certain value.
|
||||
// This is done to detect out-of-coverage UEs
|
||||
if (rlf_timer.is_running()) {
|
||||
if (rlf_release_timer.is_running()) {
|
||||
// RLF timer already running, no need to count KOs
|
||||
return;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
rlf_timer.stop();
|
||||
stop_rlf_timer();
|
||||
if (parent) {
|
||||
parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed());
|
||||
|
||||
|
@ -191,7 +209,7 @@ void rrc::ue::rlf_timer_expired()
|
|||
{
|
||||
activity_timer.stop();
|
||||
if (parent) {
|
||||
parent->logger.info("RLF timer for rnti=0x%x expired after %d ms", rnti, rlf_timer.time_elapsed());
|
||||
parent->logger.info("RLF timer for rnti=0x%x expired after %d ms", rnti, rlf_release_timer.time_elapsed());
|
||||
|
||||
if (parent->s1ap->user_exists(rnti)) {
|
||||
parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost);
|
||||
|
@ -212,28 +230,12 @@ void rrc::ue::max_retx_reached()
|
|||
parent->logger.info("Max retx reached for rnti=0x%x", rnti);
|
||||
|
||||
// Give UE time to start re-establishment
|
||||
rlf_timer.run();
|
||||
start_rlf_timer();
|
||||
|
||||
mac_ctrl.handle_max_retx();
|
||||
}
|
||||
}
|
||||
|
||||
void rrc::ue::set_rlf_timeout()
|
||||
{
|
||||
uint32_t deadline_s = 0;
|
||||
uint32_t deadline_ms = 0;
|
||||
|
||||
deadline_ms = static_cast<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)
|
||||
{
|
||||
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>(procedure_result_code::none),
|
||||
rnti);
|
||||
const rrc_conn_reest_request_r8_ies_s& req_r8 = msg->crit_exts.rrc_conn_reest_request_r8();
|
||||
uint16_t old_rnti = req_r8.ue_id.c_rnti.to_number();
|
||||
|
||||
srsran::console(
|
||||
"User 0x%x requesting RRC Reestablishment as 0x%x. Cause: %s\n", rnti, old_rnti, req_r8.reest_cause.to_string());
|
||||
|
||||
if (not parent->s1ap->is_mme_connected()) {
|
||||
parent->logger.error("MME isn't connected. Sending Connection Reject");
|
||||
send_connection_reject(procedure_result_code::error_mme_not_connected);
|
||||
send_connection_reest_rej(procedure_result_code::error_mme_not_connected);
|
||||
srsran::console("User 0x%x RRC Reestablishment Request rejected\n", rnti);
|
||||
return;
|
||||
}
|
||||
parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s",
|
||||
|
@ -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(),
|
||||
msg->crit_exts.rrc_conn_reest_request_r8().reest_cause.to_string());
|
||||
if (is_idle()) {
|
||||
uint16_t old_rnti = msg->crit_exts.rrc_conn_reest_request_r8().ue_id.c_rnti.to_number();
|
||||
uint16_t old_pci = msg->crit_exts.rrc_conn_reest_request_r8().ue_id.pci;
|
||||
const enb_cell_common* old_cell = parent->cell_common_list->get_pci(old_pci);
|
||||
auto ue_it = parent->users.find(old_rnti);
|
||||
|
@ -590,9 +597,13 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
|
|||
} else {
|
||||
parent->logger.error("Received ConnectionReestablishment for rnti=0x%x without context", old_rnti);
|
||||
send_connection_reest_rej(procedure_result_code::error_unknown_rnti);
|
||||
srsran::console(
|
||||
"User 0x%x RRC Reestablishment Request rejected. Cause: no rnti=0x%x context available\n", rnti, old_rnti);
|
||||
}
|
||||
} else {
|
||||
parent->logger.error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE", rnti);
|
||||
send_connection_reest_rej(procedure_result_code::error_unknown_rnti);
|
||||
srsran::console("ERROR: User 0x%x requesting Reestablishment is not in RRC_IDLE\n", rnti);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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_encryption(rnti, srb_to_lcid(lte_srb::srb1));
|
||||
|
||||
// Reestablish current DRBs during ConnectionReconfiguration
|
||||
bearer_list = std::move(parent->users.at(old_reest_rnti)->bearer_list);
|
||||
// Reestablish E-RABs of old rnti during ConnectionReconfiguration
|
||||
bearer_list.reestablish_bearers(std::move(parent->users.at(old_reest_rnti)->bearer_list));
|
||||
|
||||
// remove old RNTI
|
||||
parent->rem_user_thread(old_reest_rnti);
|
||||
|
@ -920,7 +931,10 @@ bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg)
|
|||
dest.resize(bref2.distance_bytes());
|
||||
memcpy(dest.data(), pdu->msg, bref2.distance_bytes());
|
||||
bref2 = asn1::bit_ref{pdu->msg, pdu->get_tailroom()};
|
||||
ue_rat_caps.pack(bref2);
|
||||
if (ue_rat_caps.pack(bref2) != asn1::SRSASN_SUCCESS) {
|
||||
parent->logger.error("Couldn't pack ue rat caps");
|
||||
return false;
|
||||
}
|
||||
pdu->N_bytes = bref2.distance_bytes();
|
||||
parent->s1ap->send_ue_cap_info_indication(rnti, std::move(pdu));
|
||||
}
|
||||
|
|
|
@ -21,5 +21,5 @@
|
|||
set(SOURCES gtpu.cc pdcp.cc rlc.cc s1ap.cc)
|
||||
add_library(srsenb_upper STATIC ${SOURCES})
|
||||
|
||||
set(SOURCES pdcp_nr.cc rlc_nr.cc sdap.cc)
|
||||
set(SOURCES pdcp_nr.cc rlc_nr.cc sdap.cc ngap.cc)
|
||||
add_library(srsgnb_upper STATIC ${SOURCES})
|
||||
|
|
|
@ -45,9 +45,10 @@ gtpu_tunnel_manager::gtpu_tunnel_manager(srsran::task_sched_handle task_sched_,
|
|||
logger(logger), task_sched(task_sched_), tunnels(1)
|
||||
{}
|
||||
|
||||
void gtpu_tunnel_manager::init(pdcp_interface_gtpu* pdcp_)
|
||||
void gtpu_tunnel_manager::init(const gtpu_args_t& args, pdcp_interface_gtpu* pdcp_)
|
||||
{
|
||||
pdcp = pdcp_;
|
||||
gtpu_args = &args;
|
||||
pdcp = pdcp_;
|
||||
}
|
||||
|
||||
const gtpu_tunnel_manager::tunnel* gtpu_tunnel_manager::find_tunnel(uint32_t teid)
|
||||
|
@ -99,7 +100,11 @@ const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid,
|
|||
tun->spgw_addr = spgw_addr;
|
||||
|
||||
if (not ue_teidin_db.contains(rnti)) {
|
||||
ue_teidin_db.insert(rnti, ue_lcid_tunnel_list());
|
||||
auto ret = ue_teidin_db.insert(rnti, ue_lcid_tunnel_list());
|
||||
if (ret.is_error()) {
|
||||
logger.error("Failed to allocate rnti=0x%x", rnti);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
auto& ue_tunnels = ue_teidin_db[rnti];
|
||||
|
||||
|
@ -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);
|
||||
logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti);
|
||||
|
||||
// Change RNTI bearers map
|
||||
ue_teidin_db.insert(new_rnti, std::move(*old_rnti_ptr));
|
||||
ue_teidin_db.erase(old_rnti);
|
||||
|
||||
// Change TEID in existing tunnels
|
||||
auto* new_rnti_ptr = find_rnti_tunnels(new_rnti);
|
||||
for (lcid_tunnel& bearer : *new_rnti_ptr) {
|
||||
// create new RNTI and update TEIDs of old rnti to reflect new rnti
|
||||
if (not ue_teidin_db.insert(new_rnti, ue_lcid_tunnel_list())) {
|
||||
logger.error("Failure to create new rnti=0x%x", new_rnti);
|
||||
return false;
|
||||
}
|
||||
std::swap(ue_teidin_db[new_rnti], *old_rnti_ptr);
|
||||
auto& new_rnti_obj = ue_teidin_db[new_rnti];
|
||||
for (lcid_tunnel& bearer : new_rnti_obj) {
|
||||
tunnels[bearer.teid].rnti = new_rnti;
|
||||
}
|
||||
|
||||
// Leave old_rnti as zombie to be removed later
|
||||
old_rnti_ptr->clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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);
|
||||
|
||||
for (lcid_tunnel& lcid_tun : to_rem) {
|
||||
bool ret = tunnels.erase(lcid_tun.teid);
|
||||
bool removed = false;
|
||||
for (srsran::span<lcid_tunnel> to_rem = find_rnti_lcid_tunnels(rnti, lcid); not to_rem.empty();
|
||||
to_rem = find_rnti_lcid_tunnels(rnti, lcid)) {
|
||||
uint32_t teid = to_rem.front().teid;
|
||||
bool ret = remove_tunnel(teid);
|
||||
srsran_expect(ret,
|
||||
"Inconsistency detected between internal data structures for rnti=0x%x,lcid=%d," TEID_IN_FMT,
|
||||
rnti,
|
||||
lcid,
|
||||
lcid_tun.teid);
|
||||
teid);
|
||||
removed |= ret;
|
||||
}
|
||||
ue_teidin_db[rnti].erase(to_rem.begin(), to_rem.end());
|
||||
return true;
|
||||
return removed;
|
||||
}
|
||||
|
||||
bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti)
|
||||
|
@ -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
|
||||
before_tun.rx_timer = task_sched.get_unique_timer();
|
||||
before_tun.rx_timer.set(500, [this, before_teid](uint32_t tid) {
|
||||
// This will self-destruct the callback object
|
||||
remove_tunnel(before_teid);
|
||||
});
|
||||
before_tun.rx_timer.run();
|
||||
// Schedule auto-removal of the indirect tunnel in case the End Marker is not received
|
||||
// TS 36.300 - On detection of the "end marker", the target eNB may also initiate the release of the data forwarding
|
||||
// resource. However, the release of the data forwarding resource is implementation dependent and could
|
||||
// also be based on other mechanisms (e.g. timer-based mechanism).
|
||||
if (gtpu_args->indirect_tunnel_timeout_msec > 0) {
|
||||
before_tun.rx_timer = task_sched.get_unique_timer();
|
||||
before_tun.rx_timer.set(gtpu_args->indirect_tunnel_timeout_msec, [this, before_teid](uint32_t tid) {
|
||||
// Note: This will self-destruct the callback object
|
||||
logger.info("Forwarding tunnel " TEID_IN_FMT "being closed after timeout=%d msec",
|
||||
before_teid,
|
||||
gtpu_args->indirect_tunnel_timeout_msec);
|
||||
remove_tunnel(before_teid);
|
||||
});
|
||||
before_tun.rx_timer.run();
|
||||
}
|
||||
}
|
||||
|
||||
void gtpu_tunnel_manager::handle_rx_pdcp_sdu(uint32_t teid)
|
||||
|
@ -332,18 +347,14 @@ gtpu::~gtpu()
|
|||
stop();
|
||||
}
|
||||
|
||||
int gtpu::init(std::string gtp_bind_addr_,
|
||||
std::string mme_addr_,
|
||||
std::string m1u_multiaddr_,
|
||||
std::string m1u_if_addr_,
|
||||
srsenb::pdcp_interface_gtpu* pdcp_,
|
||||
bool enable_mbsfn_)
|
||||
int gtpu::init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_)
|
||||
{
|
||||
args = gtpu_args;
|
||||
pdcp = pdcp_;
|
||||
gtp_bind_addr = gtp_bind_addr_;
|
||||
mme_addr = mme_addr_;
|
||||
gtp_bind_addr = gtpu_args.gtp_bind_addr;
|
||||
mme_addr = gtpu_args.mme_addr;
|
||||
|
||||
tunnels.init(pdcp);
|
||||
tunnels.init(args, pdcp);
|
||||
|
||||
char errbuf[128] = {};
|
||||
|
||||
|
@ -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));
|
||||
|
||||
// Start MCH socket if enabled
|
||||
enable_mbsfn = enable_mbsfn_;
|
||||
if (enable_mbsfn) {
|
||||
if (not m1u.init(m1u_multiaddr_, m1u_if_addr_)) {
|
||||
if (args.embms_enable) {
|
||||
if (not m1u.init(args.embms_m1u_multiaddr, args.embms_m1u_if_addr)) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size)
|
||||
{
|
||||
rrc->read_pdu_pcch(payload, buffer_size);
|
||||
}
|
||||
|
||||
int rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes)
|
||||
{
|
||||
int ret;
|
||||
|
|
|
@ -1815,7 +1815,10 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt
|
|||
return false;
|
||||
}
|
||||
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
|
||||
tx_pdu.pack(bref);
|
||||
if (tx_pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||
logger.error("Failed to pack TX PDU %s", procedure_name);
|
||||
return false;
|
||||
}
|
||||
buf->N_bytes = bref.distance_bytes();
|
||||
|
||||
// Save message to PCAP
|
||||
|
|
|
@ -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)
|
||||
add_test(sched_dci_test sched_dci_test)
|
||||
|
||||
add_executable(sched_ue_cell_test sched_ue_cell_test.cc)
|
||||
target_link_libraries(sched_ue_cell_test srsran_common srsenb_mac srsran_mac sched_test_common)
|
||||
add_test(sched_ue_cell_test sched_ue_cell_test)
|
||||
|
||||
add_executable(sched_benchmark_test sched_benchmark.cc)
|
||||
target_link_libraries(sched_benchmark_test srsran_common srsenb_mac srsran_mac sched_test_common)
|
||||
add_test(sched_benchmark_test sched_benchmark_test)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue