Merge branch 'next' into agpl_next

master
Codebot 4 years ago committed by Your Name
commit 34dbee4c7d

@ -242,7 +242,7 @@ public:
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, N); }
const_iterator begin() const { return iterator(this, 0); }
const_iterator begin() const { return const_iterator(this, 0); }
const_iterator end() const { return const_iterator(this, N); }
iterator find(K id)

@ -0,0 +1,246 @@
/**
*
* \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_INTRUSIVE_LIST_H
#define SRSRAN_INTRUSIVE_LIST_H
#include <iterator>
#include <type_traits>
namespace srsran {
struct default_intrusive_tag;
/// Base class of T, where T is a node of intrusive_forward_list<T>
template <typename Tag = default_intrusive_tag>
struct intrusive_forward_list_element {
intrusive_forward_list_element<Tag>* next_node = nullptr;
};
/**
* Forward linked list of pointers of type "T" that doesn't rely on allocations.
* It leverages each node's internal pointer (thus intrusive) to store the next node of the list.
* It supports push_front/pop_front, iteration, clear, etc.
* @tparam T node type. It must be a subclass of intrusive_forward_list_element<Tag>
* @tparam Tag useful to differentiate multiple intrusive lists in the same node
*/
template <typename T, typename Tag = default_intrusive_tag>
class intrusive_forward_list
{
using node_t = intrusive_forward_list_element<Tag>;
template <typename U>
class iterator_impl
{
using elem_t = typename std::conditional<std::is_const<U>::value, const node_t, node_t>::type;
public:
using iterator_category = std::forward_iterator_tag;
using value_type = U;
using difference_type = std::ptrdiff_t;
using pointer = U*;
using reference = U&;
explicit iterator_impl(elem_t* node_ = nullptr) : node(node_) {}
iterator_impl<U>& operator++()
{
node = node->next_node;
return *this;
}
pointer operator->() { return static_cast<pointer>(node); }
reference operator*() { return static_cast<reference>(*node); }
bool operator==(const iterator_impl<U>& other) const { return node == other.node; }
bool operator!=(const iterator_impl<U>& other) const { return node != other.node; }
private:
elem_t* node;
};
public:
using iterator = iterator_impl<T>;
using const_iterator = iterator_impl<const T>;
intrusive_forward_list()
{
static_assert(std::is_base_of<node_t, T>::value,
"Provided template argument T must have intrusive_forward_list_element<Tag> as base class");
}
intrusive_forward_list(const intrusive_forward_list&) = default;
intrusive_forward_list(intrusive_forward_list&& other) noexcept : node(other.node) { other.node = nullptr; }
intrusive_forward_list& operator=(const intrusive_forward_list&) = default;
intrusive_forward_list& operator =(intrusive_forward_list&& other) noexcept
{
node = other.node;
other.node = nullptr;
return *this;
}
T& front() const { return *static_cast<T*>(node); }
void push_front(T* t)
{
node_t* new_head = static_cast<node_t*>(t);
new_head->next_node = node;
node = new_head;
}
T* pop_front()
{
node_t* ret = node;
node = node->next_node;
return static_cast<T*>(ret);
}
void clear()
{
while (node != nullptr) {
node_t* torem = node;
node = node->next_node;
torem->next_node = nullptr;
}
}
bool empty() const { return node == nullptr; }
iterator begin() { return iterator(node); }
iterator end() { return iterator(nullptr); }
const_iterator begin() const { return const_iterator(node); }
const_iterator end() const { return const_iterator(nullptr); }
private:
node_t* node = nullptr;
};
template <typename Tag = default_intrusive_tag>
struct intrusive_double_linked_list_element {
intrusive_double_linked_list_element<Tag>* next_node = nullptr;
intrusive_double_linked_list_element<Tag>* prev_node = nullptr;
};
/**
* Double Linked List of pointers of type "T" that doesn't rely on allocations.
* Instead, it leverages T's internal pointers to store the next and previous nodes
* @tparam T node type. Must be a subclass of intrusive_double_linked_list_element<Tag>
* @tparam Tag tag of nodes. Useful to differentiate separate intrusive lists inside the same T node
*/
template <typename T, typename Tag = default_intrusive_tag>
class intrusive_double_linked_list
{
using node_t = intrusive_double_linked_list_element<Tag>;
template <typename U>
class iterator_impl
{
using elem_t = typename std::conditional<std::is_const<U>::value, const node_t, node_t>::type;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = U;
using difference_type = std::ptrdiff_t;
using pointer = U*;
using reference = U&;
explicit iterator_impl(elem_t* node_ = nullptr) : node(node_) {}
iterator_impl<U>& operator++()
{
node = node->next_node;
return *this;
}
iterator_impl<U>& operator--()
{
node = node->prev_node;
return *this;
}
pointer operator->() { return static_cast<pointer>(node); }
reference operator*() { return static_cast<reference>(*node); }
bool operator==(const iterator_impl<U>& other) const { return node == other.node; }
bool operator!=(const iterator_impl<U>& other) const { return node != other.node; }
private:
elem_t* node;
};
public:
using iterator = iterator_impl<T>;
using const_iterator = iterator_impl<const T>;
intrusive_double_linked_list()
{
static_assert(std::is_base_of<node_t, T>::value,
"Provided template argument T must have intrusive_forward_list_element<Tag> as base class");
}
intrusive_double_linked_list(const intrusive_double_linked_list&) = default;
intrusive_double_linked_list(intrusive_double_linked_list&& other) noexcept : node(other.node)
{
other.node = nullptr;
}
intrusive_double_linked_list& operator=(const intrusive_double_linked_list&) = default;
intrusive_double_linked_list& operator=(intrusive_double_linked_list&& other) noexcept
{
node = other.node;
other.node = nullptr;
return *this;
}
~intrusive_double_linked_list() { clear(); }
T& front() const { return *static_cast<T*>(node); }
void push_front(T* t)
{
node_t* new_head = static_cast<node_t*>(t);
new_head->prev_node = nullptr;
new_head->next_node = node;
if (node != nullptr) {
node->prev_node = new_head;
}
node = new_head;
}
void pop(T* t)
{
node_t* to_rem = static_cast<node_t*>(t);
if (to_rem == node) {
node = to_rem->next_node;
}
if (to_rem->prev_node != nullptr) {
to_rem->prev_node->next_node = to_rem->next_node;
}
if (to_rem->next_node != nullptr) {
to_rem->next_node->prev_node = to_rem->prev_node;
}
to_rem->next_node = nullptr;
to_rem->prev_node = nullptr;
}
void pop_front() { pop(static_cast<T*>(node)); }
void clear()
{
while (node != nullptr) {
node_t* torem = node;
node = node->next_node;
torem->next_node = nullptr;
torem->prev_node = nullptr;
}
}
bool empty() const { return node == nullptr; }
iterator begin() { return iterator(node); }
iterator end() { return iterator(nullptr); }
const_iterator begin() const { return const_iterator(node); }
const_iterator end() const { return const_iterator(nullptr); }
private:
node_t* node = nullptr;
};
} // namespace srsran
#endif // SRSRAN_INTRUSIVE_LIST_H

@ -40,6 +40,7 @@ struct rlc_cfg_c;
struct pdcp_cfg_s;
struct lc_ch_cfg_s;
struct rach_cfg_common_s;
struct phr_cfg_s;
// Phy
struct tdd_ul_dl_cfg_common_s;
@ -101,6 +102,7 @@ bool make_phy_pusch_scaling(const asn1::rrc_nr::uci_on_pusch_s& uci_on_pusch, fl
**************************/
logical_channel_config_t make_mac_logical_channel_cfg_t(uint8_t lcid, const asn1::rrc_nr::lc_ch_cfg_s& asn1_type);
rach_nr_cfg_t make_mac_rach_cfg(const asn1::rrc_nr::rach_cfg_common_s& asn1_type);
bool make_mac_phr_cfg_t(const asn1::rrc_nr::phr_cfg_s& asn1_type, phr_cfg_nr_t* phr_cfg_nr);
/***************************
* RLC Config
**************************/

@ -47,6 +47,10 @@ struct bearers_subject_to_status_transfer_item_ies_o;
struct erab_level_qos_params_s;
struct ho_cmd_s;
struct erab_admitted_item_s;
struct erab_to_be_modified_item_bearer_mod_req_s;
struct cause_c;
struct erab_item_s;
struct ue_aggregate_maximum_bitrate_s;
template <class ies_set_paramT_>
struct protocol_ie_single_container_s;
@ -55,7 +59,32 @@ using bearers_subject_to_status_transfer_list_l =
using rrc_establishment_cause_e = enumerated<rrc_establishment_cause_opts, true, 3>;
using cause_radio_network_e = enumerated<cause_radio_network_opts, true, 4>;
/**************************
* S1AP Obj Id
*************************/
template <typename T>
uint32_t get_obj_id(const T& obj);
template <typename T>
bool lower_obj_id(const T& lhs, const T& rhs)
{
return get_obj_id(lhs) < get_obj_id(rhs);
}
template <typename T>
bool equal_obj_id(const T& lhs, const T& rhs)
{
return get_obj_id(lhs) == get_obj_id(rhs);
}
} // namespace s1ap
} // namespace asn1
namespace srsenb {
using transp_addr_t = asn1::bounded_bitstring<1, 160, true, true>;
}
#endif // SRSRAN_S1AP_UTILS_H

@ -57,7 +57,8 @@ public:
if (capacity_ > 0) {
nof_buffers = (uint32_t)capacity_;
}
used.reserve(nof_buffers);
pool.reserve(nof_buffers);
free_list.reserve(nof_buffers);
pthread_mutex_init(&mutex, nullptr);
pthread_cond_init(&cv_not_empty, nullptr);
for (uint32_t i = 0; i < nof_buffers; i++) {
@ -66,21 +67,16 @@ public:
perror("Error allocating memory. Exiting...\n");
exit(-1);
}
available.push(b);
pool.push_back(b);
free_list.push_back(b);
}
capacity = nof_buffers;
}
~buffer_pool()
{
// this destructor assumes all buffers have been properly deallocated
while (available.size()) {
delete available.top();
available.pop();
}
for (uint32_t i = 0; i < used.size(); i++) {
delete used[i];
for (auto* p : pool) {
delete p;
}
pthread_cond_destroy(&cv_not_empty);
pthread_mutex_destroy(&mutex);
@ -88,11 +84,13 @@ public:
void print_all_buffers()
{
printf("%d buffers in queue\n", (int)used.size());
printf("%d buffers in queue\n", static_cast<int>(pool.size() - free_list.size()));
#ifdef SRSRAN_BUFFER_POOL_LOG_ENABLED
std::map<std::string, uint32_t> buffer_cnt;
for (uint32_t i = 0; i < used.size(); i++) {
buffer_cnt[strlen(used[i]->debug_name) ? used[i]->debug_name : "Undefined"]++;
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"]++;
}
}
std::map<std::string, uint32_t>::iterator it;
for (it = buffer_cnt.begin(); it != buffer_cnt.end(); it++) {
@ -101,22 +99,21 @@ public:
#endif
}
uint32_t nof_available_pdus() { return available.size(); }
uint32_t nof_available_pdus() { return free_list.size(); }
bool is_almost_empty() { return available.size() < capacity / 20; }
bool is_almost_empty() { return free_list.size() < capacity / 20; }
buffer_t* allocate(const char* debug_name = nullptr, bool blocking = false)
{
pthread_mutex_lock(&mutex);
buffer_t* b = nullptr;
if (available.size() > 0) {
b = available.top();
used.push_back(b);
available.pop();
if (!free_list.empty()) {
b = free_list.back();
free_list.pop_back();
if (is_almost_empty()) {
printf("Warning buffer pool capacity is %f %%\n", (float)100 * available.size() / capacity);
printf("Warning buffer pool capacity is %f %%\n", (float)100 * free_list.size() / capacity);
}
#ifdef SRSRAN_BUFFER_POOL_LOG_ENABLED
if (debug_name) {
@ -126,14 +123,13 @@ public:
#endif
} else if (blocking) {
// blocking allocation
while (available.size() == 0) {
while (free_list.empty()) {
pthread_cond_wait(&cv_not_empty, &mutex);
}
// retrieve the new buffer
b = available.top();
used.push_back(b);
available.pop();
b = free_list.back();
free_list.pop_back();
// do not print any warning
} else {
@ -152,10 +148,8 @@ public:
{
bool ret = false;
pthread_mutex_lock(&mutex);
typename std::vector<buffer_t*>::iterator elem = std::find(used.begin(), used.end(), b);
if (elem != used.end()) {
used.erase(elem);
available.push(b);
if (std::find(pool.cbegin(), pool.cend(), b) != pool.cend()) {
free_list.push_back(b);
ret = true;
}
pthread_cond_signal(&cv_not_empty);
@ -165,8 +159,8 @@ public:
private:
static const int POOL_SIZE = 4096;
std::stack<buffer_t*> available;
std::vector<buffer_t*> used;
std::vector<buffer_t*> pool;
std::vector<buffer_t*> free_list;
pthread_mutex_t mutex;
pthread_cond_t cv_not_empty;
uint32_t capacity;

@ -0,0 +1,91 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2012-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_LTE_COMMON_H
#define SRSRAN_LTE_COMMON_H
#include <array>
#include <cstdint>
namespace srsran {
// Cell nof PRBs
const std::array<uint32_t, 6> lte_cell_nof_prbs = {6, 15, 25, 50, 75, 100};
inline uint32_t lte_cell_nof_prb_to_index(uint32_t nof_prb)
{
switch (nof_prb) {
case 6:
return 0;
case 15:
return 1;
case 25:
return 2;
case 50:
return 3;
case 75:
return 4;
case 100:
return 5;
default:
break;
}
return -1;
}
inline bool is_lte_cell_nof_prb(uint32_t nof_prb)
{
return lte_cell_nof_prb_to_index(nof_prb) < lte_cell_nof_prbs.size();
}
// Radio Bearers
enum class lte_srb { srb0, srb1, srb2, count };
const uint32_t MAX_LTE_SRB_ID = 2;
enum class lte_drb { drb1 = 1, drb2, drb3, drb4, drb5, drb6, drb7, drb8, drb9, drb10, drb11, invalid };
const uint32_t MAX_LTE_DRB_ID = 11;
const uint32_t MAX_NOF_BEARERS = 14;
constexpr bool is_lte_rb(uint32_t lcid)
{
return lcid < MAX_NOF_BEARERS;
}
constexpr bool is_lte_srb(uint32_t lcid)
{
return lcid <= MAX_LTE_SRB_ID;
}
inline const char* get_srb_name(lte_srb srb_id)
{
static const char* names[] = {"SRB0", "SRB1", "SRB2", "invalid SRB id"};
return names[(uint32_t)(srb_id < lte_srb::count ? srb_id : lte_srb::count)];
}
constexpr uint32_t srb_to_lcid(lte_srb srb_id)
{
return static_cast<uint32_t>(srb_id);
}
constexpr lte_srb lte_lcid_to_srb(uint32_t lcid)
{
return static_cast<lte_srb>(lcid);
}
constexpr bool is_lte_drb(uint32_t lcid)
{
return lcid > MAX_LTE_SRB_ID and is_lte_rb(lcid);
}
inline const char* get_drb_name(lte_drb drb_id)
{
static const char* names[] = {
"DRB1", "DRB2", "DRB3", "DRB4", "DRB5", "DRB6", "DRB7", "DRB8", "DRB9", "DRB10", "DRB11", "invalid DRB id"};
return names[(uint32_t)(drb_id < lte_drb::invalid ? drb_id : lte_drb::invalid) - 1];
}
} // namespace srsran
#endif // SRSRAN_LTE_COMMON_H

@ -90,7 +90,7 @@ private:
std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - sleep_start);
if (m) {
metrics_t metric;
metrics_t metric = {};
m->get_metrics(&metric);
for (uint32_t i = 0; i < listeners.size(); i++) {
listeners[i]->set_metrics(metric, period_usec.count());

@ -29,17 +29,13 @@
#ifndef SRSRAN_TIMERS_H
#define SRSRAN_TIMERS_H
#include "srsran/adt/intrusive_list.h"
#include "srsran/adt/move_callback.h"
#include "srsran/phy/utils/debug.h"
#include <algorithm>
#include <functional>
#include <cstdint>
#include <deque>
#include <limits>
#include <mutex>
#include <queue>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include <vector>
namespace srsran {
@ -49,42 +45,60 @@ public:
virtual void timer_expired(uint32_t timer_id) = 0;
};
/**
* Class that manages stack timers. It allows creation of unique_timers, with different ids. Each unique_timer duration,
* and callback can be set via the set(...) method. A timer can be started/stopped via run()/stop() methods.
* Internal Data structures:
* - timer_list - std::deque that stores timer objects via push_back() to keep pointer/reference validity.
* The timer index in the timer_list matches the timer object id field.
* This deque will only grow in size. Erased timers are just tagged in the deque as empty, and can be reused for the
* creation of new timers. To avoid unnecessary runtime allocations, the user can set an initial capacity.
* - free_list - intrusive forward linked list to keep track of the empty timers and speed up new timer creation.
* - A large circular vector of size WHEEL_SIZE which works as a time wheel, storing and circularly indexing the
* currently running timers by their respective timeout value.
* For a number of running timers N, and uniform distribution of timeout values, the step_all() complexity
* should be O(N/WHEEL_SIZE). Thus, the performance should improve with a larger WHEEL_SIZE, at the expense of more
* used memory.
*/
class timer_handler
{
constexpr static uint32_t MAX_TIMER_DURATION = std::numeric_limits<uint32_t>::max() / 4;
constexpr static uint32_t MAX_TIMER_VALUE = std::numeric_limits<uint32_t>::max() / 2;
struct timer_impl {
timer_handler* parent;
uint32_t duration = 0, timeout = 0;
bool running = false;
bool active = false;
using tic_diff_t = uint32_t;
using tic_t = uint32_t;
constexpr static uint32_t INVALID_ID = std::numeric_limits<uint32_t>::max();
constexpr static tic_diff_t INVALID_TIME_DIFF = std::numeric_limits<tic_diff_t>::max();
constexpr static size_t WHEEL_SHIFT = 16U;
constexpr static size_t WHEEL_SIZE = 1U << WHEEL_SHIFT;
constexpr static size_t WHEEL_MASK = WHEEL_SIZE - 1U;
struct timer_impl : public intrusive_double_linked_list_element<>, public intrusive_forward_list_element<> {
timer_handler& parent;
const uint32_t id;
tic_diff_t duration = INVALID_TIME_DIFF;
tic_t timeout = 0;
enum state_t : int8_t { empty, stopped, running, expired } state = empty;
srsran::move_callback<void(uint32_t)> callback;
explicit timer_impl(timer_handler* parent_) : parent(parent_) {}
uint32_t id() const { return std::distance((const timer_handler::timer_impl*)&parent->timer_list[0], this); }
bool is_running() const { return active and running and timeout > 0; }
bool is_expired() const { return active and not running and timeout > 0 and timeout <= parent->cur_time; }
explicit timer_impl(timer_handler& parent_, uint32_t id_) : parent(parent_), id(id_) {}
timer_impl(const timer_impl&) = delete;
timer_impl(timer_impl&&) = delete;
timer_impl& operator=(const timer_impl&) = delete;
timer_impl& operator=(timer_impl&&) = delete;
uint32_t time_elapsed() const { return std::min(duration, parent->cur_time - (timeout - duration)); }
bool is_empty() const { return state == empty; }
bool is_running() const { return state == running; }
bool is_expired() const { return state == expired; }
tic_diff_t time_left() const { return is_running() ? timeout - parent.cur_time : (is_expired() ? 0 : duration); }
uint32_t time_elapsed() const { return duration - time_left(); }
bool set(uint32_t duration_)
{
if (duration_ > MAX_TIMER_DURATION) {
ERROR("Error: timer durations above %u are not supported", MAX_TIMER_DURATION);
return false;
}
if (not active) {
ERROR("Error: setting inactive timer id=%d", id());
return false;
}
duration = duration_;
duration = std::max(duration_, 1U); // the next step will be one place ahead of current one
if (is_running()) {
// if already running, just extends timer lifetime
run();
} else {
state = stopped;
timeout = 0;
}
return true;
}
@ -100,235 +114,242 @@ class timer_handler
void run()
{
std::unique_lock<std::mutex> lock(parent->mutex);
if (not active) {
ERROR("Error: calling run() for inactive timer id=%d", id());
return;
}
timeout = parent->cur_time + duration;
parent->running_timers.emplace(id(), timeout);
running = true;
std::lock_guard<std::mutex> lock(parent.mutex);
parent.start_run_(*this);
}
void stop()
{
running = false; // invalidates trigger
if (not is_expired()) {
timeout = 0; // if it has already expired, then do not alter is_expired() state
}
}
void clear()
{
stop();
duration = 0;
active = false;
callback = srsran::move_callback<void(uint32_t)>();
// leave run_id unchanged. Since the timeout was changed, we shall not get spurious triggering
std::lock_guard<std::mutex> lock(parent.mutex);
// does not call callback
parent.stop_timer_(*this, false);
}
void trigger()
{
if (is_running()) {
running = false;
if (not callback.is_empty()) {
callback(id());
}
}
}
void deallocate() { parent.dealloc_timer(*this); }
};
public:
class unique_timer
{
public:
unique_timer() : timer_id(std::numeric_limits<decltype(timer_id)>::max()) {}
explicit unique_timer(timer_handler* parent_, uint32_t timer_id_) : parent(parent_), timer_id(timer_id_) {}
unique_timer() = default;
explicit unique_timer(timer_impl* handle_) : handle(handle_) {}
unique_timer(const unique_timer&) = delete;
unique_timer(unique_timer&& other) noexcept : parent(other.parent), timer_id(other.timer_id)
{
other.parent = nullptr;
}
~unique_timer()
{
if (parent != nullptr) {
// does not call callback
impl()->clear();
}
}
unique_timer(unique_timer&& other) noexcept : handle(other.handle) { other.handle = nullptr; }
~unique_timer() { release(); }
unique_timer& operator=(const unique_timer&) = delete;
unique_timer& operator =(unique_timer&& other) noexcept
{
if (this != &other) {
timer_id = other.timer_id;
parent = other.parent;
other.parent = nullptr;
handle = other.handle;
other.handle = nullptr;
}
return *this;
}
bool is_valid() const { return parent != nullptr; }
bool is_valid() const { return handle != nullptr; }
void set(uint32_t duration_, move_callback<void(uint32_t)> callback_)
{
impl()->set(duration_, std::move(callback_));
srsran_assert(is_valid(), "Trying to setup empty timer handle");
handle->set(duration_, std::move(callback_));
}
void set(uint32_t duration_)
{
srsran_assert(is_valid(), "Trying to setup empty timer handle");
handle->set(duration_);
}
void set(uint32_t duration_) { impl()->set(duration_); }
bool is_set() const { return (impl()->duration != 0); }
bool is_set() const { return is_valid() and handle->duration != INVALID_TIME_DIFF; }
bool is_running() const { return impl()->is_running(); }
bool is_running() const { return is_valid() and handle->is_running(); }
bool is_expired() const { return impl()->is_expired(); }
bool is_expired() const { return is_valid() and handle->is_expired(); }
uint32_t time_elapsed() const { return impl()->time_elapsed(); }
tic_diff_t time_elapsed() const { return is_valid() ? handle->time_elapsed() : INVALID_TIME_DIFF; }
void run() { impl()->run(); }
uint32_t id() const { return is_valid() ? handle->id : INVALID_ID; }
void stop() { impl()->stop(); }
tic_diff_t duration() const { return is_valid() ? handle->duration : INVALID_TIME_DIFF; }
void release()
void run()
{
impl()->clear();
parent = nullptr;
srsran_assert(is_valid(), "Starting invalid timer");
handle->run();
}
uint32_t id() const { return timer_id; }
void stop()
{
if (is_valid()) {
handle->stop();
}
}
uint32_t duration() const { return impl()->duration; }
void release()
{
if (is_valid()) {
handle->deallocate();
handle = nullptr;
}
}
private:
timer_impl* impl() { return &parent->timer_list[timer_id]; }
const timer_impl* impl() const { return &parent->timer_list[timer_id]; }
timer_handler* parent = nullptr;
uint32_t timer_id;
timer_impl* handle = nullptr;
};
explicit timer_handler(uint32_t capacity = 64)
{
timer_list.reserve(capacity);
// reserve a priority queue using a vector
std::vector<timer_run> v;
v.reserve(capacity);
std::priority_queue<timer_run> q(std::less<timer_run>(), std::move(v));
running_timers = std::move(q);
time_wheel.resize(WHEEL_SIZE);
// Pre-reserve timers
while (timer_list.size() < capacity) {
timer_list.emplace_back(*this, timer_list.size());
}
// push to free list in reverse order to keep ascending ids
for (auto it = timer_list.rbegin(); it != timer_list.rend(); ++it) {
free_list.push_front(&(*it));
}
nof_free_timers = timer_list.size();
}
void step_all()
{
std::unique_lock<std::mutex> lock(mutex);
cur_time++;
while (not running_timers.empty()) {
uint32_t next_timeout = running_timers.top().timeout;
timer_impl* ptr = &timer_list[running_timers.top().timer_id];
if (not ptr->is_running() or next_timeout != ptr->timeout) {
// remove timers that were explicitly stopped, or re-run, to avoid unnecessary priority_queue growth
running_timers.pop();
continue;
}
if (cur_time < next_timeout) {
break;
}
// if the timer_run and timer_impl timeouts do not match, it means that timer_impl::timeout was overwritten.
// in such case, do not trigger
uint32_t timeout = running_timers.top().timeout;
running_timers.pop();
if (ptr->timeout == timeout) {
// unlock mutex, it could be that the callback tries to run a timer too
auto& wheel_list = time_wheel[cur_time & WHEEL_MASK];
for (auto it = wheel_list.begin(); it != wheel_list.end();) {
timer_impl& timer = timer_list[it->id];
++it;
if (timer.timeout == cur_time) {
// stop timer (callback has to see the timer has already expired)
stop_timer_(timer, true);
// Call callback if configured
if (not timer.callback.is_empty()) {
// unlock mutex. It can happen that the callback tries to run a timer too
lock.unlock();
// Call callback
ptr->trigger();
timer.callback(timer.id);
// Lock again to keep protecting the queue
// Lock again to keep protecting the wheel
lock.lock();
}
}
}
}
void stop_all()
{
std::lock_guard<std::mutex> lock(mutex);
// does not call callback
while (not running_timers.empty()) {
running_timers.pop();
}
for (auto& i : timer_list) {
i.running = false;
for (timer_impl& timer : timer_list) {
stop_timer_(timer, false);
}
}
unique_timer get_unique_timer() { return unique_timer(this, alloc_timer()); }
uint32_t get_cur_time() const { return cur_time; }
unique_timer get_unique_timer() { return unique_timer(&alloc_timer()); }
uint32_t nof_timers() const
{
return std::count_if(timer_list.begin(), timer_list.end(), [](const timer_impl& t) { return t.active; });
std::lock_guard<std::mutex> lock(mutex);
return timer_list.size() - nof_free_timers;
}
uint32_t nof_running_timers() const
{
return std::count_if(timer_list.begin(), timer_list.end(), [](const timer_impl& t) { return t.is_running(); });
std::lock_guard<std::mutex> lock(mutex);
return nof_timers_running_;
}
template <typename F>
void defer_callback(uint32_t duration, const F& func)
{
uint32_t id = alloc_timer();
srsran::move_callback<void(uint32_t)> c = [func, this, id](uint32_t tid) {
timer_impl& timer = alloc_timer();
srsran::move_callback<void(uint32_t)> c = [func, &timer](uint32_t tid) {
func();
// auto-deletes timer
timer_list[id].clear();
timer.deallocate();
};
timer_list[id].set(duration, std::move(c));
timer_list[id].run();
timer.set(duration, std::move(c));
timer.run();
}
private:
struct timer_run {
uint32_t timer_id;
uint32_t timeout;
timer_run(uint32_t timer_id_, uint32_t timeout_) : timer_id(timer_id_), timeout(timeout_) {}
bool operator<(const timer_run& other) const
timer_impl& alloc_timer()
{
std::lock_guard<std::mutex> lock(mutex);
timer_impl* t;
if (not free_list.empty()) {
t = &free_list.front();
srsran_assert(t->is_empty(), "Invalid timer id=%d state", t->id);
free_list.pop_front();
nof_free_timers--;
} else {
// Need to increase deque
timer_list.emplace_back(*this, timer_list.size());
t = &timer_list.back();
}
t->state = timer_impl::stopped;
return *t;
}
void dealloc_timer(timer_impl& timer)
{
// returns true, if other.timeout is lower than timeout, accounting for wrap around
if (timeout > other.timeout) {
return (timeout - other.timeout) < MAX_TIMER_VALUE / 2;
std::lock_guard<std::mutex> lock(mutex);
if (timer.is_empty()) {
// already deallocated
return;
}
return (other.timeout - timeout) > MAX_TIMER_VALUE / 2;
stop_timer_(timer, false);
timer.state = timer_impl::empty;
timer.duration = INVALID_TIME_DIFF;
timer.timeout = 0;
timer.callback = srsran::move_callback<void(uint32_t)>();
free_list.push_front(&timer);
nof_free_timers++;
// leave id unchanged.
}
};
uint32_t alloc_timer()
void start_run_(timer_impl& timer)
{
uint32_t i = 0;
for (; i < timer_list.size(); ++i) {
if (not timer_list[i].active) {
break;
uint32_t timeout = cur_time + timer.duration;
size_t new_wheel_pos = timeout & WHEEL_MASK;
if (timer.is_running() and (timer.timeout & WHEEL_MASK) == new_wheel_pos) {
// If no change in timer wheel position
return;
}
// Stop timer if it was running, removing it from wheel in the process
stop_timer_(timer, false);
// Insert timer in wheel
time_wheel[new_wheel_pos].push_front(&timer);
timer.timeout = timeout;
timer.state = timer_impl::running;
nof_timers_running_++;
}
if (i == timer_list.size()) {
timer_list.emplace_back(this);
/// called when user manually stops timer (as an alternative to expiry)
void stop_timer_(timer_impl& timer, bool expiry)
{
if (not timer.is_running()) {
return;
}
timer_list[i].active = true;
return i;
// If already running, need to disconnect it from previous wheel
time_wheel[timer.timeout & WHEEL_MASK].pop(&timer);
timer.state = expiry ? timer_impl::expired : timer_impl::stopped;
nof_timers_running_--;
}
std::vector<timer_impl> timer_list;
std::priority_queue<timer_run> running_timers;
uint32_t cur_time = 0;
std::mutex mutex; // Protect priority queue
tic_t cur_time = 0;
size_t nof_timers_running_ = 0, nof_free_timers = 0;
// using a deque to maintain reference validity on emplace_back. Also, this deque will only grow.
std::deque<timer_impl> timer_list;
srsran::intrusive_forward_list<timer_impl> free_list;
std::vector<srsran::intrusive_double_linked_list<timer_impl> > time_wheel;
mutable std::mutex mutex; // Protect priority queue
};
using unique_timer = timer_handler::unique_timer;

@ -41,6 +41,8 @@ struct meas_cell_cfg_t {
uint16_t pci;
uint32_t eci;
float q_offset;
uint32_t allowed_meas_bw;
bool direct_forward_path_available;
};
// neigh measurement Cell info
@ -49,6 +51,7 @@ struct rrc_meas_cfg_t {
std::vector<asn1::rrc::report_cfg_eutra_s> meas_reports;
asn1::rrc::quant_cfg_eutra_s quant_cfg;
uint32_t meas_gap_period;
uint32_t allowed_meas_bw;
};
// Cell/Sector configuration

@ -32,22 +32,49 @@ namespace srsenb {
class rrc_interface_s1ap
{
public:
using failed_erab_list = std::map<uint32_t, asn1::s1ap::cause_c>;
virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0;
virtual void release_ue(uint16_t rnti) = 0;
virtual bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) = 0;
virtual bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) = 0;
virtual bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) = 0;
virtual void modify_erabs(uint16_t rnti,
const asn1::s1ap::erab_modify_request_s& msg,
std::vector<uint16_t>* erabs_modified,
std::vector<uint16_t>* erabs_failed_to_modify) = 0;
virtual bool has_erab(uint16_t rnti, uint32_t erab_id) const = 0;
virtual bool release_erabs(uint32_t rnti) = 0;
virtual void release_erabs(uint32_t rnti,
const asn1::s1ap::erab_release_cmd_s& msg,
std::vector<uint16_t>* erabs_released,
std::vector<uint16_t>* erabs_failed_to_release) = 0;
virtual int get_erab_addr_in(uint16_t rnti, uint16_t erab_id, transp_addr_t& addr_in, uint32_t& teid_in) const = 0;
virtual void set_aggregate_max_bitrate(uint16_t rnti, const asn1::s1ap::ue_aggregate_maximum_bitrate_s& bitrate) = 0;
/**
* TS 36.413, 8.2.1 and 8.3.1 - Setup E-RAB / Initial Context Setup
* @return if error, cause argument is updated with cause
*/
virtual int setup_erab(uint16_t rnti,
uint16_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos_params,
srsran::const_span<uint8_t> nas_pdu,
const transp_addr_t& addr,
uint32_t gtpu_teid_out,
asn1::s1ap::cause_c& cause) = 0;
/**
* TS 36.413, 8.2.2 - Modify E-RAB
* @return if error, cause argument is updated with cause
*/
virtual int modify_erab(uint16_t rnti,
uint16_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos_params,
srsran::const_span<uint8_t> nas_pdu,
asn1::s1ap::cause_c& cause) = 0;
/**
* TS 36.413, 8.2.3 - Release E-RAB id
* @return error if E-RAB id or rnti were not found
*/
virtual int release_erab(uint16_t rnti, uint16_t erab_id) = 0;
virtual void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id) = 0;
/// TS 36.413, 8.2.1, 8.2.2, 8.2.3 - Notify UE of ERAB updates (done via RRC Reconfiguration Message)
virtual int notify_ue_erab_updates(uint16_t rnti, srsran::const_span<uint8_t> nas_pdu) = 0;
/**
* Reports the reception of S1 HandoverCommand / HandoverPreparationFailure or abnormal conditions during
* S1 Handover preparation back to RRC.
@ -56,13 +83,15 @@ public:
* @param is_success true if ho cmd was received
* @param container TargeteNB RRCConnectionReconfiguration message with MobilityControlInfo
*/
enum class ho_prep_result { success, failure, timeout };
virtual void ho_preparation_complete(uint16_t rnti,
bool is_success,
ho_prep_result result,
const asn1::s1ap::ho_cmd_s& msg,
srsran::unique_byte_buffer_t container) = 0;
virtual uint16_t
start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg,
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container) = 0;
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container,
asn1::s1ap::cause_c& failure_cause) = 0;
virtual void set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs) = 0;
};
@ -82,7 +111,9 @@ 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, bool ack_info) = 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;
///< Provide packed SIB to MAC (buffer is managed by RRC)

@ -43,6 +43,7 @@ struct s1ap_args_t {
class s1ap_interface_rrc
{
public:
using failed_erab_list = std::map<uint32_t, asn1::s1ap::cause_c>;
struct bearer_status_info {
uint8_t erab_id;
uint16_t pdcp_dl_sn, pdcp_ul_sn;
@ -64,10 +65,11 @@ public:
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::s1ap::cause_radio_network_e cause_radio) = 0;
virtual void ue_ctxt_setup_complete(uint16_t rnti, const asn1::s1ap::init_context_setup_resp_s& res) = 0;
virtual void ue_erab_setup_complete(uint16_t rnti, const asn1::s1ap::erab_setup_resp_s& res) = 0;
virtual bool is_mme_connected() = 0;
/// TS 36.413, 8.3.1 - Initial Context Setup
virtual void ue_ctxt_setup_complete(uint16_t rnti) = 0;
/**
* Command the s1ap to transmit a HandoverRequired message to MME.
* This message initiates the S1 handover preparation procedure at the Source eNB
@ -83,7 +85,8 @@ public:
uint32_t target_eci,
srsran::plmn_id_t target_plmn,
srsran::span<uint32_t> fwd_erabs,
srsran::unique_byte_buffer_t rrc_container) = 0;
srsran::unique_byte_buffer_t rrc_container,
bool has_direct_fwd_path) = 0;
/**
* Command the s1ap to transmit eNBStatusTransfer message to MME. This message passes the PDCP context of the UE
@ -95,25 +98,23 @@ public:
*/
virtual bool send_enb_status_transfer_proc(uint16_t rnti, std::vector<bearer_status_info>& bearer_status_list) = 0;
/* Acknowledge Handover Request message back to MME.
* This message signals the completion of the HandoverPreparation from the TeNB point of view. */
/**
* Cancel on-going S1 Handover. MME should release UE context in target eNB
* SeNB --> MME
*/
virtual void send_ho_cancel(uint16_t rnti, const asn1::s1ap::cause_c& cause) = 0;
/*************************
* Target eNB Handover
************************/
virtual bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
uint32_t enb_cc_idx,
srsran::unique_byte_buffer_t ho_cmd,
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers) = 0;
/**
* Notify MME that Handover is complete
*/
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers,
srsran::const_span<asn1::s1ap::erab_item_s> not_admitted_bearers) = 0;
virtual void send_ho_notify(uint16_t rnti, uint64_t target_eci) = 0;
/**
* Cancel on-going S1 Handover. MME should release UE context in target eNB
* SeNB --> MME
*/
virtual void send_ho_cancel(uint16_t rnti) = 0;
/**
* Called during release of a subset of eNB E-RABs. Send E-RAB RELEASE INDICATION to MME.
* SeNB --> MME

@ -159,6 +159,26 @@ struct sr_cfg_nr_t {
sr_cfg_item_nr_t item[SRSRAN_MAX_MAX_NR_OF_SR_CFG_PER_CELL_GROUP];
};
struct tag_cfg_nr_t {
uint8_t tag_id;
uint32_t time_align_timer;
};
struct phr_cfg_nr_t {
int periodic_timer;
int prohibit_timer;
int tx_pwr_factor_change;
bool extended;
phr_cfg_nr_t() { reset(); }
void reset()
{
periodic_timer = -1;
prohibit_timer = -1;
tx_pwr_factor_change = -1;
extended = false;
}
};
struct bsr_cfg_nr_t {
// mandatory BSR config
int periodic_timer;

@ -179,8 +179,9 @@ struct pdcp_lte_state_t {
};
// Custom type for interface between PDCP and RLC to convey SDU delivery status
#define MAX_SDUS_PER_RLC_PDU (256) // default to RLC SDU queue length
#define MAX_SDUS_TO_NOTIFY (MAX_SDUS_PER_RLC_PDU) // Arbitrarily chosen limit
// Arbitrarily chosen limit, optimal value depends on the RLC (pollPDU) and PDCP config, channel BLER,
// traffic characterisitcs, etc. The chosen value has been tested with 100 PRB bi-dir TCP
#define MAX_SDUS_TO_NOTIFY (1024)
typedef srsran::bounded_vector<uint32_t, MAX_SDUS_TO_NOTIFY> pdcp_sn_vector_t;
} // namespace srsran

@ -44,21 +44,6 @@ struct phy_cfg_nr_t {
phy_cfg_nr_t()
{
// tdd-UL-DL-ConfigurationCommon
// referenceSubcarrierSpacing: kHz15 (0)
// pattern1
// dl-UL-TransmissionPeriodicity: ms10 (7)
// nrofDownlinkSlots: 7
// nrofDownlinkSymbols: 6
// nrofUplinkSlots: 2
// nrofUplinkSymbols: 4
tdd.pattern1.period_ms = 10;
tdd.pattern1.nof_dl_slots = 7;
tdd.pattern1.nof_dl_symbols = 6;
tdd.pattern1.nof_ul_slots = 2;
tdd.pattern1.nof_ul_symbols = 4;
tdd.pattern2.period_ms = 0;
// physicalCellGroupConfig
// pdsch-HARQ-ACK-Codebook: dynamic (1)
harq_ack.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic;

@ -49,11 +49,15 @@ public:
uint32_t tti;
} mac_nr_grant_dl_t;
// UL grant as conveyed between PHY and MAC
typedef struct {
uint32_t pid;
uint16_t rnti;
uint32_t tti;
uint8_t pid; // HARQ process ID
uint32_t tbs; // transport block size in Bytes
uint8_t ndi; // Raw new data indicator extracted from DCI
uint8_t rv; // Redundancy Version
bool is_rar_grant; // True if grant comes from RAR
} mac_nr_grant_ul_t;
/// For UL, payload buffer remains in MAC
@ -120,6 +124,9 @@ public:
virtual int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg) = 0;
virtual int set_config(const srsran::sr_cfg_nr_t& sr_cfg) = 0;
virtual void set_config(const srsran::rach_nr_cfg_t& rach_cfg) = 0;
virtual int add_tag_config(const srsran::tag_cfg_nr_t& tag_cfg) = 0;
virtual int set_config(const srsran::phr_cfg_nr_t& phr_cfg) = 0;
virtual int remove_tag_config(const uint32_t tag_id) = 0;
// RRC triggers MAC ra procedure
virtual void start_ra_procedure() = 0;

@ -80,7 +80,7 @@ public:
virtual void set_ue_identity(srsran::s_tmsi_t s_tmsi) = 0;
virtual bool is_connected() = 0;
virtual void paging_completed(bool outcome) = 0;
virtual std::string get_rb_name(uint32_t lcid) = 0;
virtual const char* get_rb_name(uint32_t lcid) = 0;
virtual uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) = 0;
virtual bool has_nr_dc() = 0;
};
@ -93,14 +93,14 @@ public:
virtual void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) = 0;
virtual void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) = 0;
virtual void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
virtual std::string get_rb_name(uint32_t lcid) = 0;
virtual const char* get_rb_name(uint32_t lcid) = 0;
};
class rrc_interface_rlc
{
public:
virtual void max_retx_attempted() = 0;
virtual std::string get_rb_name(uint32_t lcid) = 0;
virtual const char* get_rb_name(uint32_t lcid) = 0;
virtual void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
};

@ -63,7 +63,7 @@ public:
// SDUs up to 256 B can use the short 8-bit L field
static const int32_t MAC_SUBHEADER_LEN_THRESHOLD = 256;
mac_sch_subpdu_nr(mac_sch_pdu_nr* parent_) : parent(parent_), logger(&srslog::fetch_basic_logger("MAC")){};
mac_sch_subpdu_nr(mac_sch_pdu_nr* parent_) : parent(parent_), logger(&srslog::fetch_basic_logger("MAC-NR")){};
nr_lcid_sch_t get_type();
bool is_sdu();
@ -129,10 +129,10 @@ private:
class mac_sch_pdu_nr
{
public:
mac_sch_pdu_nr(bool ulsch_ = false) : ulsch(ulsch_), logger(srslog::fetch_basic_logger("MAC")) {}
mac_sch_pdu_nr(bool ulsch_ = false) : ulsch(ulsch_), logger(srslog::fetch_basic_logger("MAC-NR")) {}
void pack();
void unpack(const uint8_t* payload, const uint32_t& len);
int unpack(const uint8_t* payload, const uint32_t& len);
uint32_t get_num_subpdus();
const mac_sch_subpdu_nr& get_subpdu(const uint32_t& index);
bool is_ulsch();

@ -58,6 +58,7 @@ typedef struct {
cf_t* pilot_estimates; /// Pilots least squares estimates
cf_t* temp; /// Temporal data vector of size SRSRAN_NRE * carrier.nof_prb
float* filter; ///< Smoothing filter
} srsran_dmrs_sch_t;
/**

@ -129,6 +129,21 @@ extern "C" {
*/
#define SRSRAN_MAX_NOF_DL_DATA_TO_UL 8
/**
* @brief Maximum number of HARQ processes in the DL, signaled through RRC (PDSCH-ServingCellConfig)
*/
#define SRSRAN_MAX_HARQ_PROC_DL_NR 16 // 3GPP TS 38.214 version 15.3.0 Sec. 5.1 or nrofHARQ-ProcessesForPDSCH
/**
* @brief Default number of HARQ processes in the DL, if config is absent.
*/
#define SRSRAN_DEFAULT_HARQ_PROC_DL_NR 8
/**
* @brief Maximum number of HARQ processes in the UL, signaled through RRC (ConfiguredGrantConfig)
*/
#define SRSRAN_MAX_HARQ_PROC_UL_NR 16 // 3GPP TS 38.214 version 15.3.0 Sec. 6.1
typedef enum SRSRAN_API {
srsran_coreset_mapping_type_non_interleaved = 0,
srsran_coreset_mapping_type_interleaved,

@ -33,6 +33,7 @@
#define SRSRAN_CRC_H
#include "srsran/config.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct SRSRAN_API {
@ -82,4 +83,6 @@ SRSRAN_API uint32_t srsran_crc_checksum_byte(srsran_crc_t* h, const uint8_t* dat
SRSRAN_API uint32_t srsran_crc_checksum(srsran_crc_t* h, uint8_t* data, int len);
SRSRAN_API bool srsran_crc_match(srsran_crc_t* h, uint8_t* data, int len);
#endif // SRSRAN_CRC_H

@ -32,13 +32,14 @@
#ifndef SRSRAN_LDPCDECODER_H
#define SRSRAN_LDPCDECODER_H
#include "srsran/phy/fec/crc.h"
#include "srsran/phy/fec/ldpc/base_graph.h"
/*!
* \brief Types of LDPC decoder.
*/
typedef enum {
SRSRAN_LDPC_DECODER_F, /*!< \brief %Decoder working with real-valued LLRs. */
SRSRAN_LDPC_DECODER_F = 0, /*!< \brief %Decoder working with real-valued LLRs. */
SRSRAN_LDPC_DECODER_S, /*!< \brief %Decoder working with 16-bit integer-valued LLRs. */
SRSRAN_LDPC_DECODER_C, /*!< \brief %Decoder working with 8-bit integer-valued LLRs. */
SRSRAN_LDPC_DECODER_C_FLOOD, /*!< \brief %Decoder working with 8-bit integer-valued LLRs, flooded scheduling. */
@ -50,6 +51,17 @@ typedef enum {
(AVX512 version). */
} srsran_ldpc_decoder_type_t;
/*!
* \brief Describes the LDPC decoder configuration arguments.
*/
typedef struct {
srsran_ldpc_decoder_type_t type; /*!< \brief Type of LDPC decoder. */
srsran_basegraph_t bg; /*!< \brief The desired base graph (BG1 or BG2). */
uint16_t ls; /*!< \brief The desired lifting size. */
float scaling_fctr; /*!< \brief Scaling factor of the normalized min-sum algorithm.*/
uint32_t max_nof_iter; /*!< \brief Maximum number of iterations, set to 0 for default value. */
} srsran_ldpc_decoder_args_t;
/*!
* \brief Describes an LDPC decoder.
*/
@ -57,6 +69,7 @@ typedef struct SRSRAN_API {
void* ptr; /*!< \brief Registers used by the decoder. */
srsran_basegraph_t bg; /*!< \brief Current base graph. */
uint16_t ls; /*!< \brief Current lifting size. */
uint32_t max_nof_iter; /*!< \brief Maximum number of iterations. */
uint8_t bgN; /*!< \brief Number of variable nodes in the BG. */
uint16_t liftN; /*!< \brief Number of variable nodes in the lifted graph. */
uint8_t bgM; /*!< \brief Number of check nodes in the BG. */
@ -74,32 +87,28 @@ typedef struct SRSRAN_API {
int (*decode_f)(void*,
const float*,
uint8_t*,
uint32_t); /*!< \brief Pointer to the decoding function (float version). */
uint32_t,
srsran_crc_t*); /*!< \brief Pointer to the decoding function (float version). */
int (*decode_s)(void*,
const int16_t*,
uint8_t*,
uint32_t); /*!< \brief Pointer to the decoding function (16-bit version). */
uint32_t,
srsran_crc_t*); /*!< \brief Pointer to the decoding function (16-bit version). */
int (*decode_c)(void*,
const int8_t*,
uint8_t*,
uint32_t); /*!< \brief Pointer to the decoding function (16-bit version). */
uint32_t,
srsran_crc_t*); /*!< \brief Pointer to the decoding function (16-bit version). */
} srsran_ldpc_decoder_t;
/*!
* Initializes all the LDPC decoder variables according to the given base graph
* and lifting size.
* \param[out] q A pointer to a srsran_ldpc_decoder_t structure.
* \param[in] type Type of LDPC decoder.
* \param[in] bg The desired base graph (BG1 or BG2).
* \param[in] ls The desired lifting size.
* \param[in] scaling_fctr Scaling factor of the normalized min-sum algorithm.
* \param[in] args LDPC configuration arguments.
* \return An integer: 0 if the function executes correctly, -1 otherwise.
*/
SRSRAN_API int srsran_ldpc_decoder_init(srsran_ldpc_decoder_t* q,
srsran_ldpc_decoder_type_t type,
srsran_basegraph_t bg,
uint16_t ls,
float scaling_fctr);
SRSRAN_API int srsran_ldpc_decoder_init(srsran_ldpc_decoder_t* q, const srsran_ldpc_decoder_args_t* args);
/*!
* The LDPC decoder "destructor": it frees all the resources allocated to the decoder.
@ -150,4 +159,24 @@ srsran_ldpc_decoder_decode_s(srsran_ldpc_decoder_t* q, const int16_t* llrs, uint
SRSRAN_API int
srsran_ldpc_decoder_decode_c(srsran_ldpc_decoder_t* q, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length);
/*!
* Carries out the actual decoding with 8-bit integer-valued LLRs. It is
* recommended to use a 7-bit representation for the LLRs, given that all
* values exceeding \f$ 2^{7}-1 \f$ (in magnitude) will be considered as infinity.
* \param[in] q A pointer to the LDPC decoder (a srsran_ldpc_decoder_t structure
* instance) that carries out the decoding.
* \param[in] llrs The LLRs obtained from the channel samples that correspond to
* the codeword to be decoded.
* \param[out] message The message (uncoded bits) resulting from the decoding
* operation.
* \param[in] cdwd_rm_length The number of bits forming the codeword (after rate matching).
* \param[in,out] crc Code-block CRC object for early stop. Set for NULL to disable check
* \return -1 if an error occurred, the number of used iterations, and 0 if CRC is provided and did not match
*/
SRSRAN_API int srsran_ldpc_decoder_decode_crc_c(srsran_ldpc_decoder_t* q,
const int8_t* llrs,
uint8_t* message,
uint32_t cdwd_rm_length,
srsran_crc_t* crc);
#endif // SRSRAN_LDPCDECODER_H

@ -42,4 +42,14 @@ SRSRAN_API int srsran_demod_soft_demodulate_s(srsran_mod_t modulation, const cf_
SRSRAN_API int srsran_demod_soft_demodulate_b(srsran_mod_t modulation, const cf_t* symbols, int8_t* llr, int nsymbols);
/**
* @brief Soft-demodulates complex symbols into 8-bit LLR. It forces zero symbols produce zero LLRs.
* @param modulation Modulation
* @param symbols Complex symbols
* @param llr 8-bit LLRs
* @param nsymbols Number of symbols
* @return SRSLTE_SUCCESS if the provided pointers are valid, SRSLTE_ERROR code otherwise
*/
SRSRAN_API int srsran_demod_soft_demodulate2_b(srsran_mod_t modulation, const cf_t* symbols, int8_t* llr, int nsymbols);
#endif // SRSRAN_DEMOD_SOFT_H

@ -46,6 +46,8 @@ typedef struct SRSRAN_API {
srsran_sch_nr_args_t sch;
bool measure_evm;
bool measure_time;
bool disable_zero_re_around_dc; ///< PDSCH NR sets the LLR around the DC to zero to avoid noise
uint32_t nof_zero_re_around_dc; ///< Number of RE to set to zero around DC. It uses default value if 0.
} srsran_pdsch_nr_args_t;
/**
@ -66,15 +68,16 @@ typedef struct SRSRAN_API {
uint32_t meas_time_us;
srsran_re_pattern_t dmrs_re_pattern;
uint32_t nof_rvd_re;
uint32_t nof_zero_re_around_dc; ///< Sets a number of RE surrounding the center of the resource grid to zero. Set to 0
///< for disabling.
} srsran_pdsch_nr_t;
/**
*
* @brief Groups NR-PDSCH data for reception
*/
typedef struct {
uint8_t* payload;
bool crc;
float evm;
srsran_sch_tb_res_nr_t tb[SRSRAN_MAX_TB]; ///< SCH payload
float evm[SRSRAN_MAX_CODEWORDS]; ///< EVM measurement if configured through arguments
} srsran_pdsch_res_nr_t;
SRSRAN_API int srsran_pdsch_nr_init_enb(srsran_pdsch_nr_t* q, const srsran_pdsch_nr_args_t* args);
@ -96,7 +99,7 @@ SRSRAN_API int srsran_pdsch_nr_decode(srsran_pdsch_nr_t* q,
const srsran_sch_grant_nr_t* grant,
srsran_chest_dl_res_t* channel,
cf_t* sf_symbols[SRSRAN_MAX_PORTS],
srsran_pdsch_res_nr_t data[SRSRAN_MAX_TB]);
srsran_pdsch_res_nr_t* res);
SRSRAN_API uint32_t srsran_pdsch_nr_rx_info(const srsran_pdsch_nr_t* q,
const srsran_sch_cfg_nr_t* cfg,

@ -125,6 +125,21 @@ typedef struct SRSRAN_API {
uint32_t sf[5];
} srsran_prach_sf_config_t;
///@brief Maximum number of subframe number candidates for PRACH NR configuration
#define PRACH_NR_CFG_MAX_NOF_SF 5
/**
* @brief PRACH configuration for NR as described in TS 38.211 Tables 6.3.3.2-2, 6.3.3.2-3 and 6.3.3.2-4
*/
typedef struct {
uint32_t preamble_format;
uint32_t x;
uint32_t y;
uint32_t subframe_number[PRACH_NR_CFG_MAX_NOF_SF];
uint32_t nof_subframe_number;
uint32_t starting_symbol; // subframe number
} prach_nr_config_t;
typedef enum SRSRAN_API {
SRSRAN_PRACH_SFN_EVEN = 0,
SRSRAN_PRACH_SFN_ANY,
@ -169,6 +184,12 @@ SRSRAN_API bool srsran_prach_tti_opportunity_config_tdd(uint32_t config_idx,
uint32_t current_tti,
uint32_t* prach_idx);
SRSRAN_API const prach_nr_config_t* srsran_prach_nr_get_cfg_fr1_unpaired(uint32_t config_idx);
SRSRAN_API bool srsran_prach_nr_tti_opportunity_fr1_unpaired(uint32_t config_idx, uint32_t current_tti);
SRSRAN_API uint32_t srsran_prach_nr_start_symbol_fr1_unpaired(uint32_t config_idx);
SRSRAN_API uint32_t srsran_prach_f_ra_tdd(uint32_t config_idx,
uint32_t tdd_ul_dl_config,
uint32_t current_tti,

@ -79,7 +79,7 @@ typedef struct SRSRAN_API {
* @brief Groups NR-PUSCH data for transmission
*/
typedef struct {
uint8_t* payload; ///< SCH payload
uint8_t* payload[SRSRAN_MAX_TB]; ///< SCH payload
srsran_uci_value_nr_t uci; ///< UCI payload
} srsran_pusch_data_nr_t;
@ -87,10 +87,9 @@ typedef struct {
* @brief Groups NR-PUSCH data for reception
*/
typedef struct {
uint8_t* payload; ///< SCH payload
srsran_sch_tb_res_nr_t tb[SRSRAN_MAX_TB]; ///< SCH payload
srsran_uci_value_nr_t uci; ///< UCI payload
bool crc; ///< CRC match
float evm; ///< EVM measurement if configured through arguments
float evm[SRSRAN_MAX_CODEWORDS]; ///< EVM measurement if configured through arguments
} srsran_pusch_res_nr_t;
SRSRAN_API int srsran_pusch_nr_init_gnb(srsran_pusch_nr_t* q, const srsran_pusch_nr_args_t* args);

@ -35,9 +35,12 @@ typedef struct SRSRAN_API {
typedef struct SRSRAN_API {
srsran_mod_t mod;
uint32_t N_L; ///< the number of transmission layers that the transport block is mapped onto
uint32_t mcs; ///< Modulation Code Scheme (MCS) for debug and trace purpose
int tbs; ///< Payload size, TS 38.212 refers to it as A
double R; ///< Target LDPC rate
int rv;
int rv; ///< Redundancy version
int ndi; ///< New Data Indicator
int pid; ///< HARQ Process ID
uint32_t nof_re; ///< Number of available resource elements to send, known as N_RE
uint32_t nof_bits; ///< Number of available bits to send, known as G
uint32_t cw_idx;

@ -41,6 +41,15 @@
#define SRSRAN_SCH_NR_MAX_NOF_CB_LDPC \
((SRSRAN_SLOT_MAX_NOF_BITS_NR + (SRSRAN_LDPC_BG2_MAX_LEN_CB - 1)) / SRSRAN_LDPC_BG2_MAX_LEN_CB)
/**
* @brief Groups NR-PUSCH data for reception
*/
typedef struct {
uint8_t* payload; ///< SCH payload
bool crc; ///< CRC match
float avg_iter; ///< Average iterations
} srsran_sch_tb_res_nr_t;
typedef struct SRSRAN_API {
srsran_carrier_nr_t carrier;
@ -72,6 +81,7 @@ typedef struct SRSRAN_API {
bool disable_simd;
bool decoder_use_flooded;
float decoder_scaling_factor;
uint32_t max_nof_iter; ///< Maximum number of LDPC iterations
} srsran_sch_nr_args_t;
/**
@ -161,8 +171,7 @@ SRSRAN_API int srsran_dlsch_nr_decode(srsran_sch_nr_t* q,
const srsran_sch_cfg_t* sch_cfg,
const srsran_sch_tb_t* tb,
int8_t* e_bits,
uint8_t* data,
bool* crc_ok);
srsran_sch_tb_res_nr_t* res);
SRSRAN_API int srsran_ulsch_nr_encode(srsran_sch_nr_t* q,
const srsran_sch_cfg_t* cfg,
@ -174,9 +183,9 @@ SRSRAN_API int srsran_ulsch_nr_decode(srsran_sch_nr_t* q,
const srsran_sch_cfg_t* sch_cfg,
const srsran_sch_tb_t* tb,
int8_t* e_bits,
uint8_t* data,
bool* crc_ok);
srsran_sch_tb_res_nr_t* res);
SRSRAN_API int srsran_sch_nr_tb_info(const srsran_sch_tb_t* tb, char* str, uint32_t str_len);
SRSRAN_API int
srsran_sch_nr_tb_info(const srsran_sch_tb_t* tb, const srsran_sch_tb_res_nr_t* res, char* str, uint32_t str_len);
#endif // SRSRAN_SCH_NR_H

@ -1276,6 +1276,10 @@ template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
}
class dynamic_arg_list {
public:
static constexpr std::size_t max_pool_string_size = 256;
private:
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
@ -1284,6 +1288,10 @@ class dynamic_arg_list {
std::unique_ptr<node<>> next;
};
// Pool storage allocation functions.
static void *allocate_from_pool(std::size_t sz);
static void free_from_pool(void *ptr);
template <typename T> struct typed_node : node<> {
T value;
@ -1295,9 +1303,35 @@ class dynamic_arg_list {
: value(arg.data(), arg.size()) {}
};
struct pooled_node : node<> {
std::array<char, max_pool_string_size> value;
static void* operator new(std::size_t sz) {
return allocate_from_pool(sz);
}
static void operator delete(void* ptr) {
free_from_pool(ptr);
}
pooled_node(const char *str, std::size_t sz) {
FMT_ASSERT(sz < value.size(), "String is too big");
std::copy(str, str + sz, value.begin());
}
};
std::unique_ptr<node<>> head_;
public:
static constexpr std::size_t max_pool_node_size = sizeof(pooled_node);
const char *push_small_string(const char *str, std::size_t sz) {
auto new_node = std::unique_ptr<pooled_node>(new pooled_node(str, sz));
auto& value = new_node->value;
new_node->next = std::move(head_);
head_ = std::move(new_node);
return value.data();
}
template <typename T, typename Arg> const T& push(const Arg& arg) {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
@ -1541,12 +1575,25 @@ class dynamic_format_arg_store
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
template <typename T,
typename std::enable_if<detail::is_string<typename std::decay<T>::type>::value, int>::type = 0>
void push_back(const T& arg) {
fmt::string_view view(arg);
if (view.size() + 1 < dynamic_args_.max_pool_string_size) {
emplace_arg(dynamic_args_.push_small_string(view.data(), view.size() + 1));
} else {
emplace_arg(dynamic_args_.push<stored_type<T> >(arg));
else
}
}
template <typename T,
typename std::enable_if<!detail::is_string<typename std::decay<T>::type>::value, int>::type = 0>
void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value)) {
emplace_arg(dynamic_args_.push<stored_type<T> >(arg));
} else {
emplace_arg(detail::unwrap(arg));
}
}
/**
\rst

@ -107,6 +107,9 @@ template <typename T, typename... Args>
inline T& fetch_logger(const std::string& id, Args&&... args)
{
static_assert(detail::is_logger<T>::value, "T should be a logger type");
if (auto *logger = find_logger<T>(id)) {
return *logger;
}
auto logger = detail::make_any<T>(id, std::forward<Args>(args)...);
detail::any* p = detail::fetch_logger(id, std::move(logger));

@ -35,15 +35,13 @@ class stack_test_dummy : public stack_interface_rrc
public:
stack_test_dummy() {}
srsran::tti_point get_current_tti() override
{
return srsran::tti_point{task_sched.get_timer_handler()->get_cur_time() % 10240};
}
srsran::tti_point get_current_tti() override { return srsran::tti_point{tti % 10240}; }
// Testing utility functions
void run_tti()
{
// update clock and run internal tasks
tti++;
task_sched.tic();
task_sched.run_pending_tasks();
@ -52,6 +50,7 @@ public:
// run pending tasks without updating timers
void run_pending_tasks() { task_sched.run_pending_tasks(); }
uint32_t tti = 0;
srsran::task_scheduler task_sched{512, 100};
};

@ -92,7 +92,7 @@ private:
uint32_t count = 0;
uint32_t bytes = 0;
uint32_t fms = 0;
uint32_t fms = 0; // SN of the first missing PDCP SDU
uint32_t lms = 0;
srsran::circular_array<sdu_data, capacity> sdus;
};

@ -354,6 +354,9 @@ private:
// Mutexes
std::mutex mutex;
// default to RLC SDU queue length
const uint32_t MAX_SDUS_PER_RLC_PDU = RLC_TX_QUEUE_LEN;
};
// Receiver sub-class

@ -58,7 +58,7 @@ INSTALL(TARGETS rrc_asn1 DESTINATION ${LIBRARY_DIR})
# S1AP ASN1 lib
add_library(s1ap_asn1 STATIC
s1ap.cc)
s1ap.cc s1ap_utils.cc)
target_compile_options(s1ap_asn1 PRIVATE "-Os")
target_link_libraries(s1ap_asn1 asn1_utils srsran_common)
INSTALL(TARGETS s1ap_asn1 DESTINATION ${LIBRARY_DIR})

@ -75,6 +75,15 @@ logical_channel_config_t make_mac_logical_channel_cfg_t(uint8_t lcid, const lc_c
return logical_channel_config;
}
bool make_mac_phr_cfg_t(const phr_cfg_s& asn1_type, phr_cfg_nr_t* phr_cfg_nr)
{
phr_cfg_nr->extended = asn1_type.ext;
phr_cfg_nr->periodic_timer = asn1_type.phr_periodic_timer.to_number();
phr_cfg_nr->prohibit_timer = asn1_type.phr_prohibit_timer.to_number();
phr_cfg_nr->tx_pwr_factor_change = asn1_type.phr_tx_pwr_factor_change.to_number();
return true;
}
rach_nr_cfg_t make_mac_rach_cfg(const rach_cfg_common_s& asn1_type)
{
rach_nr_cfg_t rach_nr_cfg = {};
@ -268,6 +277,42 @@ bool make_phy_tdd_cfg(const tdd_ul_dl_cfg_common_s& tdd_ul_dl_cfg_common,
srsran_tdd_config_nr.pattern1.nof_ul_symbols = tdd_ul_dl_cfg_common.pattern1.nrof_ul_symbols;
// Copy and return struct
*in_srsran_tdd_config_nr = srsran_tdd_config_nr;
if (not tdd_ul_dl_cfg_common.pattern2_present) {
return true;
}
switch (tdd_ul_dl_cfg_common.pattern2.dl_ul_tx_periodicity) {
case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms1:
srsran_tdd_config_nr.pattern2.period_ms = 1;
break;
case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms2:
srsran_tdd_config_nr.pattern2.period_ms = 2;
break;
case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms5:
srsran_tdd_config_nr.pattern2.period_ms = 5;
break;
case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10:
srsran_tdd_config_nr.pattern2.period_ms = 10;
break;
case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms1p25:
case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms0p5:
case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms0p625:
case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms2p5:
default:
asn1::log_warning("Invalid option for pattern2 dl_ul_tx_periodicity_opts %s",
tdd_ul_dl_cfg_common.pattern2.dl_ul_tx_periodicity.to_string());
return false;
}
srsran_tdd_config_nr.pattern2.nof_dl_slots = tdd_ul_dl_cfg_common.pattern2.nrof_dl_slots;
srsran_tdd_config_nr.pattern2.nof_dl_symbols = tdd_ul_dl_cfg_common.pattern2.nrof_dl_symbols;
srsran_tdd_config_nr.pattern2.nof_ul_slots = tdd_ul_dl_cfg_common.pattern2.nrof_ul_slots;
srsran_tdd_config_nr.pattern2.nof_ul_symbols = tdd_ul_dl_cfg_common.pattern2.nrof_ul_symbols;
// Copy and return struct
*in_srsran_tdd_config_nr = srsran_tdd_config_nr;
return true;
}

@ -548,6 +548,33 @@ void set_phy_cfg_t_dedicated_cfg(phy_cfg_t* cfg, const asn1::rrc::phys_cfg_ded_s
// TODO
}
if (asn1_type.cqi_report_cfg_r10.is_present()) {
// Parse R10 periodic CQI configuration
cfg->dl_cfg.cqi_report.periodic_configured =
asn1_type.cqi_report_cfg_r10->cqi_report_periodic_r10.type() == asn1::rrc::setup_e::setup;
if (cfg->dl_cfg.cqi_report.periodic_configured) {
const auto& cqi_report_periodic = asn1_type.cqi_report_cfg_r10->cqi_report_periodic_r10.setup();
cfg->ul_cfg.pucch.n_pucch_2 = cqi_report_periodic.cqi_pucch_res_idx_r10;
cfg->ul_cfg.pucch.simul_cqi_ack = cqi_report_periodic.simul_ack_nack_and_cqi;
cfg->dl_cfg.cqi_report.pmi_idx = cqi_report_periodic.cqi_pmi_cfg_idx;
cfg->dl_cfg.cqi_report.format_is_subband =
cqi_report_periodic.cqi_format_ind_periodic_r10.type().value ==
asn1::rrc::cqi_report_periodic_r10_c::setup_s_::cqi_format_ind_periodic_r10_c_::types::subband_cqi_r10;
if (cfg->dl_cfg.cqi_report.format_is_subband) {
cfg->dl_cfg.cqi_report.subband_size = cqi_report_periodic.cqi_format_ind_periodic_r10.subband_cqi_r10().k;
}
if (cqi_report_periodic.ri_cfg_idx_present) {
cfg->dl_cfg.cqi_report.ri_idx = cqi_report_periodic.ri_cfg_idx;
cfg->dl_cfg.cqi_report.ri_idx_present = true;
} else {
cfg->dl_cfg.cqi_report.ri_idx_present = false;
}
} else {
cfg->ul_cfg.pucch.n_pucch_2 = 0;
cfg->ul_cfg.pucch.simul_cqi_ack = false;
}
}
if (asn1_type.cqi_report_cfg_present) {
if (asn1_type.cqi_report_cfg.cqi_report_periodic_present) {
cfg->dl_cfg.cqi_report.periodic_configured =

@ -14502,6 +14502,7 @@ std::string erab_modify_resp_ies_o::value_c::types_opts::to_string() const
}
template struct asn1::s1ap::protocol_ie_field_s<erab_modify_resp_ies_o>;
template struct asn1::s1ap::protocol_ie_single_container_s<asn1::s1ap::erab_failedto_setup_item_ho_req_ack_ies_o>;
erab_modify_resp_ies_container::erab_modify_resp_ies_container() :
mme_ue_s1ap_id(0, crit_e::ignore),

@ -0,0 +1,47 @@
/**
*
* \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/asn1/s1ap_utils.h"
#include "srsran/asn1/s1ap.h"
namespace asn1 {
namespace s1ap {
template <>
uint32_t get_obj_id<erab_item_s>(const erab_item_s& obj)
{
return obj.erab_id;
}
template <>
uint32_t get_obj_id<protocol_ie_single_container_s<erab_to_be_setup_item_ctxt_su_req_ies_o> >(
const protocol_ie_single_container_s<erab_to_be_setup_item_ctxt_su_req_ies_o>& obj)
{
return obj.value.erab_to_be_setup_item_ctxt_su_req().erab_id;
}
template <>
uint32_t get_obj_id<protocol_ie_single_container_s<erab_to_be_setup_item_bearer_su_req_ies_o> >(
const protocol_ie_single_container_s<erab_to_be_setup_item_bearer_su_req_ies_o>& obj)
{
return obj.value.erab_to_be_setup_item_bearer_su_req().erab_id;
}
template <>
uint32_t get_obj_id<protocol_ie_single_container_s<erab_to_be_modified_item_bearer_mod_req_ies_o> >(
const protocol_ie_single_container_s<erab_to_be_modified_item_bearer_mod_req_ies_o>& obj)
{
return obj.value.erab_to_be_modified_item_bearer_mod_req().erab_id;
}
} // namespace s1ap
} // namespace asn1

@ -31,7 +31,7 @@ extern "C" {
namespace srsran {
mac_rar_subpdu_nr::mac_rar_subpdu_nr(mac_rar_pdu_nr* parent_) :
parent(parent_), logger(srslog::fetch_basic_logger("MAC"))
parent(parent_), logger(srslog::fetch_basic_logger("MAC-NR"))
{}
// Return true if subPDU could be parsed correctly, false otherwise
@ -156,7 +156,7 @@ std::string mac_rar_subpdu_nr::to_string()
return ss.str();
}
mac_rar_pdu_nr::mac_rar_pdu_nr() : logger(srslog::fetch_basic_logger("MAC")) {}
mac_rar_pdu_nr::mac_rar_pdu_nr() : logger(srslog::fetch_basic_logger("MAC-NR")) {}
bool mac_rar_pdu_nr::pack()
{

@ -76,7 +76,7 @@ int32_t mac_sch_subpdu_nr::read_subheader(const uint8_t* ptr)
}
sdu = (uint8_t*)ptr;
} else {
srslog::fetch_basic_logger("MAC").warning("Invalid LCID (%d) in MAC PDU", lcid);
srslog::fetch_basic_logger("MAC-NR").warning("Invalid LCID (%d) in MAC PDU", lcid);
return SRSRAN_ERROR;
}
return header_length;
@ -302,14 +302,14 @@ void mac_sch_pdu_nr::pack()
}
}
void mac_sch_pdu_nr::unpack(const uint8_t* payload, const uint32_t& len)
int mac_sch_pdu_nr::unpack(const uint8_t* payload, const uint32_t& len)
{
uint32_t offset = 0;
while (offset < len) {
mac_sch_subpdu_nr sch_pdu(this);
if (sch_pdu.read_subheader(payload + offset) == SRSRAN_ERROR) {
logger.error("Error parsing NR MAC PDU (len=%d, offset=%d)\n", len, offset);
return;
logger.error("Malformed MAC PDU (len=%d, offset=%d)\n", len, offset);
return SRSRAN_ERROR;
}
offset += sch_pdu.get_total_length();
if (sch_pdu.get_lcid() == mac_sch_subpdu_nr::PADDING) {
@ -321,8 +321,11 @@ void mac_sch_pdu_nr::unpack(const uint8_t* payload, const uint32_t& len)
subpdus.push_back(sch_pdu);
}
if (offset != len) {
logger.error("Error parsing NR MAC PDU (len=%d, offset=%d)\n", len, offset);
logger.error("Malformed MAC PDU (len=%d, offset=%d)\n", len, offset);
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
uint32_t mac_sch_pdu_nr::get_num_subpdus()

@ -21,6 +21,7 @@
#include "srsran/mac/pdu_queue.h"
#include "srsran/common/log_helper.h"
#include "srsran/phy/utils/debug.h"
namespace srsran {

@ -32,12 +32,26 @@
/// per frequency resource.
#define NOF_PILOTS_X_FREQ_RES 18
///@brief Maximum number of pilots in a PDCCH candidate location
#define DMRS_PDCCH_MAX_NOF_PILOTS_CANDIDATE \
((SRSRAN_NRE / 3) * (1U << (SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR - 1U)) * 6U)
#define DMRS_PDCCH_INFO_TX(...) INFO("PDCCH DMRS Tx: " __VA_ARGS__)
#define DMRS_PDCCH_INFO_RX(...) INFO("PDCCH DMRS Rx: " __VA_ARGS__)
#define DMRS_PDCCH_DEBUG_RX(...) DEBUG("PDCCH DMRS Rx: " __VA_ARGS__)
/// @brief Enables interpolation at CCE frequency bandwidth to avoid interference with adjacent PDCCH DMRS
#define DMRS_PDCCH_INTERPOLATE_GROUP 1
///@brief Enables synchronization error pre-compensation before group interpolator. It should decrease EVM in expense of
/// computing complexity.
#define DMRS_PDCCH_SYNC_PRECOMPENSATE_INTERP 0
///@brief Enables synchronization error pre-compensation before candidate measurement. It improves detection probability
/// in expense of computing complexity.
#define DMRS_PDCCH_SYNC_PRECOMPENSATE_MEAS 1
///@brief Enables/Disables smoothing filter
#define DMRS_PDCCH_SMOOTH_FILTER 0
static uint32_t dmrs_pdcch_get_cinit(uint32_t slot_idx, uint32_t symbol_idx, uint32_t n_id)
@ -371,21 +385,32 @@ int srsran_dmrs_pdcch_estimate(srsran_dmrs_pdcch_estimator_t* q,
uint32_t group_size = NOF_PILOTS_X_FREQ_RES / q->coreset.duration;
for (uint32_t l = 0; l < q->coreset.duration; l++) {
for (uint32_t j = 0; j < group_count; j++) {
#if DMRS_PDCCH_SMOOTH_FILTER
cf_t tmp[NOF_PILOTS_X_FREQ_RES];
// Copy group into temporal vector
srsran_vec_cf_copy(tmp, &q->lse[l][j * group_size], group_size);
#if DMRS_PDCCH_SYNC_PRECOMPENSATE_INTERP
float sync_err = srsran_vec_estimate_frequency(tmp, group_size);
if (isnormal(sync_err)) {
srsran_vec_apply_cfo(tmp, sync_err, tmp, group_size);
}
#endif // DMRS_PDCCH_SYNC_PRECOMPENSATION
#if DMRS_PDCCH_SMOOTH_FILTER
// Smoothing filter group
srsran_conv_same_cf(&q->lse[l][j * group_size], q->filter, tmp, group_size, q->filter_len);
srsran_interp_linear_offset(
&q->interpolator, tmp, &q->ce[SRSRAN_NRE * q->coreset_bw * l + j * group_size * 4], 1, 3);
#else // DMRS_PDCCH_SMOOTH_FILTER
srsran_interp_linear_offset(&q->interpolator,
&q->lse[l][j * group_size],
&q->ce[SRSRAN_NRE * q->coreset_bw * l + j * group_size * 4],
1,
3);
srsran_conv_same_cf(tmp, q->filter, tmp, group_size, q->filter_len);
#endif // DMRS_PDCCH_SMOOTH_FILTER
// Interpolate group
cf_t* dst = &q->ce[SRSRAN_NRE * q->coreset_bw * l + j * group_size * 4];
srsran_interp_linear_offset(&q->interpolator, tmp, dst, 1, 3);
#if DMRS_PDCCH_SYNC_PRECOMPENSATE_INTERP
if (isnormal(sync_err)) {
srsran_vec_apply_cfo(dst, -sync_err / 4, dst, group_size * 4);
}
#endif // DMRS_PDCCH_SYNC_PRECOMPENSATION
}
}
#else // DMRS_PDCCH_INTERPOLATE_GROUP
@ -421,51 +446,71 @@ int srsran_dmrs_pdcch_get_measure(const srsran_dmrs_pdcch_estimator_t* q,
uint32_t pilot_idx = (dci_location->ncce * 18) / q->coreset.duration;
uint32_t nof_pilots = (L * 18) / q->coreset.duration;
float rsrp = 0.0f;
float epre = 0.0f;
float cfo = 0.0f;
float sync_err = 0.0f;
cf_t corr[SRSRAN_CORESET_DURATION_MAX] = {};
// Initialise measurements
float rsrp = 0.0f; //< Averages linear RSRP
float epre = 0.0f; //< Averages linear EPRE
float cfo_avg_Hz = 0.0f; //< Averages CFO in Radians
float sync_err_avg = 0.0f; //< Averages synchronization
cf_t corr[SRSRAN_CORESET_DURATION_MAX] = {}; //< Saves correlation for the different symbols
// Iterate the CORESET duration
for (uint32_t l = 0; l < q->coreset.duration; l++) {
if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
DMRS_PDCCH_DEBUG_RX("Measuring PDCCH l=%d; lse=", l);
srsran_vec_fprint_c(stdout, &q->lse[l][pilot_idx], nof_pilots);
}
// Measure synchronization error and accumulate for average
float tmp_sync_err = srsran_vec_estimate_frequency(&q->lse[l][pilot_idx], nof_pilots);
sync_err_avg += tmp_sync_err;
#if DMRS_PDCCH_SYNC_PRECOMPENSATE_MEAS
cf_t tmp[DMRS_PDCCH_MAX_NOF_PILOTS_CANDIDATE];
// Pre-compensate synchronization error
srsran_vec_apply_cfo(&q->lse[l][pilot_idx], tmp_sync_err, tmp, nof_pilots);
#else // DMRS_PDCCH_SYNC_PRECOMPENSATE_MEAS
const cf_t* tmp = &q->lse[l][pilot_idx];
#endif // DMRS_PDCCH_SYNC_PRECOMPENSATE_MEAS
// Correlate DMRS
corr[l] = srsran_vec_acc_cc(&q->lse[l][pilot_idx], nof_pilots) / (float)nof_pilots;
corr[l] = srsran_vec_acc_cc(tmp, nof_pilots) / (float)nof_pilots;
// Measure symbol RSRP
rsrp += __real__ corr[l] * __real__ corr[l] + __imag__ corr[l] * __imag__ corr[l];
// Measure symbol EPRE
epre += srsran_vec_avg_power_cf(&q->lse[l][pilot_idx], nof_pilots);
epre += srsran_vec_avg_power_cf(tmp, nof_pilots);
// Measure CFO only from the second and third symbols
if (l != 0) {
cfo += cargf(corr[l] * conjf(corr[l - 1]));
// Calculates the time between the previous and the current symbol
float Ts = srsran_symbol_distance_s(l - 1, l, q->carrier.numerology);
if (isnormal(Ts)) {
// Compute phase difference between symbols and convert to Hz
cfo_avg_Hz += cargf(corr[l] * conjf(corr[l - 1])) / (2.0f * (float)M_PI * Ts);
}
// Measure synchronization error
sync_err += srsran_vec_estimate_frequency(&q->lse[l][pilot_idx], nof_pilots);
}
if (q->coreset.duration > 1) {
cfo /= (float)(q->coreset.duration - 1);
}
// Symbol time, including cyclic prefix. Required for CFO estimation
float Ts = (71.3541666667f / (float)(1 << q->carrier.numerology));
// Store results
measure->rsrp = rsrp / (float)q->coreset.duration;
measure->epre = epre / (float)q->coreset.duration;
measure->cfo_hz = cfo / (2.0f * (float)M_PI * Ts);
if (q->coreset.duration > 1) {
// Protected zero division
measure->cfo_hz /= (float)(q->coreset.duration - 1);
} else {
// There are not enough symbols for computing CFO, set to NAN
measure->cfo_hz = NAN;
}
measure->sync_error_us =
sync_err / (4.0e-6f * (float)q->coreset.duration * SRSRAN_SUBC_SPACING_NR(q->carrier.numerology));
sync_err_avg / (4.0e-6f * (float)q->coreset.duration * SRSRAN_SUBC_SPACING_NR(q->carrier.numerology));
// Convert power measurements into logarithmic scale
measure->rsrp_dBfs = srsran_convert_power_to_dB(measure->rsrp);
measure->epre_dBfs = srsran_convert_power_to_dB(measure->epre);
// Store DMRS correlation
if (isnormal(measure->rsrp) && isnormal(measure->epre)) {
measure->norm_corr = measure->rsrp / measure->epre;
} else {

@ -28,6 +28,26 @@
#define SRSRAN_DMRS_SCH_TYPEA_SINGLE_DURATION_MIN 3
#define SRSRAN_DMRS_SCH_TYPEA_DOUBLE_DURATION_MIN 4
/**
* @brief Set to 1 for synchronization error pre-compensation before interpolator
*/
#define DMRS_SCH_SYNC_PRECOMPENSATE 1
/**
* @brief Set to 1 for CFO error pre-compensation before interpolator
*/
#define DMRS_SCH_CFO_PRECOMPENSATE 1
/**
* @brief Set Smoothing filter length, set to 0 for disabling. The recommended value is 5.
*/
#define DMRS_SCH_SMOOTH_FILTER_LEN 5
/**
* @brief Set smoothing filter (gaussian) standard deviation
*/
#define DMRS_SCH_SMOOTH_FILTER_STDDEV 2
int srsran_dmrs_sch_cfg_to_str(const srsran_dmrs_sch_cfg_t* cfg, char* msg, uint32_t max_len)
{
int type = (int)cfg->type + 1;
@ -512,10 +532,22 @@ int srsran_dmrs_sch_init(srsran_dmrs_sch_t* q, bool is_rx)
return SRSRAN_ERROR_INVALID_INPUTS;
}
SRSRAN_MEM_ZERO(q, srsran_dmrs_sch_t, 1);
if (is_rx) {
q->is_rx = true;
}
#if DMRS_SCH_SMOOTH_FILTER_LEN
if (q->filter == NULL) {
q->filter = srsran_vec_f_malloc(DMRS_SCH_SMOOTH_FILTER_LEN);
if (q->filter == NULL) {
return SRSRAN_ERROR;
}
srsran_chest_set_smooth_filter_gauss(q->filter, DMRS_SCH_SMOOTH_FILTER_LEN - 1, 2);
}
#endif // DMRS_SCH_SMOOTH_FILTER_LEN
return SRSRAN_SUCCESS;
}
@ -533,6 +565,9 @@ void srsran_dmrs_sch_free(srsran_dmrs_sch_t* q)
if (q->temp) {
free(q->temp);
}
if (q->filter) {
free(q->filter);
}
SRSRAN_MEM_ZERO(q, srsran_dmrs_sch_t, 1);
}
@ -731,9 +766,16 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q,
return SRSRAN_ERROR;
}
// Get DMRS reserved RE pattern
srsran_re_pattern_t dmrs_pattern = {};
if (srsran_dmrs_sch_rvd_re_pattern(dmrs_cfg, grant, &dmrs_pattern) < SRSRAN_SUCCESS) {
ERROR("Error computing DMRS Reserved Re pattern");
return SRSRAN_ERROR;
}
uint32_t nof_pilots_x_symbol = 0;
// Iterate symbols
// Iterate symbols and extract LSE estimates
for (uint32_t i = 0; i < nof_symbols; i++) {
uint32_t l = symbols[i]; // Symbol index inside the slot
@ -749,6 +791,27 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q,
}
}
// Estimate average synchronization error
float dmrs_stride = (dmrs_cfg->type == srsran_dmrs_sch_type_1) ? 2 : 3;
float sync_err = 0.0f;
for (uint32_t i = 0; i < nof_symbols; i++) {
sync_err += srsran_vec_estimate_frequency(&q->pilot_estimates[nof_pilots_x_symbol * i], nof_pilots_x_symbol);
}
sync_err /= (float)nof_symbols;
chest_res->sync_error = sync_err / (dmrs_stride * SRSRAN_SUBC_SPACING_NR(q->carrier.numerology));
#if DMRS_SCH_SYNC_PRECOMPENSATE
// Pre-compensate synchronization error
if (isnormal(sync_err)) {
for (uint32_t i = 0; i < nof_symbols; i++) {
srsran_vec_apply_cfo(&q->pilot_estimates[nof_pilots_x_symbol * i],
sync_err,
&q->pilot_estimates[nof_pilots_x_symbol * i],
nof_pilots_x_symbol);
}
}
#endif // DMRS_SCH_SYNC_ERROR_PRECOMPENSATE
// Perform Power measurements
float rsrp = 0.0f;
float epre = 0.0f;
@ -783,6 +846,37 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q,
}
chest_res->cfo = cfo_avg;
#if DMRS_SCH_CFO_PRECOMPENSATE
// Pre-compensate CFO
cf_t cfo_correction[SRSRAN_NSYMB_PER_SLOT_NR] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
if (isnormal(cfo_avg)) {
// Calculate phase of the first OFDM symbol (l = 0)
float arg0 =
cargf(corr[0]) - 2.0f * M_PI * srsran_symbol_distance_s(0, symbols[0], q->carrier.numerology) * cfo_avg;
// Calculate CFO corrections
for (uint32_t l = 0; l < SRSRAN_NSYMB_PER_SLOT_NR; l++) {
float arg = arg0 + 2.0f * M_PI * cfo_avg * srsran_symbol_distance_s(0, l, q->carrier.numerology);
cfo_correction[l] = cexpf(I * arg);
}
// Remove CFO phases
for (uint32_t i = 0; i < nof_symbols; i++) {
uint32_t l = symbols[i];
srsran_vec_sc_prod_ccc(&q->pilot_estimates[nof_pilots_x_symbol * i],
conjf(cfo_correction[l]),
&q->pilot_estimates[nof_pilots_x_symbol * i],
nof_pilots_x_symbol);
}
}
#endif // DMRS_SCH_CFO_PRECOMPENSATE
INFO("PDSCH-DMRS: RSRP=%+.2fdB EPRE=%+.2fdB CFO=%+.0fHz Sync=%.3fus",
chest_res->rsrp_dbm,
srsran_convert_power_to_dB(epre),
cfo_avg,
chest_res->sync_error * 1e6);
// Average over time, only if more than one DMRS symbol
for (uint32_t i = 1; i < nof_symbols; i++) {
srsran_vec_sum_ccc(
@ -792,6 +886,12 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q,
srsran_vec_sc_prod_cfc(q->pilot_estimates, 1.0f / (float)nof_symbols, q->pilot_estimates, nof_pilots_x_symbol);
}
#if DMRS_SCH_SMOOTH_FILTER_LEN
// Apply smoothing filter
srsran_conv_same_cf(
q->pilot_estimates, q->filter, q->pilot_estimates, nof_pilots_x_symbol, DMRS_SCH_SMOOTH_FILTER_LEN);
#endif // DMRS_SCH_SMOOTH_FILTER_LEN
// Frequency domain interpolate
uint32_t nof_re_x_symbol =
(dmrs_cfg->type == srsran_dmrs_sch_type_1) ? nof_pilots_x_symbol * 2 : nof_pilots_x_symbol * 3;
@ -816,23 +916,31 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q,
srsran_interp_linear_offset(&q->interpolator_type2, q->pilot_estimates, ce, delta, 3 - delta);
}
#if DMRS_SCH_SYNC_PRECOMPENSATE
// Remove synchronization error pre-compensation
if (isnormal(sync_err)) {
srsran_vec_apply_cfo(ce, -sync_err / dmrs_stride, ce, nof_re_x_symbol);
}
#endif // DMRS_SCH_SYNC_ERROR_PRECOMPENSATE
// Time domain hold, extract resource elements estimates for PDSCH
uint32_t symbol_idx = 0;
uint32_t count = 0;
for (uint32_t l = grant->S; l < grant->S + grant->L; l++) {
while (symbols[symbol_idx] < l && symbol_idx < nof_symbols - 1) {
symbol_idx++;
}
// Initialise reserved mask
bool rvd_mask_wb[SRSRAN_NRE * SRSRAN_MAX_PRB_NR] = {};
// Compute reserved RE
// Compute reserved RE mask by procedures
if (srsran_re_pattern_list_to_symbol_mask(&cfg->rvd_re, l, rvd_mask_wb) < SRSRAN_SUCCESS) {
ERROR("Error generating reserved RE mask");
return SRSRAN_ERROR;
}
// Compute reserved RE mask for DMRS
if (srsran_re_pattern_to_symbol_mask(&dmrs_pattern, l, rvd_mask_wb) < SRSRAN_SUCCESS) {
ERROR("Error generating reserved RE mask");
return SRSRAN_ERROR;
}
// Narrow reserved subcarriers to the ones used in the transmission
bool rvd_mask[SRSRAN_NRE * SRSRAN_MAX_PRB_NR] = {};
for (uint32_t i = 0, k = 0; i < q->carrier.nof_prb; i++) {
@ -843,40 +951,13 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q,
}
}
// Check if it s DMRS symbol
if (symbols[symbol_idx] == l) {
switch (dmrs_cfg->type) {
case srsran_dmrs_sch_type_1:
// Skip if there is no data to read
if (grant->nof_dmrs_cdm_groups_without_data != 1) {
continue;
}
for (uint32_t i = 1; i < nof_re_x_symbol; i += 2) {
if (!rvd_mask[i]) {
chest_res->ce[0][0][count++] = ce[i];
}
}
break;
case srsran_dmrs_sch_type_2:
// Skip if there is no data to read
if (grant->nof_dmrs_cdm_groups_without_data != 1 && grant->nof_dmrs_cdm_groups_without_data != 2) {
continue;
}
for (uint32_t i = grant->nof_dmrs_cdm_groups_without_data * 2; i < nof_re_x_symbol; i += 6) {
uint32_t nof_re = (3 - grant->nof_dmrs_cdm_groups_without_data) * 2;
for (uint32_t j = 0; j < nof_re; j++) {
if (!rvd_mask[i + j]) {
chest_res->ce[0][0][count++] = ce[i + j];
}
}
}
break;
}
} else {
for (uint32_t i = 0; i < nof_re_x_symbol; i++) {
if (!rvd_mask[i]) {
#if DMRS_SCH_CFO_PRECOMPENSATE
chest_res->ce[0][0][count++] = ce[i] * cfo_correction[l];
#else // DMRS_SCH_CFO_PRECOMPENSATE
chest_res->ce[0][0][count++] = ce[i];
}
#endif // DMRS_SCH_CFO_PRECOMPENSATE
}
}
}

@ -107,7 +107,7 @@ static int run_test(srsran_dmrs_pdcch_estimator_t* estimator,
}
TESTASSERT(fabsf(measure.epre - 1.0f) < 1e-3f);
TESTASSERT(fabsf(measure.rsrp - 1.0f) < 1e-3f);
TESTASSERT(fabsf(measure.cfo_hz) < 1e-3f);
TESTASSERT(coreset->duration == 1 || fabsf(measure.cfo_hz) < 1e-3f);
TESTASSERT(fabsf(measure.sync_error_us) < 1e-3f);
TESTASSERT(srsran_dmrs_pdcch_get_ce(estimator, &dci_location, ce) == SRSRAN_SUCCESS);

@ -188,10 +188,16 @@ float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology)
bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx)
{
// Protect NULL pointer access
if (cfg == NULL) {
return false;
}
// Prevent zero division
if (cfg->pattern1.period_ms == 0 && cfg->pattern2.period_ms == 0) {
return false;
}
// Calculate slot index within the TDD overall period
uint32_t slot_x_ms = 1U << numerology; // Number of slots per millisecond
uint32_t period_sum = (cfg->pattern1.period_ms + cfg->pattern2.period_ms) * slot_x_ms; // Total perdiod sum
@ -204,16 +210,23 @@ bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology,
slot_idx_period -= cfg->pattern1.period_ms * slot_x_ms; // Remove pattern 1 offset
}
// Check DL boundaries
return (slot_idx_period < pattern->nof_dl_slots ||
(slot_idx_period == pattern->nof_dl_slots && pattern->nof_dl_symbols != 0));
}
bool srsran_tdd_nr_is_ul(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx)
{
// Protect NULL pointer access
if (cfg == NULL) {
return false;
}
// Prevent zero division
if (cfg->pattern1.period_ms == 0 && cfg->pattern2.period_ms == 0) {
return false;
}
// Calculate slot index within the TDD overall period
uint32_t slot_x_ms = 1U << numerology; // Number of slots per millisecond
uint32_t period_sum = (cfg->pattern1.period_ms + cfg->pattern2.period_ms) * slot_x_ms; // Total perdiod sum
@ -229,5 +242,6 @@ bool srsran_tdd_nr_is_ul(const srsran_tdd_config_nr_t* cfg, uint32_t numerology,
// Calculate slot in which UL starts
uint32_t start_ul = (pattern->period_ms * slot_x_ms - pattern->nof_ul_slots) - 1;
// Check UL boundaries
return (slot_idx_period > start_ul || (slot_idx_period == start_ul && pattern->nof_ul_symbols != 0));
}

@ -23,6 +23,10 @@
#include "srsran/phy/utils/bit.h"
#include "srsran/phy/utils/debug.h"
#ifdef LV_HAVE_SSE
#include <immintrin.h>
#endif // LV_HAVE_SSE
static void gen_crc_table(srsran_crc_t* h)
{
uint32_t pad = (h->order < 8) ? (8 - h->order) : 0;
@ -113,7 +117,18 @@ uint32_t srsran_crc_checksum(srsran_crc_t* h, uint8_t* data, int len)
byte |= ((uint8_t) * (pter + k)) << (7 - k);
}
} else {
#ifdef LV_HAVE_SSE
// Get 8 Bit
__m64 mask = _mm_cmpgt_pi8(*((__m64*)pter), _mm_set1_pi8(0));
// Reverse
mask = _mm_shuffle_pi8(mask, _mm_set_pi8(0, 1, 2, 3, 4, 5, 6, 7));
// Get mask and write
byte = (uint8_t)_mm_movemask_pi8(mask);
#else /* LV_HAVE_SSE */
byte = (uint8_t)(srsran_bit_pack(&pter, 8) & 0xFF);
#endif /* LV_HAVE_SSE */
}
srsran_crc_checksum_put_byte(h, byte);
}
@ -168,3 +183,11 @@ uint32_t srsran_crc_attach(srsran_crc_t* h, uint8_t* data, int len)
srsran_bit_unpack(checksum, &ptr, h->order);
return checksum;
}
bool srsran_crc_match(srsran_crc_t* h, uint8_t* data, int len)
{
uint8_t* ptr = &data[len];
uint32_t checksum1 = srsran_crc_checksum(h, data, len);
uint32_t checksum2 = srsran_bit_pack(&ptr, h->order);
return (checksum1 == checksum2);
}

@ -241,9 +241,7 @@ int extract_ldpc_message_c_avx512(void* p, uint8_t* message, uint16_t liftK)
int ini = 0;
for (int i = 0; i < liftK; i = i + vp->ls) {
for (int k = 0; k < vp->ls; k++) {
message[i + k] = (vp->soft_bits.c[ini + k] < 0);
}
fec_avx512_hard_decision_c(&vp->soft_bits.c[ini], &message[i], vp->ls);
ini = ini + SRSRAN_AVX512_B_SIZE;
}

@ -301,9 +301,7 @@ int extract_ldpc_message_c_avx512long(void* p, uint8_t* message, uint16_t liftK)
int ini = 0;
for (int i = 0; i < liftK; i = i + vp->ls) {
for (int k = 0; k < vp->ls; k++) {
message[i + k] = (vp->soft_bits->c[ini + k] < 0);
}
fec_avx512_hard_decision_c(&vp->soft_bits->c[ini], &message[i], vp->ls);
ini = ini + vp->node_size;
}

@ -39,7 +39,119 @@
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#define MAX_ITERATIONS 10 /*!< \brief Iterations of the BP algorithm. */
#define LDPC_DECODER_DEFAULT_MAX_NOF_ITER 10 /*!< \brief Default maximum number of iterations of the BP algorithm. */
#define LDPC_DECODER_TEMPLATE(LLR_TYPE, SUFFIX) \
static int decode_##SUFFIX( \
void* o, const LLR_TYPE* llrs, uint8_t* message, uint32_t cdwd_rm_length, srsran_crc_t* crc) \
{ \
srsran_ldpc_decoder_t* q = o; \
\
/* it must be smaller than the codeword size */ \
if (cdwd_rm_length > q->liftN - 2 * q->ls) { \
cdwd_rm_length = q->liftN - 2 * q->ls; \
} \
/* We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,*/ \
/* 2 variable nodes are systematically punctured by the encoder. */ \
if (cdwd_rm_length < (q->bgK + 2) * q->ls) { \
/* ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");*/ \
cdwd_rm_length = (q->bgK + 2) * q->ls; \
/* return -1;*/ \
} \
if (cdwd_rm_length % q->ls) { \
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls; \
/* ERROR("The rate-matched codeword length should be a multiple of the lifting size."); */ \
/* return -1;*/ \
} \
init_ldpc_dec_##SUFFIX(q->ptr, llrs, q->ls); \
\
uint16_t* this_pcm = NULL; \
int8_t(*these_var_indices)[MAX_CNCT] = NULL; \
\
/* When computing the number of layers, we need to recall that the standard always removes */ \
/* the first two variable nodes from the final codeword.*/ \
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2; \
\
for (int i_iteration = 0; i_iteration < q->max_nof_iter; i_iteration++) { \
for (int i_layer = 0; i_layer < n_layers; i_layer++) { \
update_ldpc_var_to_check_##SUFFIX(q->ptr, i_layer); \
\
this_pcm = q->pcm + i_layer * q->bgN; \
these_var_indices = q->var_indices + i_layer; \
\
update_ldpc_check_to_var_##SUFFIX(q->ptr, i_layer, this_pcm, these_var_indices); \
\
update_ldpc_soft_bits_##SUFFIX(q->ptr, i_layer, these_var_indices); \
} \
\
if (crc != NULL) { \
extract_ldpc_message_##SUFFIX(q->ptr, message, q->liftK); \
\
if (srsran_crc_match(crc, message, q->liftK - crc->order)) { \
return i_iteration + 1; \
} \
} \
} \
\
/* If reached here, and CRC is being checked, it has failed */ \
if (crc != NULL) { \
return 0; \
} \
\
/* Without CRC, extract message and return the maximum number of iterations */ \
extract_ldpc_message_##SUFFIX(q->ptr, message, q->liftK); \
return q->max_nof_iter; \
}
#define LDPC_DECODER_TEMPLATE_FLOOD(LLR_TYPE, SUFFIX) \
static int decode_##SUFFIX( \
void* o, const LLR_TYPE* llrs, uint8_t* message, uint32_t cdwd_rm_length, srsran_crc_t* crc) \
{ \
srsran_ldpc_decoder_t* q = o; \
\
/* it must be smaller than the codeword size */ \
if (cdwd_rm_length > q->liftN - 2 * q->ls) { \
cdwd_rm_length = q->liftN - 2 * q->ls; \
} \
/* We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,*/ \
/* 2 variable nodes are systematically punctured by the encoder. */ \
if (cdwd_rm_length < (q->bgK + 2) * q->ls) { \
/* ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");*/ \
cdwd_rm_length = (q->bgK + 2) * q->ls; \
/* return -1;*/ \
} \
if (cdwd_rm_length % q->ls) { \
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls; \
/* ERROR("The rate-matched codeword length should be a multiple of the lifting size."); */ \
/* return -1;*/ \
} \
init_ldpc_dec_##SUFFIX(q->ptr, llrs, q->ls); \
\
uint16_t* this_pcm = NULL; \
int8_t(*these_var_indices)[MAX_CNCT] = NULL; \
\
/* When computing the number of layers, we need to recall that the standard always removes */ \
/* the first two variable nodes from the final codeword.*/ \
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2; \
\
for (int i_iteration = 0; i_iteration < 2 * q->max_nof_iter; i_iteration++) { \
for (int i_layer = 0; i_layer < n_layers; i_layer++) { \
update_ldpc_var_to_check_##SUFFIX(q->ptr, i_layer); \
} \
\
for (int i_layer = 0; i_layer < n_layers; i_layer++) { \
this_pcm = q->pcm + i_layer * q->bgN; \
these_var_indices = q->var_indices + i_layer; \
\
update_ldpc_check_to_var_##SUFFIX(q->ptr, i_layer, this_pcm, these_var_indices); \
} \
\
update_ldpc_soft_bits_##SUFFIX(q->ptr, q->var_indices); \
} \
\
extract_ldpc_message_##SUFFIX(q->ptr, message, q->liftK); \
\
return q->max_nof_iter; \
}
/*! Carries out the actual destruction of the memory allocated to the decoder, float-LLR case. */
static void free_dec_f(void* o)
@ -55,52 +167,7 @@ static void free_dec_f(void* o)
}
/*! Carries out the decoding with real-valued LLRs. */
static int decode_f(void* o, const float* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_f(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_f(q->ptr, i_layer);
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_f(q->ptr, i_layer, this_pcm, these_var_indices);
update_ldpc_soft_bits_f(q->ptr, i_layer, these_var_indices);
}
}
extract_ldpc_message_f(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE(float, f)
/*! Initializes the decoder to work with real valued LLRs. */
static int init_f(srsran_ldpc_decoder_t* q)
@ -132,53 +199,7 @@ static void free_dec_s(void* o)
}
/*! Carries out the decoding with 16-bit integer-valued LLRs. */
static int decode_s(void* o, const int16_t* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
// it must be smaller than the codeword size
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_s(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_s(q->ptr, i_layer);
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_s(q->ptr, i_layer, this_pcm, these_var_indices);
update_ldpc_soft_bits_s(q->ptr, i_layer, these_var_indices);
}
}
extract_ldpc_message_s(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE(int16_t, s)
/*! Initializes the decoder to work with 16-bit integer-valued LLRs. */
static int init_s(srsran_ldpc_decoder_t* q)
@ -210,53 +231,7 @@ static void free_dec_c(void* o)
}
/*! Carries out the decoding with 8-bit integer-valued LLRs. */
static int decode_c(void* o, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
// it must be smaller than the codeword size
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_c(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_c(q->ptr, i_layer);
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_c(q->ptr, i_layer, this_pcm, these_var_indices);
update_ldpc_soft_bits_c(q->ptr, i_layer, these_var_indices);
}
}
extract_ldpc_message_c(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE(int8_t, c)
/*! Initializes the decoder to work with 8-bit integer-valued LLRs. */
static int init_c(srsran_ldpc_decoder_t* q)
@ -288,53 +263,7 @@ static void free_dec_c_flood(void* o)
}
/*! Carries out the decoding with 8-bit integer-valued LLRs, flooded scheduling. */
static int decode_c_flood(void* o, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
// it must be smaller than the codeword size
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_c_flood(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < 2 * MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_c_flood(q->ptr, i_layer);
}
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_c_flood(q->ptr, i_layer, this_pcm, these_var_indices);
}
update_ldpc_soft_bits_c_flood(q->ptr, q->var_indices);
}
extract_ldpc_message_c_flood(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE_FLOOD(int8_t, c_flood);
/*! Initializes the decoder to work with 8-bit integer-valued LLRs. */
static int init_c_flood(srsran_ldpc_decoder_t* q)
@ -367,52 +296,7 @@ static void free_dec_c_avx2(void* o)
}
/*! Carries out the decoding with 8-bit integer-valued LLRs (AVX2 implementation). */
static int decode_c_avx2(void* o, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
// it must be smaller than the codeword size
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_c_avx2(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_c_avx2(q->ptr, i_layer);
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_c_avx2(q->ptr, i_layer, this_pcm, these_var_indices);
update_ldpc_soft_bits_c_avx2(q->ptr, i_layer, these_var_indices);
}
}
extract_ldpc_message_c_avx2(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE(int8_t, c_avx2);
/*! Initializes the decoder to work with 8-bit integer-valued LLRs (AVX2 implementation). */
static int init_c_avx2(srsran_ldpc_decoder_t* q)
@ -445,52 +329,7 @@ static void free_dec_c_avx2long(void* o)
}
/*! Carries out the decoding with 8-bit integer-valued LLRs (AVX2 implementation, large lifting size). */
static int decode_c_avx2long(void* o, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
// it must be smaller than the codeword size
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_c_avx2long(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_c_avx2long(q->ptr, i_layer);
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_c_avx2long(q->ptr, i_layer, this_pcm, these_var_indices);
update_ldpc_soft_bits_c_avx2long(q->ptr, i_layer, these_var_indices);
}
}
extract_ldpc_message_c_avx2long(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE(int8_t, c_avx2long);
/*! Initializes the decoder to work with 8-bit integer-valued LLRs (AVX2 implementation, large lifting size). */
static int init_c_avx2long(srsran_ldpc_decoder_t* q)
@ -523,53 +362,7 @@ static void free_dec_c_avx2_flood(void* o)
}
/*! Carries out the decoding with 8-bit integer-valued LLRs (AVX2 implementation, flooded scheduling). */
static int decode_c_avx2_flood(void* o, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
// it must be smaller than the codeword size
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_c_avx2_flood(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < 2 * MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_c_avx2_flood(q->ptr, i_layer);
}
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_c_avx2_flood(q->ptr, i_layer, this_pcm, these_var_indices);
}
update_ldpc_soft_bits_c_avx2_flood(q->ptr, q->var_indices);
}
extract_ldpc_message_c_avx2_flood(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE_FLOOD(int8_t, c_avx2_flood);
/*! Initializes the decoder to work with 8-bit integer-valued LLRs (AVX2 implementation, flooded scheduling). */
static int init_c_avx2_flood(srsran_ldpc_decoder_t* q)
@ -603,54 +396,7 @@ static void free_dec_c_avx2long_flood(void* o)
/*! Carries out the decoding with 8-bit integer-valued LLRs (flooded scheduling, AVX2 implementation, large lifting
* size). */
static int decode_c_avx2long_flood(void* o, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
// it must be smaller than the codeword size
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_c_avx2long_flood(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < 2 * MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_c_avx2long_flood(q->ptr, i_layer);
}
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_c_avx2long_flood(q->ptr, i_layer, this_pcm, these_var_indices);
}
update_ldpc_soft_bits_c_avx2long_flood(q->ptr, q->var_indices);
}
extract_ldpc_message_c_avx2long_flood(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE_FLOOD(int8_t, c_avx2long_flood)
/*! Initializes the decoder to work with 8-bit integer-valued LLRs
* (flooded scheduling, AVX2 implementation, large lifting size). */
@ -689,52 +435,7 @@ static void free_dec_c_avx512(void* o)
}
/*! Carries out the decoding with 8-bit integer-valued LLRs (AVX512 implementation). */
static int decode_c_avx512(void* o, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
// it must be smaller than the codeword size
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_c_avx512(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_c_avx512(q->ptr, i_layer);
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_c_avx512(q->ptr, i_layer, this_pcm, these_var_indices);
update_ldpc_soft_bits_c_avx512(q->ptr, i_layer, these_var_indices);
}
}
extract_ldpc_message_c_avx512(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE(int8_t, c_avx512)
/*! Initializes the decoder to work with 8-bit integer-valued LLRs (AVX512 implementation). */
static int init_c_avx512(srsran_ldpc_decoder_t* q)
@ -767,52 +468,7 @@ static void free_dec_c_avx512long(void* o)
}
/*! Carries out the decoding with 8-bit integer-valued LLRs (AVX512 implementation, large lifting size). */
static int decode_c_avx512long(void* o, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
// it must be smaller than the codeword size
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_c_avx512long(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_c_avx512long(q->ptr, i_layer);
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_c_avx512long(q->ptr, i_layer, this_pcm, these_var_indices);
update_ldpc_soft_bits_c_avx512long(q->ptr, i_layer, these_var_indices);
}
}
extract_ldpc_message_c_avx512long(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE(int8_t, c_avx512long)
/*! Initializes the decoder to work with 8-bit integer-valued LLRs (AVX512 implementation, large lifting size). */
static int init_c_avx512long(srsran_ldpc_decoder_t* q)
@ -846,54 +502,7 @@ static void free_dec_c_avx512long_flood(void* o)
/*! Carries out the decoding with 8-bit integer-valued LLRs (flooded scheduling, AVX512 implementation, large lifting
* size). */
static int decode_c_avx512long_flood(void* o, const int8_t* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
srsran_ldpc_decoder_t* q = o;
// it must be smaller than the codeword size
if (cdwd_rm_length > q->liftN - 2 * q->ls) {
cdwd_rm_length = q->liftN - 2 * q->ls;
}
// We need at least q->bgK + 4 variable nodes to cover the high-rate region. However,
// 2 variable nodes are systematically punctured by the encoder.
if (cdwd_rm_length < (q->bgK + 2) * q->ls) {
// ERROR("The rate-matched codeword should have a length at least equal to the high-rate region.");
cdwd_rm_length = (q->bgK + 2) * q->ls;
// return -1;
}
if (cdwd_rm_length % q->ls) {
cdwd_rm_length = (cdwd_rm_length / q->ls + 1) * q->ls;
// ERROR("The rate-matched codeword length should be a multiple of the lifting size.");
// return -1;
}
init_ldpc_dec_c_avx512long_flood(q->ptr, llrs, q->ls);
uint16_t* this_pcm = NULL;
int8_t(*these_var_indices)[MAX_CNCT] = NULL;
// When computing the number of layers, we need to recall that the standard always removes
// the first two variable nodes from the final codeword.
uint8_t n_layers = cdwd_rm_length / q->ls - q->bgK + 2;
for (int i_iteration = 0; i_iteration < 2 * MAX_ITERATIONS; i_iteration++) {
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
update_ldpc_var_to_check_c_avx512long_flood(q->ptr, i_layer);
}
for (int i_layer = 0; i_layer < n_layers; i_layer++) {
this_pcm = q->pcm + i_layer * q->bgN;
these_var_indices = q->var_indices + i_layer;
update_ldpc_check_to_var_c_avx512long_flood(q->ptr, i_layer, this_pcm, these_var_indices);
}
update_ldpc_soft_bits_c_avx512long_flood(q->ptr, q->var_indices);
}
extract_ldpc_message_c_avx512long_flood(q->ptr, message, q->liftK);
return 0;
}
LDPC_DECODER_TEMPLATE_FLOOD(int8_t, c_avx512long_flood)
/*! Initializes the decoder to work with 8-bit integer-valued LLRs
* (flooded scheduling, AVX512 implementation, large lifting size). */
@ -914,14 +523,19 @@ static int init_c_avx512long_flood(srsran_ldpc_decoder_t* q)
#endif // LV_HAVE_AVX512
int srsran_ldpc_decoder_init(srsran_ldpc_decoder_t* q,
srsran_ldpc_decoder_type_t type,
srsran_basegraph_t bg,
uint16_t ls,
float scaling_fctr)
int srsran_ldpc_decoder_init(srsran_ldpc_decoder_t* q, const srsran_ldpc_decoder_args_t* args)
{
int ls_index = get_ls_index(ls);
if (q == NULL || args == NULL) {
return -1;
}
// Extract configuration arguments
uint16_t ls = args->ls;
srsran_basegraph_t bg = args->bg;
float scaling_fctr = args->scaling_fctr;
srsran_ldpc_decoder_type_t type = args->type;
int ls_index = get_ls_index(ls);
if (ls_index == VOID_LIFTSIZE) {
ERROR("Invalid lifting size %d", ls);
return -1;
@ -948,6 +562,8 @@ int srsran_ldpc_decoder_init(srsran_ldpc_decoder_t* q,
q->liftM = ls * q->bgM;
q->liftN = ls * q->bgN;
q->max_nof_iter = (args->max_nof_iter == 0) ? LDPC_DECODER_DEFAULT_MAX_NOF_ITER : args->max_nof_iter;
q->pcm = srsran_vec_u16_malloc(q->bgM * q->bgN);
if (!q->pcm) {
perror("malloc");
@ -1026,7 +642,7 @@ void srsran_ldpc_decoder_free(srsran_ldpc_decoder_t* q)
int srsran_ldpc_decoder_decode_f(srsran_ldpc_decoder_t* q, const float* llrs, uint8_t* message, uint32_t cdwd_rm_length)
{
return q->decode_f(q, llrs, message, cdwd_rm_length);
return q->decode_f(q, llrs, message, cdwd_rm_length, NULL);
}
int srsran_ldpc_decoder_decode_s(srsran_ldpc_decoder_t* q,
@ -1034,7 +650,7 @@ int srsran_ldpc_decoder_decode_s(srsran_ldpc_decoder_t* q,
uint8_t* message,
uint32_t cdwd_rm_length)
{
return q->decode_s(q, llrs, message, cdwd_rm_length);
return q->decode_s(q, llrs, message, cdwd_rm_length, NULL);
}
int srsran_ldpc_decoder_decode_c(srsran_ldpc_decoder_t* q,
@ -1042,5 +658,14 @@ int srsran_ldpc_decoder_decode_c(srsran_ldpc_decoder_t* q,
uint8_t* message,
uint32_t cdwd_rm_length)
{
return q->decode_c(q, llrs, message, cdwd_rm_length);
return q->decode_c(q, llrs, message, cdwd_rm_length, NULL);
}
int srsran_ldpc_decoder_decode_crc_c(srsran_ldpc_decoder_t* q,
const int8_t* llrs,
uint8_t* message,
uint32_t cdwd_rm_length,
srsran_crc_t* crc)
{
return q->decode_c(q, llrs, message, cdwd_rm_length, crc);
}

@ -177,42 +177,53 @@ int main(int argc, char** argv)
#endif // LV_HAVE_AVX2
#endif // LV_HAVE_AVX512
// Create LDPC configuration arguments
srsran_ldpc_decoder_args_t decoder_args = {};
decoder_args.bg = base_graph;
decoder_args.ls = lift_size;
decoder_args.scaling_fctr = MS_SF;
// create an LDPC decoder (float)
srsran_ldpc_decoder_t decoder_f;
if (srsran_ldpc_decoder_init(&decoder_f, SRSRAN_LDPC_DECODER_F, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_F;
if (srsran_ldpc_decoder_init(&decoder_f, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
// create an LDPC decoder (16 bit)
srsran_ldpc_decoder_t decoder_s;
if (srsran_ldpc_decoder_init(&decoder_s, SRSRAN_LDPC_DECODER_S, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_S;
if (srsran_ldpc_decoder_init(&decoder_s, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
// create an LDPC decoder (8 bit)
srsran_ldpc_decoder_t decoder_c;
if (srsran_ldpc_decoder_init(&decoder_c, SRSRAN_LDPC_DECODER_C, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C;
if (srsran_ldpc_decoder_init(&decoder_c, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
// create an LDPC decoder (8 bit, flooded)
srsran_ldpc_decoder_t decoder_c_flood;
if (srsran_ldpc_decoder_init(&decoder_c_flood, SRSRAN_LDPC_DECODER_C_FLOOD, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C_FLOOD;
if (srsran_ldpc_decoder_init(&decoder_c_flood, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
#ifdef LV_HAVE_AVX2
// create an LDPC decoder (8 bit, AVX2 version)
srsran_ldpc_decoder_t decoder_avx;
if (srsran_ldpc_decoder_init(&decoder_avx, SRSRAN_LDPC_DECODER_C_AVX2, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX2;
if (srsran_ldpc_decoder_init(&decoder_avx, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
// create an LDPC decoder (8 bit, flooded scheduling, AVX2 version)
srsran_ldpc_decoder_t decoder_avx_flood;
if (srsran_ldpc_decoder_init(&decoder_avx_flood, SRSRAN_LDPC_DECODER_C_AVX2_FLOOD, base_graph, lift_size, MS_SF) !=
0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX2_FLOOD;
if (srsran_ldpc_decoder_init(&decoder_avx_flood, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
@ -221,15 +232,16 @@ int main(int argc, char** argv)
#ifdef LV_HAVE_AVX512
// create an LDPC decoder (8 bit, AVX512 version)
srsran_ldpc_decoder_t decoder_avx512;
if (srsran_ldpc_decoder_init(&decoder_avx512, SRSRAN_LDPC_DECODER_C_AVX512, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX512;
if (srsran_ldpc_decoder_init(&decoder_avx512, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
// create an LDPC decoder (8 bit, flooded scheduling, AVX512 version)
srsran_ldpc_decoder_t decoder_avx512_flood;
if (srsran_ldpc_decoder_init(
&decoder_avx512_flood, SRSRAN_LDPC_DECODER_C_AVX512_FLOOD, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX512_FLOOD;
if (srsran_ldpc_decoder_init(&decoder_avx512_flood, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}

@ -162,9 +162,16 @@ int main(int argc, char** argv)
srsran_ldpc_decoder_type_t dectype =
(scheduling == 0) ? SRSRAN_LDPC_DECODER_C_AVX2 : SRSRAN_LDPC_DECODER_C_AVX2_FLOOD;
// Create LDPC configuration arguments
srsran_ldpc_decoder_args_t decoder_args = {};
decoder_args.type = dectype;
decoder_args.bg = base_graph;
decoder_args.ls = lift_size;
decoder_args.scaling_fctr = 1.0f;
// create an LDPC decoder
srsran_ldpc_decoder_t decoder;
if (srsran_ldpc_decoder_init(&decoder, dectype, base_graph, lift_size, 1) != 0) {
if (srsran_ldpc_decoder_init(&decoder, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}

@ -161,9 +161,16 @@ int main(int argc, char** argv)
srsran_ldpc_decoder_type_t dectype =
(scheduling == 0) ? SRSRAN_LDPC_DECODER_C_AVX512 : SRSRAN_LDPC_DECODER_C_AVX512_FLOOD;
// Create LDPC configuration arguments
srsran_ldpc_decoder_args_t decoder_args = {};
decoder_args.type = dectype;
decoder_args.bg = base_graph;
decoder_args.ls = lift_size;
decoder_args.scaling_fctr = 1.0f;
// create an LDPC decoder
srsran_ldpc_decoder_t decoder;
if (srsran_ldpc_decoder_init(&decoder, dectype, base_graph, lift_size, 1) != 0) {
if (srsran_ldpc_decoder_init(&decoder, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}

@ -155,9 +155,16 @@ int main(int argc, char** argv)
srsran_ldpc_decoder_type_t dectype = (scheduling == 0) ? SRSRAN_LDPC_DECODER_C : SRSRAN_LDPC_DECODER_C_FLOOD;
// Create LDPC configuration arguments
srsran_ldpc_decoder_args_t decoder_args = {};
decoder_args.type = dectype;
decoder_args.bg = base_graph;
decoder_args.ls = lift_size;
decoder_args.scaling_fctr = 1.0f;
// create an LDPC decoder
srsran_ldpc_decoder_t decoder;
if (srsran_ldpc_decoder_init(&decoder, dectype, base_graph, lift_size, 1) != 0) {
if (srsran_ldpc_decoder_init(&decoder, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}

@ -148,9 +148,16 @@ int main(int argc, char** argv)
parse_args(argc, argv);
// Create LDPC configuration arguments
srsran_ldpc_decoder_args_t decoder_args = {};
decoder_args.type = SRSRAN_LDPC_DECODER_S;
decoder_args.bg = base_graph;
decoder_args.ls = lift_size;
decoder_args.scaling_fctr = 1.0f;
// create an LDPC decoder
srsran_ldpc_decoder_t decoder;
if (srsran_ldpc_decoder_init(&decoder, SRSRAN_LDPC_DECODER_S, base_graph, lift_size, 1) != 0) {
if (srsran_ldpc_decoder_init(&decoder, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}

@ -148,9 +148,16 @@ int main(int argc, char** argv)
parse_args(argc, argv);
// Create LDPC configuration arguments
srsran_ldpc_decoder_args_t decoder_args = {};
decoder_args.type = SRSRAN_LDPC_DECODER_F;
decoder_args.bg = base_graph;
decoder_args.ls = lift_size;
decoder_args.scaling_fctr = 1.0f;
// create an LDPC decoder
srsran_ldpc_decoder_t decoder;
if (srsran_ldpc_decoder_init(&decoder, SRSRAN_LDPC_DECODER_F, base_graph, lift_size, 1) != 0) {
if (srsran_ldpc_decoder_init(&decoder, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}

@ -243,42 +243,53 @@ int main(int argc, char** argv)
exit(-1);
}
// Create LDPC configuration arguments
srsran_ldpc_decoder_args_t decoder_args = {};
decoder_args.bg = base_graph;
decoder_args.ls = lift_size;
decoder_args.scaling_fctr = MS_SF;
// create an LDPC decoder (float)
srsran_ldpc_decoder_t decoder_f;
if (srsran_ldpc_decoder_init(&decoder_f, SRSRAN_LDPC_DECODER_F, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_F;
if (srsran_ldpc_decoder_init(&decoder_f, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
// create an LDPC decoder (16 bit)
srsran_ldpc_decoder_t decoder_s;
if (srsran_ldpc_decoder_init(&decoder_s, SRSRAN_LDPC_DECODER_S, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_S;
if (srsran_ldpc_decoder_init(&decoder_s, &decoder_args) != 0) {
perror("decoder init (int16_t)");
exit(-1);
}
// create an LDPC decoder (8 bit)
srsran_ldpc_decoder_t decoder_c;
if (srsran_ldpc_decoder_init(&decoder_c, SRSRAN_LDPC_DECODER_C, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C;
if (srsran_ldpc_decoder_init(&decoder_c, &decoder_args) != 0) {
perror("decoder init (int8_t)");
exit(-1);
}
// create an LDPC decoder (8 bit, flooded)
srsran_ldpc_decoder_t decoder_c_flood;
if (srsran_ldpc_decoder_init(&decoder_c_flood, SRSRAN_LDPC_DECODER_C_FLOOD, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C_FLOOD;
if (srsran_ldpc_decoder_init(&decoder_c_flood, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
#ifdef LV_HAVE_AVX2
// create an LDPC decoder (8 bit, AVX2 version)
srsran_ldpc_decoder_t decoder_avx;
if (srsran_ldpc_decoder_init(&decoder_avx, SRSRAN_LDPC_DECODER_C_AVX2, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX2;
if (srsran_ldpc_decoder_init(&decoder_avx, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
// create an LDPC decoder (8 bit, flooded scheduling, AVX2 version)
srsran_ldpc_decoder_t decoder_avx_flood;
if (srsran_ldpc_decoder_init(&decoder_avx_flood, SRSRAN_LDPC_DECODER_C_AVX2_FLOOD, base_graph, lift_size, MS_SF) !=
0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX2_FLOOD;
if (srsran_ldpc_decoder_init(&decoder_avx_flood, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
@ -287,15 +298,16 @@ int main(int argc, char** argv)
#ifdef LV_HAVE_AVX512
// create an LDPC decoder (8 bit, AVX2 version)
srsran_ldpc_decoder_t decoder_avx512;
if (srsran_ldpc_decoder_init(&decoder_avx512, SRSRAN_LDPC_DECODER_C_AVX512, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX512;
if (srsran_ldpc_decoder_init(&decoder_avx512, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}
// create an LDPC decoder (8 bit, flooded scheduling, AVX512 version)
srsran_ldpc_decoder_t decoder_avx512_flood;
if (srsran_ldpc_decoder_init(
&decoder_avx512_flood, SRSRAN_LDPC_DECODER_C_AVX512_FLOOD, base_graph, lift_size, MS_SF) != 0) {
decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX512_FLOOD;
if (srsran_ldpc_decoder_init(&decoder_avx512_flood, &decoder_args) != 0) {
perror("decoder init");
exit(-1);
}

@ -35,4 +35,21 @@
#define SRSRAN_AVX512_B_SIZE 64 /*!< \brief Number of packed bytes in an AVX512 instruction. */
#define SRSRAN_AVX512_B_SIZE_LOG 6 /*!< \brief \f$\log_2\f$ of \ref SRSRAN_AVX512_B_SIZE. */
#ifdef LV_HAVE_AVX512
#include <immintrin.h>
static inline void fec_avx512_hard_decision_c(const int8_t* llr, uint8_t* message, int nof_llr)
{
int k = 0;
for (; k < nof_llr - (SRSRAN_AVX512_B_SIZE - 1); k += SRSRAN_AVX512_B_SIZE) {
__mmask64 mask = _mm512_cmpge_epi8_mask(_mm512_load_si512((__m512i*)&llr[k]), _mm512_set1_epi8(0));
_mm512_storeu_si512((__m512i*)&message[k], _mm512_mask_blend_epi8(mask, _mm512_set1_epi8(1), _mm512_set1_epi8(0)));
}
for (; k < nof_llr; k++) {
message[k] = (llr[k] < 0);
}
}
#endif // LV_HAVE_AVX512
#endif // SRSRAN_UTILS_AVX512_H

@ -892,6 +892,9 @@ int srsran_demod_soft_demodulate(srsran_mod_t modulation, const cf_t* symbols, f
int srsran_demod_soft_demodulate_s(srsran_mod_t modulation, const cf_t* symbols, short* llr, int nsymbols)
{
if (symbols == NULL || llr == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
switch (modulation) {
case SRSRAN_MOD_BPSK:
demod_bpsk_lte_s(symbols, llr, nsymbols);
@ -917,6 +920,9 @@ int srsran_demod_soft_demodulate_s(srsran_mod_t modulation, const cf_t* symbols,
int srsran_demod_soft_demodulate_b(srsran_mod_t modulation, const cf_t* symbols, int8_t* llr, int nsymbols)
{
if (symbols == NULL || llr == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
switch (modulation) {
case SRSRAN_MOD_BPSK:
demod_bpsk_lte_b(symbols, llr, nsymbols);
@ -935,7 +941,34 @@ int srsran_demod_soft_demodulate_b(srsran_mod_t modulation, const cf_t* symbols,
break;
default:
ERROR("Invalid modulation %d", modulation);
return -1;
return SRSRAN_ERROR;
}
return 0;
return SRSRAN_SUCCESS;
}
int srsran_demod_soft_demodulate2_b(srsran_mod_t modulation, const cf_t* symbols, int8_t* llr, int nsymbols)
{
if (symbols == NULL || llr == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (srsran_demod_soft_demodulate_b(modulation, symbols, llr, nsymbols) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// If the number of bits is 2 or less, this is unnecessary
if (modulation < SRSRAN_MOD_16QAM) {
return SRSRAN_SUCCESS;
}
// Iterate all symbols seeking for zero LLR
uint32_t nof_bits_x_symbol = srsran_mod_bits_x_symbol(modulation);
for (uint32_t i = 0; i < nsymbols; i++) {
if (symbols[i] == 0.0f) {
for (uint32_t j = 0; j < nof_bits_x_symbol; j++) {
llr[i * nof_bits_x_symbol + j] = 0;
}
}
}
return SRSRAN_SUCCESS;
}

@ -19,11 +19,8 @@
*
*/
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
@ -34,7 +31,7 @@ static uint32_t nof_frames = 10;
static uint32_t num_bits = 1000;
static srsran_mod_t modulation = SRSRAN_MOD_NITEMS;
void usage(char* prog)
static void usage(char* prog)
{
printf("Usage: %s [nfv] -m modulation (1: BPSK, 2: QPSK, 4: QAM16, 6: QAM64)\n", prog);
printf("\t-n num_bits [Default %d]\n", num_bits);
@ -42,7 +39,7 @@ void usage(char* prog)
printf("\t-v srsran_verbose [Default None]\n");
}
void parse_args(int argc, char** argv)
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "nmvf")) != -1) {
@ -91,33 +88,16 @@ void parse_args(int argc, char** argv)
}
}
float mse_threshold()
{
switch (modulation) {
case SRSRAN_MOD_BPSK:
return 1.0e-6;
case SRSRAN_MOD_QPSK:
return 1.0e-6;
case SRSRAN_MOD_16QAM:
return 0.11;
case SRSRAN_MOD_64QAM:
return 0.19;
case SRSRAN_MOD_256QAM:
return 0.3;
default:
return -1.0f;
}
}
int main(int argc, char** argv)
{
int i;
srsran_modem_table_t mod;
uint8_t * input, *output;
cf_t* symbols;
float* llr;
short* llr_s;
int8_t* llr_b;
uint8_t* input = NULL;
cf_t* symbols = NULL;
float* llr = NULL;
short* llr_s = NULL;
int8_t* llr_b = NULL;
int8_t* llr_b2 = NULL;
srsran_random_t random_gen = srsran_random_init(0);
parse_args(argc, argv);
@ -136,11 +116,6 @@ int main(int argc, char** argv)
perror("malloc");
exit(-1);
}
output = srsran_vec_u8_malloc(num_bits);
if (!output) {
perror("malloc");
exit(-1);
}
symbols = srsran_vec_cf_malloc(num_bits / mod.nbits_x_symbol);
if (!symbols) {
perror("malloc");
@ -165,17 +140,21 @@ int main(int argc, char** argv)
exit(-1);
}
/* generate random data */
srand(0);
llr_b2 = srsran_vec_i8_malloc(num_bits);
if (!llr_b2) {
perror("malloc");
exit(-1);
}
int ret = -1;
struct timeval t[3];
float mean_texec = 0.0;
float mean_texec_s = 0.0;
float mean_texec_b = 0.0;
float mean_texec = 0.0f;
float mean_texec_s = 0.0f;
float mean_texec_b = 0.0f;
float mean_texec_b2 = 0.0f;
for (int n = 0; n < nof_frames; n++) {
for (i = 0; i < num_bits; i++) {
input[i] = rand() % 2;
for (int i = 0; i < num_bits; i++) {
input[i] = srsran_random_uniform_int_dist(random_gen, 0, 1);
}
/* modulate */
@ -209,6 +188,15 @@ int main(int argc, char** argv)
mean_texec_b = SRSRAN_VEC_CMA((float)t[0].tv_usec, mean_texec_b, n - 1);
}
gettimeofday(&t[1], NULL);
srsran_demod_soft_demodulate2_b(modulation, symbols, llr_b2, num_bits / mod.nbits_x_symbol);
gettimeofday(&t[2], NULL);
get_time_interval(t);
if (n > 0) {
mean_texec_b2 = SRSRAN_VEC_CMA((float)t[0].tv_usec, mean_texec_b2, n - 1);
}
if (SRSRAN_VERBOSE_ISDEBUG()) {
printf("bits=");
srsran_vec_fprint_b(stdout, input, num_bits);
@ -224,12 +212,27 @@ int main(int argc, char** argv)
printf("llr_b=");
srsran_vec_fprint_bs(stdout, llr_b, num_bits);
printf("llr_b2=");
srsran_vec_fprint_bs(stdout, llr_b2, num_bits);
}
// Check demodulation errors
for (int i = 0; i < num_bits; i++) {
if (input[i] != (llr[i] > 0 ? 1 : 0)) {
printf("Error in bit %d\n", i);
for (int j = 0; j < num_bits; j++) {
if (input[j] != (llr[j] > 0 ? 1 : 0)) {
ERROR("Error in bit %d\n", j);
goto clean_exit;
}
if (input[j] != (llr_s[j] > 0 ? 1 : 0)) {
ERROR("Error in bit %d\n", j);
goto clean_exit;
}
if (input[j] != (llr_b[j] > 0 ? 1 : 0)) {
ERROR("Error in bit %d\n", j);
goto clean_exit;
}
if (input[j] != (llr_b2[j] > 0 ? 1 : 0)) {
ERROR("Error in bit %d\n", j);
goto clean_exit;
}
}
@ -237,21 +240,23 @@ int main(int argc, char** argv)
ret = 0;
clean_exit:
srsran_random_free(random_gen);
free(llr_b);
free(llr_s);
free(llr);
free(symbols);
free(output);
free(input);
srsran_modem_table_free(&mod);
printf("Mean Throughput: %.2f/%.2f/%.2f. Mbps ExTime: %.2f/%.2f/%.2f us\n",
printf("Mean Throughput: %.2f/%.2f/%.2f/%.2f. Mbps ExTime: %.2f/%.2f/%.2f/%.2f us\n",
num_bits / mean_texec,
num_bits / mean_texec_s,
num_bits / mean_texec_b,
num_bits / mean_texec_b2,
mean_texec,
mean_texec_s,
mean_texec_b);
mean_texec_b,
mean_texec_b2);
exit(ret);
}

@ -33,6 +33,7 @@
#define PDCCH_INFO_TX(...) INFO("PDCCH Tx: " __VA_ARGS__)
#define PDCCH_INFO_RX(...) INFO("PDCCH Rx: " __VA_ARGS__)
#define PDCCH_DEBUG_RX(...) DEBUG("PDCCH Rx: " __VA_ARGS__)
/**
* @brief Recursive Y_p_n function
@ -482,8 +483,8 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q,
}
// Print channel estimates if enabled
if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) {
PDCCH_INFO_RX("ce=");
if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
PDCCH_DEBUG_RX("ce=");
srsran_vec_fprint_c(stdout, ce->ce, q->M);
}
@ -491,8 +492,8 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q,
srsran_predecoding_single(q->symbols, ce->ce, q->symbols, NULL, q->M, 1.0f, ce->noise_var);
// Print symbols if enabled
if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) {
PDCCH_INFO_RX("symbols=");
if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
PDCCH_DEBUG_RX("symbols=");
srsran_vec_fprint_c(stdout, q->symbols, q->M);
}
@ -522,8 +523,8 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q,
}
// Print d
if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) {
PDCCH_INFO_RX("d=");
if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
PDCCH_DEBUG_RX("d=");
srsran_vec_fprint_bs(stdout, d, q->K);
}

@ -25,10 +25,14 @@
#include "srsran/phy/mimo/layermap.h"
#include "srsran/phy/mimo/precoding.h"
#include "srsran/phy/modem/demod_soft.h"
#include "srsran/phy/phch/ra_nr.h"
///@brief Default number of zero RE around DC
#define PDSCH_NR_DEFAULT_NOF_ZERO_RE_AROUND_DC 3
int pdsch_nr_init_common(srsran_pdsch_nr_t* q, const srsran_pdsch_nr_args_t* args)
{
SRSRAN_MEM_ZERO(q, srsran_pdsch_nr_t, 1);
for (srsran_mod_t mod = SRSRAN_MOD_BPSK; mod < SRSRAN_MOD_NITEMS; mod++) {
if (srsran_modem_table_lte(&q->modem_tables[mod], mod) < SRSRAN_SUCCESS) {
ERROR("Error initialising modem table for %s", srsran_mod_string(mod));
@ -39,6 +43,14 @@ int pdsch_nr_init_common(srsran_pdsch_nr_t* q, const srsran_pdsch_nr_args_t* arg
}
}
if (!args->disable_zero_re_around_dc) {
if (args->nof_zero_re_around_dc == 0) {
q->nof_zero_re_around_dc = PDSCH_NR_DEFAULT_NOF_ZERO_RE_AROUND_DC;
} else {
q->nof_zero_re_around_dc = args->nof_zero_re_around_dc;
}
}
return SRSRAN_SUCCESS;
}
@ -244,11 +256,27 @@ static int srsran_pdsch_nr_cp(const srsran_pdsch_nr_t* q,
// Put or get
if (put) {
count += pdsch_nr_put_rb(&sf_symbols[re_idx], &symbols[count], &rvd_mask[rb * SRSRAN_NRE]);
} else {
uint32_t k_begin = rb * SRSRAN_NRE;
uint32_t k_end = (rb + 1) * SRSRAN_NRE;
uint32_t k_dc_begin = q->carrier.nof_prb * SRSRAN_NRE / 2 - q->nof_zero_re_around_dc / 2;
uint32_t k_dc_end = q->carrier.nof_prb * SRSRAN_NRE / 2 + SRSRAN_CEIL(q->nof_zero_re_around_dc, 2);
if (k_begin <= k_dc_end && k_end >= k_dc_begin && q->nof_zero_re_around_dc > 0) {
for (uint32_t k = k_begin; k < k_end; k++) {
if (!rvd_mask[k]) {
if (k >= k_dc_begin && k < k_dc_end) {
symbols[count++] = 0.0f;
} else {
symbols[count++] = sf_symbols[q->carrier.nof_prb * l * SRSRAN_NRE + k];
}
}
}
} else {
count += pdsch_nr_get_rb(&symbols[count], &sf_symbols[re_idx], &rvd_mask[rb * SRSRAN_NRE]);
}
}
}
}
return count;
}
@ -437,13 +465,14 @@ static inline int pdsch_nr_decode_codeword(srsran_pdsch_nr_t* q,
// Demodulation
int8_t* llr = (int8_t*)q->b[tb->cw_idx];
if (srsran_demod_soft_demodulate_b(tb->mod, q->d[tb->cw_idx], llr, tb->nof_re)) {
if (srsran_demod_soft_demodulate2_b(tb->mod, q->d[tb->cw_idx], llr, tb->nof_re)) {
return SRSRAN_ERROR;
}
// EVM
if (q->evm_buffer != NULL) {
res->evm = srsran_evm_run_b(q->evm_buffer, &q->modem_tables[tb->mod], q->d[tb->cw_idx], llr, tb->nof_bits);
res->evm[tb->cw_idx] =
srsran_evm_run_b(q->evm_buffer, &q->modem_tables[tb->mod], q->d[tb->cw_idx], llr, tb->nof_bits);
}
// Change LLR sign and set to zero the LLR that are not used
@ -458,7 +487,7 @@ static inline int pdsch_nr_decode_codeword(srsran_pdsch_nr_t* q,
}
// Decode SCH
if (srsran_dlsch_nr_decode(&q->sch, &cfg->sch_cfg, tb, llr, res->payload, &res->crc) < SRSRAN_SUCCESS) {
if (srsran_dlsch_nr_decode(&q->sch, &cfg->sch_cfg, tb, llr, &res->tb[tb->cw_idx]) < SRSRAN_SUCCESS) {
ERROR("Error in DL-SCH encoding");
return SRSRAN_ERROR;
}
@ -547,8 +576,10 @@ int srsran_pdsch_nr_decode(srsran_pdsch_nr_t* q,
return SRSRAN_SUCCESS;
}
static uint32_t srsran_pdsch_nr_grant_info(const srsran_sch_cfg_nr_t* cfg,
static uint32_t pdsch_nr_grant_info(const srsran_pdsch_nr_t* q,
const srsran_sch_cfg_nr_t* cfg,
const srsran_sch_grant_nr_t* grant,
const srsran_pdsch_res_nr_t* res,
char* str,
uint32_t str_len)
{
@ -566,7 +597,9 @@ static uint32_t srsran_pdsch_nr_grant_info(const srsran_sch_cfg_nr_t* cfg,
len = srsran_print_check(str,
str_len,
len,
",k0=%d,prb=%d:%d,symb=%d:%d,mapping=%s",
"beta_dmrs=%.3f CDM-grp=%d k0=%d prb=%d:%d symb=%d:%d mapping=%s ",
isnormal(grant->beta_dmrs) ? grant->beta_dmrs : 1.0f,
grant->nof_dmrs_cdm_groups_without_data,
grant->k,
first_prb,
grant->nof_prb,
@ -578,14 +611,22 @@ static uint32_t srsran_pdsch_nr_grant_info(const srsran_sch_cfg_nr_t* cfg,
// ...
// Append spatial resources
len = srsran_print_check(str, str_len, len, ",Nl=%d", grant->nof_layers);
len = srsran_print_check(str, str_len, len, "Nl=%d ", grant->nof_layers);
// Append scrambling ID
len = srsran_print_check(str, str_len, len, ",n_scid=%d,", grant->n_scid);
len = srsran_print_check(str, str_len, len, "n_scid=%d ", grant->n_scid);
// Append TB info
for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) {
len += srsran_sch_nr_tb_info(&grant->tb[i], &str[len], str_len - len);
len += srsran_sch_nr_tb_info(&grant->tb[i], &res->tb[i], &str[len], str_len - len);
if (res != NULL) {
if (grant->tb[i].enabled && !isnan(res->evm[i])) {
len = srsran_print_check(str, str_len, len, "evm=%.2f ", res->evm[i]);
if (i < SRSRAN_MAX_CODEWORDS - 1) {
}
}
}
}
return len;
@ -594,52 +635,21 @@ static uint32_t srsran_pdsch_nr_grant_info(const srsran_sch_cfg_nr_t* cfg,
uint32_t srsran_pdsch_nr_rx_info(const srsran_pdsch_nr_t* q,
const srsran_sch_cfg_nr_t* cfg,
const srsran_sch_grant_nr_t* grant,
const srsran_pdsch_res_nr_t res[SRSRAN_MAX_CODEWORDS],
const srsran_pdsch_res_nr_t* res,
char* str,
uint32_t str_len)
{
uint32_t len = 0;
len += srsran_pdsch_nr_grant_info(cfg, grant, &str[len], str_len - len);
len += pdsch_nr_grant_info(q, cfg, grant, res, &str[len], str_len - len);
if (cfg->rvd_re.count != 0) {
len = srsran_print_check(str, str_len, len, ", Reserved={");
len = srsran_print_check(str, str_len, len, "Reserved: ");
len += srsran_re_pattern_list_info(&cfg->rvd_re, &str[len], str_len - len);
len = srsran_print_check(str, str_len, len, "}");
}
if (q->evm_buffer != NULL) {
len = srsran_print_check(str, str_len, len, ",evm={", 0);
for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) {
if (grant->tb[i].enabled && !isnan(res[i].evm)) {
len = srsran_print_check(str, str_len, len, "%.2f", res[i].evm);
if (i < SRSRAN_MAX_CODEWORDS - 1) {
if (grant->tb[i + 1].enabled) {
len = srsran_print_check(str, str_len, len, ",", 0);
}
}
}
}
len = srsran_print_check(str, str_len, len, "}", 0);
}
if (res != NULL) {
len = srsran_print_check(str, str_len, len, ",crc={", 0);
for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) {
if (grant->tb[i].enabled) {
len = srsran_print_check(str, str_len, len, "%s", res[i].crc ? "OK" : "KO");
if (i < SRSRAN_MAX_CODEWORDS - 1) {
if (grant->tb[i + 1].enabled) {
len = srsran_print_check(str, str_len, len, ",", 0);
}
}
}
}
len = srsran_print_check(str, str_len, len, "}", 0);
}
if (q->meas_time_en) {
len = srsran_print_check(str, str_len, len, ", t=%d us", q->meas_time_us);
len = srsran_print_check(str, str_len, len, " t=%d us", q->meas_time_us);
}
return len;

@ -123,6 +123,14 @@ srsran_prach_sfn_t srsran_prach_get_sfn(uint32_t config_idx)
*/
bool srsran_prach_tti_opportunity(srsran_prach_t* p, uint32_t current_tti, int allowed_subframe)
{
if (p == NULL) {
return false;
}
if (p->is_nr) {
return srsran_prach_nr_tti_opportunity_fr1_unpaired(p->config_idx, current_tti);
}
uint32_t config_idx = p->config_idx;
if (!p->tdd_config.configured) {
return srsran_prach_tti_opportunity_config_fdd(config_idx, current_tti, allowed_subframe);
@ -265,6 +273,66 @@ void srsran_prach_sf_config(uint32_t config_idx, srsran_prach_sf_config_t* sf_co
memcpy(sf_config, &prach_sf_config[config_idx % 16], sizeof(srsran_prach_sf_config_t));
}
const prach_nr_config_t* srsran_prach_nr_get_cfg_fr1_unpaired(uint32_t config_idx)
{
if (config_idx < PRACH_NR_CFG_FR1_UNPAIRED_NOF_CFG) {
return &prach_nr_cfg_fr1_unpaired[config_idx];
}
ERROR("Invalid configuration index %d", config_idx);
return NULL;
}
bool srsran_prach_nr_tti_opportunity_fr1_unpaired(uint32_t config_idx, uint32_t current_tti)
{
uint32_t sfn = current_tti / SRSRAN_NOF_SF_X_FRAME;
uint32_t sf_idx = current_tti % SRSRAN_NOF_SF_X_FRAME;
// Get configuration
const prach_nr_config_t* cfg = srsran_prach_nr_get_cfg_fr1_unpaired(config_idx);
if (cfg == NULL) {
return false;
}
// Protect zero division
if (cfg->x == 0) {
ERROR("Invalid Zero value");
return false;
}
// Check for System Frame Number match
if (sfn % cfg->x != cfg->y) {
return false;
}
// Protect subframe number vector access
if (cfg->nof_subframe_number > PRACH_NR_CFG_MAX_NOF_SF) {
ERROR("Invalid number of subframes (%d)", cfg->nof_subframe_number);
return false;
}
// Check for subframe number match
for (uint32_t i = 0; i < cfg->nof_subframe_number; i++) {
if (cfg->subframe_number[i] == sf_idx) {
return true;
}
}
// If reached here, no opportunity
return false;
}
uint32_t srsran_prach_nr_start_symbol_fr1_unpaired(uint32_t config_idx)
{
// Get configuration
const prach_nr_config_t* cfg = srsran_prach_nr_get_cfg_fr1_unpaired(config_idx);
if (cfg == NULL) {
return false;
}
return cfg->starting_symbol;
}
// For debug use only
void print(void* d, uint32_t size, uint32_t len, char* file_str)
{

@ -443,3 +443,22 @@ srsran_prach_tdd_loc_table_t prach_tdd_loc_table[64][7] = {
{0, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}},
{0, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}},
{4, {{0, 0, 0, 0}, {1, 0, 0, 0}, {2, 0, 0, 0}, {3, 0, 0, 0}}}}};
#define PRACH_NR_CFG_FR1_UNPAIRED_NOF_CFG 28
// Table 6.3.3.2-3: Random access configurations for FR1 and unpaired spectrum.
static const prach_nr_config_t prach_nr_cfg_fr1_unpaired[PRACH_NR_CFG_FR1_UNPAIRED_NOF_CFG] = {
{0, 16, 1, {9}, 1, 0}, {0, 8, 1, {9}, 1, 0},
{0, 4, 1, {9}, 1, 0}, {0, 2, 0, {9}, 1, 0},
{0, 2, 1, {9}, 1, 0}, {0, 2, 0, {4}, 1, 0},
{0, 2, 1, {4}, 1, 0}, {0, 1, 0, {9}, 1, 0},
{0, 1, 0, {8}, 1, 0}, {0, 1, 0, {7}, 1, 0},
{0, 1, 0, {6}, 1, 0}, {0, 1, 0, {5}, 1, 0},
{0, 1, 0, {4}, 1, 0}, {0, 1, 0, {3}, 1, 0},
{0, 1, 0, {2}, 1, 0}, {0, 1, 0, {1, 6}, 1, 0},
{0, 1, 0, {1, 6}, 1, 7}, {0, 1, 0, {4, 9}, 1, 0},
{0, 1, 0, {3, 8}, 1, 0}, {0, 1, 0, {2, 7}, 1, 0},
{0, 1, 0, {8, 9}, 1, 0}, {0, 1, 0, {4, 8, 9}, 1, 0},
{0, 1, 0, {3, 4, 9}, 1, 0}, {0, 1, 0, {7, 8, 9}, 1, 0},
{0, 1, 0, {3, 4, 8, 9}, 1, 0}, {0, 1, 0, {6, 7, 8, 9}, 1, 0},
{0, 1, 0, {1, 4, 6, 9}, 1, 0}, {0, 1, 0, {1, 3, 5, 7, 9}, 1, 0}};

@ -970,7 +970,7 @@ int srsran_pusch_nr_encode(srsran_pusch_nr_t* q,
for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) {
nof_cw += grant->tb[tb].enabled ? 1 : 0;
if (pusch_nr_encode_codeword(q, cfg, &grant->tb[tb], data[tb].payload, &data[0].uci, grant->rnti) <
if (pusch_nr_encode_codeword(q, cfg, &grant->tb[tb], data->payload[tb], &data[0].uci, grant->rnti) <
SRSRAN_SUCCESS) {
ERROR("Error encoding TB %d", tb);
return SRSRAN_ERROR;
@ -1073,7 +1073,8 @@ static inline int pusch_nr_decode_codeword(srsran_pusch_nr_t* q,
// EVM
if (q->evm_buffer != NULL) {
res->evm = srsran_evm_run_b(q->evm_buffer, &q->modem_tables[tb->mod], q->d[tb->cw_idx], llr, tb->nof_bits);
res->evm[tb->cw_idx] =
srsran_evm_run_b(q->evm_buffer, &q->modem_tables[tb->mod], q->d[tb->cw_idx], llr, tb->nof_bits);
}
// Descrambling
@ -1142,7 +1143,7 @@ static inline int pusch_nr_decode_codeword(srsran_pusch_nr_t* q,
// Decode Ul-SCH
if (tb->nof_bits != 0) {
if (srsran_ulsch_nr_decode(&q->sch, &cfg->sch_cfg, tb, llr, res->payload, &res->crc) < SRSRAN_SUCCESS) {
if (srsran_ulsch_nr_decode(&q->sch, &cfg->sch_cfg, tb, llr, &res->tb[tb->cw_idx]) < SRSRAN_SUCCESS) {
ERROR("Error in SCH decoding");
return SRSRAN_ERROR;
}
@ -1240,6 +1241,7 @@ int srsran_pusch_nr_decode(srsran_pusch_nr_t* q,
static uint32_t srsran_pusch_nr_grant_info(const srsran_sch_cfg_nr_t* cfg,
const srsran_sch_grant_nr_t* grant,
const srsran_pusch_res_nr_t* res,
char* str,
uint32_t str_len)
{
@ -1276,7 +1278,7 @@ static uint32_t srsran_pusch_nr_grant_info(const srsran_sch_cfg_nr_t* cfg,
// Append TB info
for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) {
len += srsran_sch_nr_tb_info(&grant->tb[i], &str[len], str_len - len);
len += srsran_sch_nr_tb_info(&grant->tb[i], &res->tb[i], &str[len], str_len - len);
}
return len;
@ -1285,7 +1287,7 @@ static uint32_t srsran_pusch_nr_grant_info(const srsran_sch_cfg_nr_t* cfg,
uint32_t srsran_pusch_nr_rx_info(const srsran_pusch_nr_t* q,
const srsran_sch_cfg_nr_t* cfg,
const srsran_sch_grant_nr_t* grant,
const srsran_pusch_res_nr_t res[SRSRAN_MAX_CODEWORDS],
const srsran_pusch_res_nr_t* res,
char* str,
uint32_t str_len)
{
@ -1295,12 +1297,12 @@ uint32_t srsran_pusch_nr_rx_info(const srsran_pusch_nr_t* q,
return 0;
}
len += srsran_pusch_nr_grant_info(cfg, grant, &str[len], str_len - len);
len += srsran_pusch_nr_grant_info(cfg, grant, res, &str[len], str_len - len);
if (q->evm_buffer != NULL) {
len = srsran_print_check(str, str_len, len, ",evm={", 0);
for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) {
if (grant->tb[i].enabled && !isnan(res[i].evm)) {
if (grant->tb[i].enabled && !isnan(res->evm[i])) {
len = srsran_print_check(str, str_len, len, "%.2f", res[i].evm);
if (i < SRSRAN_MAX_CODEWORDS - 1) {
if (grant->tb[i + 1].enabled) {
@ -1321,7 +1323,7 @@ uint32_t srsran_pusch_nr_rx_info(const srsran_pusch_nr_t* q,
len = srsran_print_check(str, str_len, len, ",crc={", 0);
for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) {
if (grant->tb[i].enabled) {
len = srsran_print_check(str, str_len, len, "%s", res[i].crc ? "OK" : "KO");
len = srsran_print_check(str, str_len, len, "%s", res->tb[i].crc ? "OK" : "KO");
if (i < SRSRAN_MAX_CODEWORDS - 1) {
if (grant->tb[i + 1].enabled) {
len = srsran_print_check(str, str_len, len, ",", 0);
@ -1352,7 +1354,7 @@ uint32_t srsran_pusch_nr_tx_info(const srsran_pusch_nr_t* q,
return 0;
}
len += srsran_pusch_nr_grant_info(cfg, grant, &str[len], str_len - len);
len += srsran_pusch_nr_grant_info(cfg, grant, NULL, &str[len], str_len - len);
if (uci_value != NULL) {
srsran_uci_data_nr_t uci_data = {};

@ -685,6 +685,7 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier,
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;
// 5.1.4 PDSCH resource mapping
if (ra_dl_resource_mapping(carrier, slot, pdsch_hl_cfg, pdsch_cfg) < SRSRAN_SUCCESS) {
@ -793,6 +794,10 @@ int srsran_ra_ul_dci_to_grant_nr(const srsran_carrier_nr_t* carrier,
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->tb[0].pid = dci_ul->pid;
// 5.1.6.2 DM-RS reception procedure
if (ra_ul_dmrs(pusch_hl_cfg, pusch_grant, pusch_cfg) < SRSRAN_SUCCESS) {

@ -284,24 +284,35 @@ int srsran_sch_nr_init_rx(srsran_sch_nr_t* q, const srsran_sch_nr_args_t* args)
continue;
}
q->decoder_bg1[ls] = calloc(1, sizeof(srsran_ldpc_decoder_t));
// Initialise LDPC configuration arguments
srsran_ldpc_decoder_args_t decoder_args = {};
decoder_args.type = decoder_type;
decoder_args.ls = ls;
decoder_args.scaling_fctr = scaling_factor;
decoder_args.max_nof_iter = args->max_nof_iter;
q->decoder_bg1[ls] = SRSRAN_MEM_ALLOC(srsran_ldpc_decoder_t, 1);
if (!q->decoder_bg1[ls]) {
ERROR("Error: calloc");
return SRSRAN_ERROR;
}
SRSRAN_MEM_ZERO(q->decoder_bg1[ls], srsran_ldpc_decoder_t, 1);
if (srsran_ldpc_decoder_init(q->decoder_bg1[ls], decoder_type, BG1, ls, scaling_factor) < SRSRAN_SUCCESS) {
decoder_args.bg = BG1;
if (srsran_ldpc_decoder_init(q->decoder_bg1[ls], &decoder_args) < SRSRAN_SUCCESS) {
ERROR("Error: initialising BG1 LDPC decoder for ls=%d", ls);
return SRSRAN_ERROR;
}
q->decoder_bg2[ls] = calloc(1, sizeof(srsran_ldpc_decoder_t));
q->decoder_bg2[ls] = SRSRAN_MEM_ALLOC(srsran_ldpc_decoder_t, 1);
if (!q->decoder_bg2[ls]) {
ERROR("Error: calloc");
return SRSRAN_ERROR;
}
SRSRAN_MEM_ZERO(q->decoder_bg2[ls], srsran_ldpc_decoder_t, 1);
if (srsran_ldpc_decoder_init(q->decoder_bg2[ls], decoder_type, BG2, ls, scaling_factor) < SRSRAN_SUCCESS) {
decoder_args.bg = BG2;
if (srsran_ldpc_decoder_init(q->decoder_bg2[ls], &decoder_args) < SRSRAN_SUCCESS) {
ERROR("Error: initialising BG2 LDPC decoder for ls=%d", ls);
return SRSRAN_ERROR;
}
@ -507,19 +518,19 @@ static inline int sch_nr_encode(srsran_sch_nr_t* q,
return SRSRAN_SUCCESS;
}
int sch_nr_decode(srsran_sch_nr_t* q,
static int sch_nr_decode(srsran_sch_nr_t* q,
const srsran_sch_cfg_t* sch_cfg,
const srsran_sch_tb_t* tb,
int8_t* e_bits,
uint8_t* data,
bool* crc_ok)
srsran_sch_tb_res_nr_t* res)
{
// Pointer protection
if (!q || !sch_cfg || !tb || !data || !e_bits || !crc_ok) {
if (!q || !sch_cfg || !tb || !e_bits || !res) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
int8_t* input_ptr = e_bits;
uint32_t nof_iter_sum = 0;
srsran_sch_nr_tb_info_t cfg = {};
if (srsran_sch_nr_fill_tb_info(&q->carrier, sch_cfg, tb, &cfg) < SRSRAN_SUCCESS) {
@ -597,26 +608,24 @@ int sch_nr_decode(srsran_sch_nr_t* q,
return SRSRAN_ERROR;
}
// Decode
srsran_ldpc_decoder_decode_c(decoder, rm_buffer, q->temp_cb, n_llr);
// Compute CB CRC
uint32_t cb_len = cfg.Kp - cfg.L_cb;
// Select CB or TB early stop CRC
srsran_crc_t* crc = (cfg.L_tb == 16) ? &q->crc_tb_16 : &q->crc_tb_24;
if (cfg.L_cb) {
uint8_t* ptr = q->temp_cb + cb_len;
uint32_t checksum1 = srsran_crc_checksum(&q->crc_cb, q->temp_cb, (int)cb_len);
uint32_t checksum2 = srsran_bit_pack(&ptr, cfg.L_cb);
tb->softbuffer.rx->cb_crc[r] = (checksum1 == checksum2);
crc = &q->crc_cb;
}
SCH_INFO_RX("CB %d/%d: CRC={%06x, %06x} ... %s",
r,
cfg.C,
checksum1,
checksum2,
tb->softbuffer.rx->cb_crc[r] ? "OK" : "KO");
} else {
tb->softbuffer.rx->cb_crc[r] = true;
// Decode
int n_iter = srsran_ldpc_decoder_decode_crc_c(decoder, rm_buffer, q->temp_cb, n_llr, crc);
if (n_iter < SRSRAN_SUCCESS) {
ERROR("Error decoding CB");
return SRSRAN_ERROR;
}
nof_iter_sum += ((n_iter == 0) ? decoder->max_nof_iter : (uint32_t)n_iter);
// Compute CB CRC only if LDPC decoder reached the end
uint32_t cb_len = cfg.Kp - cfg.L_cb;
tb->softbuffer.rx->cb_crc[r] = (n_iter != 0);
SCH_INFO_RX("CB %d/%d CRC=%s", r, cfg.C, tb->softbuffer.rx->cb_crc[r] ? "OK" : "KO");
// Pack and count CRC OK only if CRC is match
if (tb->softbuffer.rx->cb_crc[r]) {
@ -627,10 +636,13 @@ int sch_nr_decode(srsran_sch_nr_t* q,
input_ptr += E;
}
// All CB are decoded
if (cb_ok == cfg.C) {
// Not all CB are decoded, skip TB union and CRC check
if (cb_ok != cfg.C) {
return SRSRAN_SUCCESS;
}
uint32_t checksum2 = 0;
uint8_t* output_ptr = data;
uint8_t* output_ptr = res->payload;
for (uint32_t r = 0; r < cfg.C; r++) {
uint32_t cb_len = cfg.Kp - cfg.L_cb;
@ -640,14 +652,18 @@ int sch_nr_decode(srsran_sch_nr_t* q,
cb_len -= cfg.L_tb;
}
// Append CB
srsran_vec_u8_copy(output_ptr, tb->softbuffer.rx->data[r], cb_len / 8);
output_ptr += cb_len / 8;
// CB Debug trace
if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
DEBUG("CB %d:", r);
DEBUG("CB %d/%d:", r, cfg.C);
srsran_vec_fprint_byte(stdout, tb->softbuffer.rx->data[r], cb_len / 8);
}
if (r == cfg.C - 1) {
// Compute TB CRC for last block
if (cfg.C > 1 && r == cfg.C - 1) {
uint8_t tb_crc_unpacked[24] = {};
uint8_t* tb_crc_unpacked_ptr = tb_crc_unpacked;
srsran_bit_unpack_vector(&tb->softbuffer.rx->data[r][cb_len / 8], tb_crc_unpacked, cfg.L_tb);
@ -658,20 +674,26 @@ int sch_nr_decode(srsran_sch_nr_t* q,
// Check if TB is all zeros
bool all_zeros = true;
for (uint32_t i = 0; i < tb->tbs / 8 && all_zeros; i++) {
all_zeros = (data[i] == 0);
all_zeros = (res->payload[i] == 0);
}
// Calculate TB CRC from packed data
uint32_t checksum1 = srsran_crc_checksum_byte(crc_tb, data, tb->tbs);
*crc_ok = (checksum1 == checksum2 && !all_zeros);
if (cfg.C == 1) {
res->crc = !all_zeros;
SCH_INFO_RX("TB: TBS=%d; CRC=%s", tb->tbs, tb->softbuffer.rx->cb_crc[0] ? "OK" : "KO");
} else {
// More than one
uint32_t checksum1 = srsran_crc_checksum_byte(crc_tb, res->payload, tb->tbs);
res->crc = (checksum1 == checksum2 && !all_zeros);
SCH_INFO_RX("TB: TBS=%d; CRC={%06x, %06x}", tb->tbs, checksum1, checksum2);
}
// Set average number of iterations
res->avg_iter = (float)nof_iter_sum / (float)cfg.C;
if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
DEBUG("Decode: ");
srsran_vec_fprint_byte(stdout, data, tb->tbs / 8);
}
} else {
*crc_ok = false;
srsran_vec_fprint_byte(stdout, res->payload, tb->tbs / 8);
}
return SRSRAN_SUCCESS;
@ -690,10 +712,9 @@ int srsran_dlsch_nr_decode(srsran_sch_nr_t* q,
const srsran_sch_cfg_t* sch_cfg,
const srsran_sch_tb_t* tb,
int8_t* e_bits,
uint8_t* data,
bool* crc_ok)
srsran_sch_tb_res_nr_t* res)
{
return sch_nr_decode(q, sch_cfg, tb, e_bits, data, crc_ok);
return sch_nr_decode(q, sch_cfg, tb, e_bits, res);
}
int srsran_ulsch_nr_encode(srsran_sch_nr_t* q,
@ -709,29 +730,32 @@ int srsran_ulsch_nr_decode(srsran_sch_nr_t* q,
const srsran_sch_cfg_t* sch_cfg,
const srsran_sch_tb_t* tb,
int8_t* e_bits,
uint8_t* data,
bool* crc_ok)
srsran_sch_tb_res_nr_t* res)
{
return sch_nr_decode(q, sch_cfg, tb, e_bits, data, crc_ok);
return sch_nr_decode(q, sch_cfg, tb, e_bits, res);
}
int srsran_sch_nr_tb_info(const srsran_sch_tb_t* tb, char* str, uint32_t str_len)
int srsran_sch_nr_tb_info(const srsran_sch_tb_t* tb, const srsran_sch_tb_res_nr_t* res, char* str, uint32_t str_len)
{
int len = 0;
if (tb->enabled) {
len += srsran_print_check(str,
len = srsran_print_check(str,
str_len,
len,
"tb={mod=%s,Nl=%d,tbs=%d,R=%.3f,rv=%d,Nre=%d,Nbit=%d,cw=%d}",
"CW%d: mod=%s Nl=%d tbs=%d R=%.3f rv=%d Nre=%d Nbit=%d ",
tb->cw_idx,
srsran_mod_string(tb->mod),
tb->N_L,
tb->tbs / 8,
tb->R,
tb->rv,
tb->nof_re,
tb->nof_bits,
tb->cw_idx);
tb->nof_bits);
if (res != NULL) {
len = srsran_print_check(str, str_len, len, "CRC=%s iter=%.1f ", res->crc ? "OK" : "KO", res->avg_iter);
}
}
return len;

@ -88,7 +88,7 @@ int main(int argc, char** argv)
srsran_pdsch_nr_t pdsch_tx = {};
srsran_pdsch_nr_t pdsch_rx = {};
srsran_chest_dl_res_t chest = {};
srsran_pdsch_res_nr_t pdsch_res[SRSRAN_MAX_TB] = {};
srsran_pdsch_res_nr_t pdsch_res = {};
srsran_random_t rand_gen = srsran_random_init(1234);
uint8_t* data_tx[SRSRAN_MAX_TB] = {};
@ -142,7 +142,7 @@ int main(int argc, char** argv)
goto clean_exit;
}
pdsch_res[i].payload = data_rx[i];
pdsch_res.tb[i].payload = data_rx[i];
}
srsran_softbuffer_tx_t softbuffer_tx = {};
@ -233,14 +233,14 @@ int main(int argc, char** argv)
}
chest.nof_re = pdsch_cfg.grant.tb->nof_re;
if (srsran_pdsch_nr_decode(&pdsch_rx, &pdsch_cfg, &pdsch_cfg.grant, &chest, sf_symbols, pdsch_res) <
if (srsran_pdsch_nr_decode(&pdsch_rx, &pdsch_cfg, &pdsch_cfg.grant, &chest, sf_symbols, &pdsch_res) <
SRSRAN_SUCCESS) {
ERROR("Error encoding");
goto clean_exit;
}
if (pdsch_res->evm > 0.001f) {
ERROR("Error PDSCH EVM is too high %f", pdsch_res->evm);
if (pdsch_res.evm[0] > 0.001f) {
ERROR("Error PDSCH EVM is too high %f", pdsch_res.evm[0]);
goto clean_exit;
}
@ -265,7 +265,7 @@ int main(int argc, char** argv)
goto clean_exit;
}
if (!pdsch_res[0].crc) {
if (!pdsch_res.tb[0].crc) {
ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs);
goto clean_exit;
}
@ -279,7 +279,7 @@ int main(int argc, char** argv)
goto clean_exit;
}
INFO("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!\n", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs, pdsch_res[0].evm);
INFO("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!\n", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs, pdsch_res.evm[0]);
}
}

@ -99,8 +99,8 @@ int main(int argc, char** argv)
srsran_chest_dl_res_t chest = {};
srsran_random_t rand_gen = srsran_random_init(1234);
srsran_pusch_data_nr_t data_tx[SRSRAN_MAX_TB] = {};
srsran_pusch_res_nr_t data_rx[SRSRAN_MAX_CODEWORDS] = {};
srsran_pusch_data_nr_t data_tx = {};
srsran_pusch_res_nr_t data_rx = {};
cf_t* sf_symbols[SRSRAN_MAX_LAYERS_NR] = {};
// Set default PUSCH configuration
@ -143,9 +143,9 @@ int main(int argc, char** argv)
}
for (uint32_t i = 0; i < pusch_tx.max_cw; i++) {
data_tx[i].payload = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR);
data_rx[i].payload = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR);
if (data_tx[i].payload == NULL || data_rx[i].payload == NULL) {
data_tx.payload[i] = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR);
data_rx.tb[i].payload = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR);
if (data_tx.payload[i] == NULL || data_rx.tb[i].payload == NULL) {
ERROR("Error malloc");
goto clean_exit;
}
@ -221,12 +221,12 @@ int main(int argc, char** argv)
// Generate SCH payload
for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) {
// Skip TB if no allocated
if (data_tx[tb].payload == NULL) {
if (data_tx.payload[tb] == NULL) {
continue;
}
for (uint32_t i = 0; i < pusch_cfg.grant.tb[tb].tbs; i++) {
data_tx[tb].payload[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX);
data_tx.payload[tb][i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX);
}
pusch_cfg.grant.tb[tb].softbuffer.tx = &softbuffer_tx;
}
@ -235,7 +235,7 @@ int main(int argc, char** argv)
if (nof_ack_bits > 0) {
pusch_cfg.uci.o_ack = nof_ack_bits;
for (uint32_t i = 0; i < nof_ack_bits; i++) {
data_tx->uci.ack[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1);
data_tx.uci.ack[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1);
}
}
@ -246,15 +246,15 @@ int main(int argc, char** argv)
pusch_cfg.uci.csi[0].quantity = SRSRAN_CSI_REPORT_QUANTITY_NONE;
pusch_cfg.uci.csi[0].K_csi_rs = nof_csi_bits;
pusch_cfg.uci.nof_csi = 1;
data_tx->uci.csi[0].none = csi_report_tx;
data_tx.uci.csi[0].none = csi_report_tx;
for (uint32_t i = 0; i < nof_csi_bits; i++) {
csi_report_tx[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1);
}
data_rx->uci.csi[0].none = csi_report_rx;
data_rx.uci.csi[0].none = csi_report_rx;
}
if (srsran_pusch_nr_encode(&pusch_tx, &pusch_cfg, &pusch_cfg.grant, data_tx, sf_symbols) < SRSRAN_SUCCESS) {
if (srsran_pusch_nr_encode(&pusch_tx, &pusch_cfg, &pusch_cfg.grant, &data_tx, sf_symbols) < SRSRAN_SUCCESS) {
ERROR("Error encoding");
goto clean_exit;
}
@ -269,14 +269,14 @@ int main(int argc, char** argv)
}
chest.nof_re = pusch_cfg.grant.tb->nof_re;
if (srsran_pusch_nr_decode(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &chest, sf_symbols, data_rx) <
if (srsran_pusch_nr_decode(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &chest, sf_symbols, &data_rx) <
SRSRAN_SUCCESS) {
ERROR("Error encoding");
goto clean_exit;
}
if (data_rx[0].evm > 0.001f) {
ERROR("Error PUSCH EVM is too high %f", data_rx[0].evm);
if (data_rx.evm[0] > 0.001f) {
ERROR("Error PUSCH EVM is too high %f", data_rx.evm[0]);
goto clean_exit;
}
@ -302,24 +302,24 @@ int main(int argc, char** argv)
}
// Validate UL-SCH CRC check
if (!data_rx[0].crc) {
if (!data_rx.tb[0].crc) {
ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pusch_cfg.grant.tb[0].tbs);
goto clean_exit;
}
// Validate UL-SCH payload
if (memcmp(data_tx[0].payload, data_rx[0].payload, pusch_cfg.grant.tb[0].tbs / 8) != 0) {
if (memcmp(data_tx.payload[0], data_rx.tb[0].payload, pusch_cfg.grant.tb[0].tbs / 8) != 0) {
ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pusch_cfg.grant.tb[0].tbs);
printf("Tx data: ");
srsran_vec_fprint_byte(stdout, data_tx[0].payload, pusch_cfg.grant.tb[0].tbs / 8);
srsran_vec_fprint_byte(stdout, data_tx.payload[0], pusch_cfg.grant.tb[0].tbs / 8);
printf("Rx data: ");
srsran_vec_fprint_byte(stdout, data_tx[0].payload, pusch_cfg.grant.tb[0].tbs / 8);
srsran_vec_fprint_byte(stdout, data_tx.payload[0], pusch_cfg.grant.tb[0].tbs / 8);
goto clean_exit;
}
// Validate UCI is decoded successfully
if (nof_ack_bits > 0 || nof_csi_bits > 0) {
if (!data_rx[0].uci.valid) {
if (!data_rx.uci.valid) {
ERROR("UCI data was not decoded ok");
goto clean_exit;
}
@ -327,29 +327,29 @@ int main(int argc, char** argv)
// Validate HARQ-ACK is decoded successfully
if (nof_ack_bits > 0) {
if (memcmp(data_tx[0].uci.ack, data_rx[0].uci.ack, nof_ack_bits) != 0) {
if (memcmp(data_tx.uci.ack, data_rx.uci.ack, nof_ack_bits) != 0) {
ERROR("UCI HARQ-ACK bits are unmatched");
printf("Tx data: ");
srsran_vec_fprint_byte(stdout, data_tx[0].uci.ack, nof_ack_bits);
srsran_vec_fprint_byte(stdout, data_tx.uci.ack, nof_ack_bits);
printf("Rx data: ");
srsran_vec_fprint_byte(stdout, data_rx[0].uci.ack, nof_ack_bits);
srsran_vec_fprint_byte(stdout, data_rx.uci.ack, nof_ack_bits);
goto clean_exit;
}
}
// Validate CSI is decoded successfully
if (nof_csi_bits > 0) {
if (memcmp(data_tx[0].uci.csi[0].none, data_rx[0].uci.csi[0].none, nof_csi_bits) != 0) {
if (memcmp(data_tx.uci.csi[0].none, data_rx.uci.csi[0].none, nof_csi_bits) != 0) {
ERROR("UCI CSI bits are unmatched");
printf("Tx data: ");
srsran_vec_fprint_byte(stdout, data_tx[0].uci.csi[0].none, nof_csi_bits);
srsran_vec_fprint_byte(stdout, data_tx.uci.csi[0].none, nof_csi_bits);
printf("Rx data: ");
srsran_vec_fprint_byte(stdout, data_rx[0].uci.csi[0].none, nof_csi_bits);
srsran_vec_fprint_byte(stdout, data_rx.uci.csi[0].none, nof_csi_bits);
goto clean_exit;
}
}
printf("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!\n", n_prb, mcs, pusch_cfg.grant.tb[0].tbs, data_rx[0].evm);
printf("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!\n", n_prb, mcs, pusch_cfg.grant.tb[0].tbs, data_rx.evm[0]);
}
}
@ -361,11 +361,11 @@ clean_exit:
srsran_pusch_nr_free(&pusch_tx);
srsran_pusch_nr_free(&pusch_rx);
for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) {
if (data_tx[i].payload) {
free(data_tx[i].payload);
if (data_tx.payload[i]) {
free(data_tx.payload[i]);
}
if (data_rx[i].payload) {
free(data_rx[i].payload);
if (data_rx.tb[i].payload) {
free(data_rx.tb[i].payload);
}
}
for (uint32_t i = 0; i < SRSRAN_MAX_LAYERS_NR; i++) {

@ -215,14 +215,15 @@ int main(int argc, char** argv)
tb.softbuffer.rx = &softbuffer_rx;
srsran_softbuffer_rx_reset(tb.softbuffer.rx);
bool crc = false;
if (srsran_dlsch_nr_decode(&sch_nr_rx, &pdsch_cfg.sch_cfg, &tb, llr, data_rx, &crc) < SRSRAN_SUCCESS) {
srsran_sch_tb_res_nr_t res = {};
res.payload = data_rx;
if (srsran_dlsch_nr_decode(&sch_nr_rx, &pdsch_cfg.sch_cfg, &tb, llr, &res) < SRSRAN_SUCCESS) {
ERROR("Error encoding");
goto clean_exit;
}
if (rv == 0) {
if (!crc) {
if (!res.crc) {
ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, tb.tbs);
goto clean_exit;
}

@ -57,7 +57,6 @@ private:
uhd_error test_ad936x_device(uint32_t nof_channels)
{
uhd_error err = set_rx_rate(1.92e6);
if (err != UHD_ERROR_NONE) {
return err;
@ -218,7 +217,6 @@ public:
Info("The device is based on AD9361, get RX stream for checking LIBUSB_TRANSFER_ERROR");
uint32_t ntrials = 10;
do {
// If no error getting RX stream, return
err = test_ad936x_device(nof_channels);
if (err == UHD_ERROR_NONE) {
@ -277,13 +275,13 @@ public:
{
UHD_SAFE_C_SAVE_ERROR(this, timespec = usrp->get_time_now();)
}
uhd_error set_sync_source(const std::string& source) override
uhd_error set_sync_source(const std::string& sync_source, const std::string& clock_source) override
{
Debug("Setting PPS source to '" << source << "'");
Debug("Setting PPS source to '" << sync_source << "' and clock source to '" << clock_source << "'");
#if UHD_VERSION < 3140099
UHD_SAFE_C_SAVE_ERROR(this, usrp->set_clock_source(source); usrp->set_time_source(source);)
UHD_SAFE_C_SAVE_ERROR(this, usrp->set_clock_source(clock_source); usrp->set_time_source(sync_source);)
#else
UHD_SAFE_C_SAVE_ERROR(this, usrp->set_sync_source(source, source);)
UHD_SAFE_C_SAVE_ERROR(this, usrp->set_sync_source(clock_source, sync_source);)
#endif
}
uhd_error get_gain_range(uhd::gain_range_t& tx_gain_range, uhd::gain_range_t& rx_gain_range) override

@ -612,6 +612,12 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels
clock_src = device_addr.pop("clock");
}
// Select same synchronization source only if more than one channel is opened
std::string sync_src = "internal";
if (nof_channels > 1) {
sync_src = clock_src;
}
// Logging level
#ifdef UHD_LOG_INFO
uhd::log::severity_level severity_level = uhd::log::severity_level::info;
@ -788,7 +794,7 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels
std::string sensor_name;
// Set sync source
if (handler->uhd->set_sync_source(clock_src) != UHD_ERROR_NONE) {
if (handler->uhd->set_sync_source(sync_src, clock_src) != UHD_ERROR_NONE) {
print_usrp_error(handler);
return SRSRAN_ERROR;
}

@ -114,7 +114,8 @@ private:
template <class T>
uhd_error parse_param(uhd::device_addr_t& args, const std::string& param, T& value, bool pop = true)
{
UHD_SAFE_C_SAVE_ERROR(this,
UHD_SAFE_C_SAVE_ERROR(
this,
// Check if parameter exists
if (not args.has_key(param)) {
last_error = "RF-NOC requires " + param + " parameter";
@ -236,7 +237,6 @@ private:
}
for (size_t j = 0; j < nof_channels; j++) {
uhd::device_addr_t args;
args.set("input_rate", std::to_string(master_clock_rate));
args.set("fullscale", "1.0");
@ -271,7 +271,6 @@ private:
}
for (size_t j = 0; j < nof_channels; j++) {
uhd::device_addr_t args;
args.set("output_rate", std::to_string(master_clock_rate));
args.set("fullscale", "1.0");
@ -471,15 +470,16 @@ public:
};
uhd_error get_mboard_sensor_names(std::vector<std::string>& sensors) override
{
UHD_SAFE_C_SAVE_ERROR(this, if (device3->get_tree()->exists(TREE_MBOARD_SENSORS)) {
UHD_SAFE_C_SAVE_ERROR(
this, if (device3->get_tree()->exists(TREE_MBOARD_SENSORS)) {
sensors = device3->get_tree()->list(TREE_MBOARD_SENSORS);
})
}
uhd_error get_rx_sensor_names(std::vector<std::string>& sensors) override
{
UHD_SAFE_C_SAVE_ERROR(this, if (device3->get_tree()->exists(TREE_RX_SENSORS)) {
sensors = device3->get_tree()->list(TREE_RX_SENSORS);
})
UHD_SAFE_C_SAVE_ERROR(
this,
if (device3->get_tree()->exists(TREE_RX_SENSORS)) { sensors = device3->get_tree()->list(TREE_RX_SENSORS); })
}
uhd_error get_sensor(const std::string& sensor_name, double& sensor_value) override
{
@ -504,23 +504,28 @@ public:
uhd_error set_time_unknown_pps(const uhd::time_spec_t& timespec) override
{
Info("Setting time " << timespec.get_real_secs() << " at next PPS...");
UHD_SAFE_C_SAVE_ERROR(this, for (auto& r : radio_ctrl) { r->set_time_next_pps(timespec); });
UHD_SAFE_C_SAVE_ERROR(
this,
for (auto& r
: radio_ctrl) { r->set_time_next_pps(timespec); });
}
uhd_error get_time_now(uhd::time_spec_t& timespec) override
{
UHD_SAFE_C_SAVE_ERROR(this, timespec = device3->get_tree()->access<uhd::time_spec_t>(TREE_TIME_NOW).get();
Info("-- " << timespec.get_real_secs());)
}
uhd_error set_sync_source(const std::string& source) override
uhd_error set_sync_source(const std::string& sync_source, const std::string& clock_source) override
{
if (loopback) {
return UHD_ERROR_NONE;
}
UHD_SAFE_C_SAVE_ERROR(this, for (size_t radio_idx = 0; radio_idx < nof_radios; radio_idx++) {
UHD_LOG_DEBUG(radio_id[radio_idx], "Setting sync source to " << source);
radio_ctrl[radio_idx]->set_clock_source(source);
radio_ctrl[radio_idx]->set_time_source(source);
UHD_SAFE_C_SAVE_ERROR(
this, for (size_t radio_idx = 0; radio_idx < nof_radios; radio_idx++) {
UHD_LOG_DEBUG(radio_id[radio_idx],
"Setting PPS source to '" << sync_source << "' and clock source to '" << clock_source << "'");
radio_ctrl[radio_idx]->set_clock_source(clock_source);
radio_ctrl[radio_idx]->set_time_source(sync_source);
})
}
uhd_error get_gain_range(uhd::gain_range_t& tx_gain_range, uhd::gain_range_t& rx_gain_range) override
@ -533,7 +538,8 @@ public:
uhd_error set_master_clock_rate(double rate) override { return UHD_ERROR_NONE; }
uhd_error set_rx_rate(double rate) override
{
UHD_SAFE_C_SAVE_ERROR(this, for (size_t i = 0; i < nof_radios; i++) {
UHD_SAFE_C_SAVE_ERROR(
this, for (size_t i = 0; i < nof_radios; i++) {
for (size_t j = 0; j < nof_channels; j++) {
UHD_LOG_DEBUG(ddc_id[i], "Setting channel " << j << " output rate to " << rate / 1e6 << " MHz");
ddc_ctrl[i]->set_arg("output_rate", std::to_string(rate), j);
@ -542,7 +548,8 @@ public:
}
uhd_error set_tx_rate(double rate) override
{
UHD_SAFE_C_SAVE_ERROR(this, for (size_t i = 0; i < nof_radios; i++) {
UHD_SAFE_C_SAVE_ERROR(
this, for (size_t i = 0; i < nof_radios; i++) {
for (size_t j = 0; j < nof_channels; j++) {
UHD_LOG_DEBUG(duc_id[i], "Setting channel " << j << " input rate to " << rate / 1e6 << " MHz");
duc_ctrl[i]->set_arg("input_rate", std::to_string(rate), j);
@ -665,7 +672,6 @@ public:
}
uhd_error get_rx_gain(double& gain) override
{
if (radio_ctrl.size() == 0) {
return UHD_ERROR_NONE;
}

@ -152,7 +152,7 @@ public:
stream_cmd.stream_now = true;
rx_stream->issue_stream_cmd(stream_cmd);)
}
virtual uhd_error set_sync_source(const std::string& source) = 0;
virtual uhd_error set_sync_source(const std::string& sync_source, const std::string& clock_source) = 0;
virtual uhd_error get_gain_range(uhd::gain_range_t& tx_gain_range, uhd::gain_range_t& rx_gain_range) = 0;
virtual uhd_error set_master_clock_rate(double rate) = 0;
virtual uhd_error set_rx_rate(double rate) = 0;

@ -25,6 +25,13 @@
#define UE_DL_NR_PDCCH_CORR_DEFAULT_THR 0.5f
#define UE_DL_NR_PDCCH_EPRE_DEFAULT_THR -80.0f
/**
* @brief Shifts FFT window a fraction of the cyclic prefix. Set to 0.0f for disabling.
* @note Increases protection against inter-symbol interference in case of synchronization error in expense of computing
* performance
*/
#define UE_DL_NR_FFT_WINDOW_OFFSET 0.5f
static int ue_dl_nr_alloc_prb(srsran_ue_dl_nr_t* q, uint32_t new_nof_prb)
{
if (q->max_prb < new_nof_prb) {
@ -91,6 +98,7 @@ int srsran_ue_dl_nr_init(srsran_ue_dl_nr_t* q, cf_t* input[SRSRAN_MAX_PORTS], co
fft_cfg.nof_prb = args->nof_max_prb;
fft_cfg.symbol_sz = srsran_symbol_sz(args->nof_max_prb);
fft_cfg.keep_dc = true;
fft_cfg.rx_window_offset = UE_DL_NR_FFT_WINDOW_OFFSET;
for (uint32_t i = 0; i < q->nof_rx_antennas; i++) {
fft_cfg.in_buffer = input[i];
@ -165,6 +173,7 @@ int srsran_ue_dl_nr_set_carrier(srsran_ue_dl_nr_t* q, const srsran_carrier_nr_t*
cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb);
cfg.cp = SRSRAN_CP_NORM;
cfg.keep_dc = true;
cfg.rx_window_offset = UE_DL_NR_FFT_WINDOW_OFFSET;
srsran_ofdm_rx_init_cfg(&q->fft[i], &cfg);
}
}
@ -585,7 +594,7 @@ int srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q,
len += srsran_pdsch_nr_rx_info(&q->pdsch, cfg, &cfg->grant, res, &str[len], str_len - len);
// Append channel estimator info
len = srsran_print_check(str, str_len, len, ",SNR=%+.1f", q->chest.snr_db);
len = srsran_print_check(str, str_len, len, "SNR=%+.1f", q->chest.snr_db);
return len;
}

@ -1719,14 +1719,13 @@ void srsran_vec_apply_cfo_simd(const cf_t* x, float cfo, cf_t* z, int len)
}
} else {
for (; i < len - SRSRAN_SIMD_F_SIZE + 1; i += SRSRAN_SIMD_F_SIZE) {
for (; i < len - SRSRAN_SIMD_CF_SIZE + 1; i += SRSRAN_SIMD_CF_SIZE) {
simd_cf_t a = srsran_simd_cfi_loadu(&x[i]);
simd_cf_t r = srsran_simd_cf_prod(a, _simd_phase);
_simd_phase = srsran_simd_cf_prod(_simd_phase, _simd_osc);
srsran_simd_cfi_storeu(&z[i], r);
}
_simd_phase = srsran_simd_cf_prod(_simd_phase, _simd_osc);
}
}
#endif

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include "fmt/format-inl.h"
#include <mutex>
FMT_BEGIN_NAMESPACE
namespace detail {
@ -23,6 +24,79 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
#define NODE_POOL_SIZE (10000u)
static constexpr uint8_t memory_heap_tag = 0xAA;
class dyn_node_pool
{
/// The extra byte is used to store the memory tag at position 0 in the array.
using type = std::array<uint8_t, dynamic_arg_list::max_pool_node_size + 1>;
public:
dyn_node_pool() {
pool.resize(NODE_POOL_SIZE);
free_list.reserve(NODE_POOL_SIZE);
for (auto& elem : pool) {
free_list.push_back(elem.data());
}
}
dyn_node_pool(const dyn_node_pool&) = delete;
dyn_node_pool(dyn_node_pool&&) = delete;
dyn_node_pool& operator=(dyn_node_pool&&) = delete;
dyn_node_pool& operator=(const dyn_node_pool&) = delete;
void* alloc(std::size_t sz) {
assert(sz <= dynamic_arg_list::max_pool_node_size && "Object is too large to fit in the pool");
std::lock_guard<std::mutex> lock(m);
if (free_list.empty()) {
// Tag that this allocation was performed by the heap.
auto *p = new type;
(*p)[0] = memory_heap_tag;
return p->data() + 1;
}
auto* p = free_list.back();
free_list.pop_back();
// Tag that this allocation was performed by the pool.
p[0] = 0;
return p + 1;
}
void dealloc(void* p) {
if (!p) {
return;
}
std::lock_guard<std::mutex> lock(m);
uint8_t* base_ptr = reinterpret_cast<uint8_t *>(p) - 1;
if (*base_ptr == memory_heap_tag) {
// This pointer was allocated using the heap.
delete reinterpret_cast<type *>(base_ptr);
return;
}
free_list.push_back(base_ptr);
}
private:
std::vector<type> pool;
std::vector<uint8_t *> free_list;
mutable std::mutex m;
};
static dyn_node_pool node_pool;
void *dynamic_arg_list::allocate_from_pool(std::size_t sz) {
return node_pool.alloc(sz);
}
void dynamic_arg_list::free_from_pool(void *ptr) {
return node_pool.dealloc(ptr);
}
} // namespace detail
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;

@ -56,7 +56,7 @@ static void format_metadata(const detail::log_entry_metadata& metadata, fmt::mem
// Format optional fields if present.
if (!metadata.log_name.empty()) {
fmt::format_to(buffer, "[{: <4}] ", metadata.log_name);
fmt::format_to(buffer, "[{: <7}] ", metadata.log_name);
}
if (metadata.log_tag != '\0') {
fmt::format_to(buffer, "[{}] ", metadata.log_tag);

@ -37,13 +37,10 @@ static std::string remove_sharp_chars(const std::string& s)
/// Generic argument function that fetches a log channel from the repository.
template <typename... Args>
static log_channel& fetch_log_channel_helper(const std::string& id,
Args&&... args)
static log_channel& fetch_log_channel_helper(const std::string& id, Args&&... args)
{
return srslog_instance::get().get_channel_repo().emplace(
std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(id, std::forward<Args>(args)...));
std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(id, std::forward<Args>(args)...));
}
///
@ -60,21 +57,27 @@ log_channel& srslog::fetch_log_channel(const std::string& id)
assert(!id.empty() && "Empty id string");
std::string clean_id = remove_sharp_chars(id);
if (auto* c = find_log_channel(clean_id)) {
return *c;
}
srslog_instance& instance = srslog_instance::get();
return fetch_log_channel_helper(
clean_id, instance.get_default_sink(), instance.get_backend());
return fetch_log_channel_helper(clean_id, instance.get_default_sink(), instance.get_backend());
}
log_channel& srslog::fetch_log_channel(const std::string& id,
sink& s,
log_channel_config config)
log_channel& srslog::fetch_log_channel(const std::string& id, sink& s, log_channel_config config)
{
assert(!id.empty() && "Empty id string");
std::string clean_id = remove_sharp_chars(id);
if (auto* c = find_log_channel(clean_id)) {
return *c;
}
srslog_instance& instance = srslog_instance::get();
return fetch_log_channel_helper(
clean_id, s, instance.get_backend(), std::move(config));
return fetch_log_channel_helper(clean_id, s, instance.get_backend(), std::move(config));
}
///
@ -121,40 +124,46 @@ sink* srslog::find_sink(const std::string& id)
return (ptr) ? ptr->get() : nullptr;
}
sink& srslog::fetch_stdout_sink(const std::string& id,
std::unique_ptr<log_formatter> f)
sink& srslog::fetch_stdout_sink(const std::string& id, std::unique_ptr<log_formatter> f)
{
assert(!id.empty() && "Empty id string");
if (auto* s = find_sink(id)) {
return *s;
}
auto& s = srslog_instance::get().get_sink_repo().emplace(
std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(
new stream_sink(sink_stream_type::stdout, std::move(f))));
std::forward_as_tuple(new stream_sink(sink_stream_type::stdout, std::move(f))));
return *s;
}
sink& srslog::fetch_stderr_sink(const std::string& id,
std::unique_ptr<log_formatter> f)
sink& srslog::fetch_stderr_sink(const std::string& id, std::unique_ptr<log_formatter> f)
{
assert(!id.empty() && "Empty id string");
if (auto* s = find_sink(id)) {
return *s;
}
auto& s = srslog_instance::get().get_sink_repo().emplace(
std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(
new stream_sink(sink_stream_type::stderr, std::move(f))));
std::forward_as_tuple(new stream_sink(sink_stream_type::stderr, std::move(f))));
return *s;
}
sink& srslog::fetch_file_sink(const std::string& path,
size_t max_size,
std::unique_ptr<log_formatter> f)
sink& srslog::fetch_file_sink(const std::string& path, size_t max_size, std::unique_ptr<log_formatter> f)
{
assert(!path.empty() && "Empty path string");
if (auto* s = find_sink(path)) {
return *s;
}
//:TODO: GCC5 or lower versions emits an error if we use the new() expression
// directly, use redundant piecewise_construct instead.
auto& s = srslog_instance::get().get_sink_repo().emplace(
@ -170,8 +179,7 @@ bool srslog::install_custom_sink(const std::string& id, std::unique_ptr<sink> s)
assert(!id.empty() && "Empty path string");
sink* input_sink = s.get();
sink* returned_sink =
srslog_instance::get().get_sink_repo().emplace(id, std::move(s)).get();
sink* returned_sink = srslog_instance::get().get_sink_repo().emplace(id, std::move(s)).get();
// Successful insertion occurs when the returned object is the same one as the
// input object.
@ -208,8 +216,8 @@ void srslog::flush()
detail::log_entry cmd;
cmd.metadata.store = nullptr;
cmd.flush_cmd = std::unique_ptr<detail::flush_backend_cmd>(
new detail::flush_backend_cmd{completion_flag, std::move(sinks)});
cmd.flush_cmd =
std::unique_ptr<detail::flush_backend_cmd>(new detail::flush_backend_cmd{completion_flag, std::move(sinks)});
// Make sure the flush command gets into the backend, otherwise we will be
// stuck waiting forever for the command to succeed.
@ -236,12 +244,10 @@ detail::any* srslog::detail::find_logger(const std::string& id)
return srslog_instance::get().get_logger_repo().find(id);
}
detail::any* srslog::detail::fetch_logger(const std::string& id,
detail::any&& logger)
detail::any* srslog::detail::fetch_logger(const std::string& id, detail::any&& logger)
{
assert(!id.empty() && "Empty id string");
return &srslog_instance::get().get_logger_repo().emplace(id,
std::move(logger));
return &srslog_instance::get().get_logger_repo().emplace(id, std::move(logger));
}
/// Builds a logger name out of the id and tag.
@ -251,9 +257,7 @@ static std::string build_logger_name(const std::string& id, char tag)
}
/// Fetches a logger with all its log channels.
static basic_logger& fetch_basic_logger_helper(const std::string& id,
sink& s,
bool should_print_context)
static basic_logger& fetch_basic_logger_helper(const std::string& id, sink& s, bool should_print_context)
{
static constexpr char basic_logger_chan_tags[] = {'E', 'W', 'I', 'D'};
@ -262,23 +266,22 @@ static basic_logger& fetch_basic_logger_helper(const std::string& id,
// User created log channels cannot have ids with a # character, encode the
// ids here with a # to ensure all channels are unique.
log_channel& error = fetch_log_channel_helper(
build_logger_name(id, basic_logger_chan_tags[0]),
log_channel& error =
fetch_log_channel_helper(build_logger_name(id, basic_logger_chan_tags[0]),
s,
instance.get_backend(),
log_channel_config{id, basic_logger_chan_tags[0], should_print_context});
log_channel& warning = fetch_log_channel_helper(
build_logger_name(id, basic_logger_chan_tags[1]),
log_channel& warning =
fetch_log_channel_helper(build_logger_name(id, basic_logger_chan_tags[1]),
s,
instance.get_backend(),
log_channel_config{id, basic_logger_chan_tags[1], should_print_context});
log_channel& info = fetch_log_channel_helper(
build_logger_name(id, basic_logger_chan_tags[2]),
log_channel& info = fetch_log_channel_helper(build_logger_name(id, basic_logger_chan_tags[2]),
s,
instance.get_backend(),
log_channel_config{id, basic_logger_chan_tags[2], should_print_context});
log_channel& debug = fetch_log_channel_helper(
build_logger_name(id, basic_logger_chan_tags[3]),
log_channel& debug =
fetch_log_channel_helper(build_logger_name(id, basic_logger_chan_tags[3]),
s,
instance.get_backend(),
log_channel_config{id, basic_logger_chan_tags[3], should_print_context});
@ -286,19 +289,25 @@ static basic_logger& fetch_basic_logger_helper(const std::string& id,
return fetch_logger<basic_logger>(id, error, warning, info, debug);
}
basic_logger& srslog::fetch_basic_logger(const std::string& id,
bool should_print_context)
basic_logger& srslog::fetch_basic_logger(const std::string& id, bool should_print_context)
{
assert(!id.empty() && "Empty id string");
return fetch_basic_logger_helper(
id, srslog_instance::get().get_default_sink(), should_print_context);
if (auto* logger = find_logger<basic_logger>(id)) {
return *logger;
}
basic_logger& srslog::fetch_basic_logger(const std::string& id,
sink& s,
bool should_print_context)
return fetch_basic_logger_helper(id, srslog_instance::get().get_default_sink(), should_print_context);
}
basic_logger& srslog::fetch_basic_logger(const std::string& id, sink& s, bool should_print_context)
{
assert(!id.empty() && "Empty id string");
if (auto* logger = find_logger<basic_logger>(id)) {
return *logger;
}
return fetch_basic_logger_helper(id, s, should_print_context);
}
@ -308,31 +317,26 @@ basic_logger& srslog::fetch_basic_logger(const std::string& id,
/// Creates and registers a log channel. Returns a pointer to the newly created
/// channel on success, otherwise nullptr.
static log_channel* create_and_register_log_channel(const std::string& id,
sink& s)
static log_channel* create_and_register_log_channel(const std::string& id, sink& s)
{
assert(!id.empty() && "Empty id string");
srslog_instance& instance = srslog_instance::get();
auto& p = instance.get_channel_repo().emplace(
std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(id, s, instance.get_backend()));
std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(id, s, instance.get_backend()));
return &p;
}
static log_channel* create_and_register_log_channel(const std::string& id,
log_channel_config config,
sink& s)
static log_channel* create_and_register_log_channel(const std::string& id, log_channel_config config, sink& s)
{
assert(!id.empty() && "Empty id string");
srslog_instance& instance = srslog_instance::get();
auto& p = instance.get_channel_repo().emplace(
std::piecewise_construct,
auto& p =
instance.get_channel_repo().emplace(std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(id, s, instance.get_backend(), std::move(config)));
@ -373,16 +377,11 @@ sink* srslog::create_file_sink(const std::string& path, size_t max_size)
.get_sink_repo()
.emplace(std::piecewise_construct,
std::forward_as_tuple(path),
std::forward_as_tuple(new file_sink(
path,
max_size,
std::unique_ptr<log_formatter>(new text_formatter))))
std::forward_as_tuple(new file_sink(path, max_size, std::unique_ptr<log_formatter>(new text_formatter))))
.get();
}
basic_logger* srslog::create_basic_logger(const std::string& id,
sink& s,
bool should_print_context)
basic_logger* srslog::create_basic_logger(const std::string& id, sink& s, bool should_print_context)
{
assert(!id.empty() && "Empty id string");
@ -400,24 +399,16 @@ basic_logger* srslog::create_basic_logger(const std::string& id,
// without any id clashes.
log_channel* error = create_and_register_log_channel(
build_logger_name(id, basic_logger_chan_tags[0]),
{id, basic_logger_chan_tags[0], should_print_context},
s);
build_logger_name(id, basic_logger_chan_tags[0]), {id, basic_logger_chan_tags[0], should_print_context}, s);
assert(error && "Could not create channel");
log_channel* warning = create_and_register_log_channel(
build_logger_name(id, basic_logger_chan_tags[1]),
{id, basic_logger_chan_tags[1], should_print_context},
s);
build_logger_name(id, basic_logger_chan_tags[1]), {id, basic_logger_chan_tags[1], should_print_context}, s);
assert(warning && "Could not create channel");
log_channel* info = create_and_register_log_channel(
build_logger_name(id, basic_logger_chan_tags[2]),
{id, basic_logger_chan_tags[2], should_print_context},
s);
build_logger_name(id, basic_logger_chan_tags[2]), {id, basic_logger_chan_tags[2], should_print_context}, s);
assert(info && "Could not create channel");
log_channel* debug = create_and_register_log_channel(
build_logger_name(id, basic_logger_chan_tags[3]),
{id, basic_logger_chan_tags[3], should_print_context},
s);
build_logger_name(id, basic_logger_chan_tags[3]), {id, basic_logger_chan_tags[3], should_print_context}, s);
assert(debug && "Could not create channel");
return create_logger<basic_logger>(id, *error, *warning, *info, *debug);

@ -114,17 +114,14 @@ void pdcp::add_bearer(uint32_t lcid, pdcp_config_t cfg)
logger.error("Error inserting PDCP entity in to array.");
return;
}
logger.info("Add %s (lcid=%d, bearer_id=%d, sn_len=%dbits)",
rrc->get_rb_name(lcid).c_str(),
lcid,
cfg.bearer_id,
cfg.sn_len);
logger.info(
"Add %s (lcid=%d, bearer_id=%d, sn_len=%dbits)", rrc->get_rb_name(lcid), lcid, cfg.bearer_id, cfg.sn_len);
{
std::lock_guard<std::mutex> lock(cache_mutex);
valid_lcids_cached.insert(lcid);
}
} else {
logger.info("Bearer %s already configured.", rrc->get_rb_name(lcid).c_str());
logger.info("Bearer %s already configured.", rrc->get_rb_name(lcid));
}
}
@ -138,21 +135,14 @@ void pdcp::add_bearer_mrb(uint32_t lcid, pdcp_config_t cfg)
return;
}
if (not pdcp_array_mrb
.insert(std::make_pair(
lcid,
std::move(entity)))
.second) {
if (not pdcp_array_mrb.insert(std::make_pair(lcid, std::move(entity))).second) {
logger.error("Error inserting PDCP entity in to array.");
return;
}
logger.info("Add %s (lcid=%d, bearer_id=%d, sn_len=%dbits)",
rrc->get_rb_name(lcid).c_str(),
lcid,
cfg.bearer_id,
cfg.sn_len);
logger.info(
"Add %s (lcid=%d, bearer_id=%d, sn_len=%dbits)", rrc->get_rb_name(lcid), lcid, cfg.bearer_id, cfg.sn_len);
} else {
logger.warning("Bearer %s already configured. Reconfiguration not supported", rrc->get_rb_name(lcid).c_str());
logger.warning("Bearer %s already configured. Reconfiguration not supported", rrc->get_rb_name(lcid));
}
}
@ -164,9 +154,9 @@ void pdcp::del_bearer(uint32_t lcid)
}
if (valid_lcid(lcid)) {
pdcp_array.erase(lcid);
logger.warning("Deleted PDCP bearer %s", rrc->get_rb_name(lcid).c_str());
logger.warning("Deleted PDCP bearer %s", rrc->get_rb_name(lcid));
} else {
logger.warning("Can't delete bearer %s. Bearer doesn't exist.", rrc->get_rb_name(lcid).c_str());
logger.warning("Can't delete bearer %s. Bearer doesn't exist.", rrc->get_rb_name(lcid));
}
}
@ -189,7 +179,7 @@ void pdcp::change_lcid(uint32_t old_lcid, uint32_t new_lcid)
logger.warning("Changed LCID of PDCP bearer from %d to %d", old_lcid, new_lcid);
} else {
logger.error("Can't change PDCP of bearer %s from %d to %d. Bearer doesn't exist or new LCID already occupied.",
rrc->get_rb_name(old_lcid).c_str(),
rrc->get_rb_name(old_lcid),
old_lcid,
new_lcid);
}

@ -79,7 +79,7 @@ bool pdcp_entity_lte::configure(const pdcp_config_t& cnfg_)
// Queue Helpers
maximum_allocated_sns_window = (1u << cfg.sn_len) / 2u;
logger.info("Init %s with bearer ID: %d", rrc->get_rb_name(lcid).c_str(), cfg.bearer_id);
logger.info("Init %s with bearer ID: %d", rrc->get_rb_name(lcid), cfg.bearer_id);
logger.info("SN len bits: %d, SN len bytes: %d, reordering window: %d, Maximum SN: %d, discard timer: %d ms",
cfg.sn_len,
cfg.hdr_len_bytes,
@ -104,7 +104,7 @@ bool pdcp_entity_lte::configure(const pdcp_config_t& cnfg_)
// Reestablishment procedure: 36.323 5.2
void pdcp_entity_lte::reestablish()
{
logger.info("Re-establish %s with bearer ID: %d", rrc->get_rb_name(lcid).c_str(), cfg.bearer_id);
logger.info("Re-establish %s with bearer ID: %d", rrc->get_rb_name(lcid), cfg.bearer_id);
// For SRBs
if (is_srb()) {
st.next_pdcp_tx_sn = 0;
@ -126,7 +126,7 @@ void pdcp_entity_lte::reestablish()
void pdcp_entity_lte::reset()
{
if (active) {
logger.debug("Reset %s", rrc->get_rb_name(lcid).c_str());
logger.debug("Reset %s", rrc->get_rb_name(lcid));
}
active = false;
}
@ -135,7 +135,7 @@ void pdcp_entity_lte::reset()
void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn)
{
if (rlc->sdu_queue_is_full(lcid)) {
logger.info(sdu->msg, sdu->N_bytes, "Dropping %s SDU due to full queue", rrc->get_rb_name(lcid).c_str());
logger.info(sdu->msg, sdu->N_bytes, "Dropping %s SDU due to full queue", rrc->get_rb_name(lcid));
return;
}
@ -157,7 +157,7 @@ void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn)
if (!rlc->rb_is_um(lcid) and is_drb()) {
if (not store_sdu(used_sn, sdu)) {
// Could not store the SDU, discarding
logger.info("Could not store SDU. Discarding %d\n", used_sn);
logger.warning("Could not store SDU. Discarding SN=%d", used_sn);
return;
}
}
@ -190,7 +190,7 @@ void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn)
logger.info(sdu->msg,
sdu->N_bytes,
"TX %s PDU, SN=%d, integrity=%s, encryption=%s",
rrc->get_rb_name(lcid).c_str(),
rrc->get_rb_name(lcid),
used_sn,
srsran_direction_text[integrity_direction],
srsran_direction_text[encryption_direction]);
@ -242,7 +242,7 @@ void pdcp_entity_lte::write_pdu(unique_byte_buffer_t pdu)
logger.info(pdu->msg,
pdu->N_bytes,
"%s Rx PDU SN=%d (%d B, integrity=%s, encryption=%s)",
rrc->get_rb_name(lcid).c_str(),
rrc->get_rb_name(lcid),
sn,
pdu->N_bytes,
srsran_direction_text[integrity_direction],
@ -302,7 +302,7 @@ void pdcp_entity_lte::handle_srb_pdu(srsran::unique_byte_buffer_t pdu)
cipher_decrypt(&pdu->msg[cfg.hdr_len_bytes], pdu->N_bytes - cfg.hdr_len_bytes, count, &pdu->msg[cfg.hdr_len_bytes]);
}
logger.debug(pdu->msg, pdu->N_bytes, "%s Rx SDU SN=%d", rrc->get_rb_name(lcid).c_str(), sn);
logger.debug(pdu->msg, pdu->N_bytes, "%s Rx SDU SN=%d", rrc->get_rb_name(lcid), sn);
// Extract MAC
uint8_t mac[4];
@ -311,7 +311,7 @@ void pdcp_entity_lte::handle_srb_pdu(srsran::unique_byte_buffer_t pdu)
// Perfrom integrity checks
if (integrity_direction == DIRECTION_RX || integrity_direction == DIRECTION_TXRX) {
if (not integrity_verify(pdu->msg, pdu->N_bytes, count, mac)) {
logger.error(pdu->msg, pdu->N_bytes, "%s Dropping PDU", rrc->get_rb_name(lcid).c_str());
logger.error(pdu->msg, pdu->N_bytes, "%s Dropping PDU", rrc->get_rb_name(lcid));
return; // Discard
}
}
@ -349,7 +349,7 @@ void pdcp_entity_lte::handle_um_drb_pdu(srsran::unique_byte_buffer_t pdu)
cipher_decrypt(pdu->msg, pdu->N_bytes, count, pdu->msg);
}
logger.debug(pdu->msg, pdu->N_bytes, "%s Rx PDU SN=%d", rrc->get_rb_name(lcid).c_str(), sn);
logger.debug(pdu->msg, pdu->N_bytes, "%s Rx PDU SN=%d", rrc->get_rb_name(lcid), sn);
st.next_pdcp_rx_sn = sn + 1;
if (st.next_pdcp_rx_sn > maximum_pdcp_sn) {
@ -413,7 +413,7 @@ void pdcp_entity_lte::handle_am_drb_pdu(srsran::unique_byte_buffer_t pdu)
// Decrypt
cipher_decrypt(pdu->msg, pdu->N_bytes, count, pdu->msg);
logger.debug(pdu->msg, pdu->N_bytes, "%s Rx SDU SN=%d", rrc->get_rb_name(lcid).c_str(), sn);
logger.debug(pdu->msg, pdu->N_bytes, "%s Rx SDU SN=%d", rrc->get_rb_name(lcid), sn);
// Update info on last PDU submitted to upper layers
st.last_submitted_pdcp_rx_sn = sn;
@ -697,7 +697,7 @@ bool pdcp_entity_lte::store_sdu(uint32_t sn, const unique_byte_buffer_t& sdu)
// Discard Timer Callback (discardTimer)
void pdcp_entity_lte::discard_callback::operator()(uint32_t timer_id)
{
parent->logger.debug("Discard timer expired for PDU with SN = %d", discard_sn);
parent->logger.info("Discard timer for SN=%d expired", discard_sn);
// Notify the RLC of the discard. It's the RLC to actually discard, if no segment was transmitted yet.
parent->rlc->discard_sdu(parent->lcid, discard_sn);

@ -46,7 +46,7 @@ pdcp_entity_nr::~pdcp_entity_nr() {}
// Reestablishment procedure: 38.323 5.2
void pdcp_entity_nr::reestablish()
{
logger.info("Re-establish %s with bearer ID: %d", rrc->get_rb_name(lcid).c_str(), cfg.bearer_id);
logger.info("Re-establish %s with bearer ID: %d", rrc->get_rb_name(lcid), cfg.bearer_id);
// TODO
}
@ -70,7 +70,7 @@ bool pdcp_entity_nr::configure(const pdcp_config_t& cnfg_)
void pdcp_entity_nr::reset()
{
active = false;
logger.debug("Reset %s", rrc->get_rb_name(lcid).c_str());
logger.debug("Reset %s", rrc->get_rb_name(lcid));
}
// SDAP/RRC interface
@ -80,7 +80,7 @@ void pdcp_entity_nr::write_sdu(unique_byte_buffer_t sdu, int sn)
logger.info(sdu->msg,
sdu->N_bytes,
"TX %s SDU, integrity=%s, encryption=%s",
rrc->get_rb_name(lcid).c_str(),
rrc->get_rb_name(lcid),
srsran_direction_text[integrity_direction],
srsran_direction_text[encryption_direction]);
@ -136,7 +136,7 @@ void pdcp_entity_nr::write_pdu(unique_byte_buffer_t pdu)
logger.info(pdu->msg,
pdu->N_bytes,
"RX %s PDU (%d B), integrity=%s, encryption=%s",
rrc->get_rb_name(lcid).c_str(),
rrc->get_rb_name(lcid),
pdu->N_bytes,
srsran_direction_text[integrity_direction],
srsran_direction_text[encryption_direction]);

@ -157,7 +157,7 @@ void rlc::reestablish()
void rlc::reestablish(uint32_t lcid)
{
if (valid_lcid(lcid)) {
logger.info("Reestablishing %s", rrc->get_rb_name(lcid).c_str());
logger.info("Reestablishing %s", rrc->get_rb_name(lcid));
rlc_array.at(lcid)->reestablish();
} else {
logger.warning("RLC LCID %d doesn't exist.", lcid);
@ -451,7 +451,7 @@ void rlc::add_bearer(uint32_t lcid, const rlc_config_t& cnfg)
logger.error("Error inserting RLC entity in to array.");
goto delete_and_exit;
}
logger.info("Added radio bearer %s in %s", rrc->get_rb_name(lcid).c_str(), to_string(cnfg.rlc_mode).c_str());
logger.info("Added radio bearer %s in %s", rrc->get_rb_name(lcid), to_string(cnfg.rlc_mode).c_str());
rlc_entity = NULL;
}
@ -463,7 +463,7 @@ void rlc::add_bearer(uint32_t lcid, const rlc_config_t& cnfg)
}
}
logger.info("Configured radio bearer %s in %s", rrc->get_rb_name(lcid).c_str(), to_string(cnfg.rlc_mode).c_str());
logger.info("Configured radio bearer %s in %s", rrc->get_rb_name(lcid), to_string(cnfg.rlc_mode).c_str());
delete_and_exit:
if (rlc_entity) {
@ -510,9 +510,9 @@ void rlc::del_bearer(uint32_t lcid)
it->second->stop();
delete (it->second);
rlc_array.erase(it);
logger.warning("Deleted RLC bearer %s", rrc->get_rb_name(lcid).c_str());
logger.warning("Deleted RLC bearer %s", rrc->get_rb_name(lcid));
} else {
logger.error("Can't delete bearer %s. Bearer doesn't exist.", rrc->get_rb_name(lcid).c_str());
logger.error("Can't delete bearer %s. Bearer doesn't exist.", rrc->get_rb_name(lcid));
}
}
@ -525,9 +525,9 @@ void rlc::del_bearer_mrb(uint32_t lcid)
it->second->stop();
delete (it->second);
rlc_array_mrb.erase(it);
logger.warning("Deleted RLC MRB bearer %s", rrc->get_rb_name(lcid).c_str());
logger.warning("Deleted RLC MRB bearer %s", rrc->get_rb_name(lcid));
} else {
logger.error("Can't delete bearer %s. Bearer doesn't exist.", rrc->get_rb_name(lcid).c_str());
logger.error("Can't delete bearer %s. Bearer doesn't exist.", rrc->get_rb_name(lcid));
}
}
@ -554,7 +554,7 @@ void rlc::change_lcid(uint32_t old_lcid, uint32_t new_lcid)
}
} else {
logger.error("Can't change LCID of bearer %s from %d to %d. Bearer doesn't exist or new LCID already occupied.",
rrc->get_rb_name(old_lcid).c_str(),
rrc->get_rb_name(old_lcid),
old_lcid,
new_lcid);
}
@ -565,26 +565,26 @@ void rlc::suspend_bearer(uint32_t lcid)
{
if (valid_lcid(lcid)) {
if (rlc_array.at(lcid)->suspend()) {
logger.info("Suspended radio bearer %s", rrc->get_rb_name(lcid).c_str());
logger.info("Suspended radio bearer %s", rrc->get_rb_name(lcid));
} else {
logger.error("Error suspending RLC entity: bearer already suspended.");
}
} else {
logger.error("Suspending bearer: bearer %s not configured.", rrc->get_rb_name(lcid).c_str());
logger.error("Suspending bearer: bearer %s not configured.", rrc->get_rb_name(lcid));
}
}
void rlc::resume_bearer(uint32_t lcid)
{
logger.info("Resuming radio bearer %s", rrc->get_rb_name(lcid).c_str());
logger.info("Resuming radio bearer %s", rrc->get_rb_name(lcid));
if (valid_lcid(lcid)) {
if (rlc_array.at(lcid)->resume()) {
logger.info("Resumed radio bearer %s", rrc->get_rb_name(lcid).c_str());
logger.info("Resumed radio bearer %s", rrc->get_rb_name(lcid));
} else {
logger.error("Error resuming RLC entity: bearer not suspended.");
}
} else {
logger.error("Resuming bearer: bearer %s not configured.", rrc->get_rb_name(lcid).c_str());
logger.error("Resuming bearer: bearer %s not configured.", rrc->get_rb_name(lcid));
}
}

@ -20,6 +20,7 @@
*/
#include "srsran/upper/rlc_tm.h"
#include "srsran/common/lte_common.h"
#include "srsran/interfaces/ue_pdcp_interfaces.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
@ -91,14 +92,14 @@ void rlc_tm::write_sdu(unique_byte_buffer_t sdu)
logger.info(msg_ptr,
nof_bytes,
"%s Tx SDU, queue size=%d, bytes=%d",
rrc->get_rb_name(lcid).c_str(),
rrc->get_rb_name(lcid),
ul_queue.size(),
ul_queue.size_bytes());
} else {
logger.warning(ret.error()->msg,
ret.error()->N_bytes,
"[Dropped SDU] %s Tx SDU, queue size=%d, bytes=%d",
rrc->get_rb_name(lcid).c_str(),
rrc->get_rb_name(lcid),
ul_queue.size(),
ul_queue.size_bytes());
}
@ -146,8 +147,7 @@ int rlc_tm::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
uint32_t pdu_size = ul_queue.size_tail_bytes();
if (pdu_size > nof_bytes) {
logger.info(
"%s Tx PDU size larger than MAC opportunity (%d > %d)", rrc->get_rb_name(lcid).c_str(), pdu_size, nof_bytes);
logger.info("%s Tx PDU size larger than MAC opportunity (%d > %d)", rrc->get_rb_name(lcid), pdu_size, nof_bytes);
return -1;
}
unique_byte_buffer_t buf;
@ -155,12 +155,12 @@ int rlc_tm::read_pdu(uint8_t* payload, uint32_t nof_bytes)
pdu_size = buf->N_bytes;
memcpy(payload, buf->msg, buf->N_bytes);
logger.debug("%s Complete SDU scheduled for tx. Stack latency: %" PRIu64 " us",
rrc->get_rb_name(lcid).c_str(),
rrc->get_rb_name(lcid),
(uint64_t)buf->get_latency_us().count());
logger.info(payload,
pdu_size,
"%s Tx %s PDU, queue size=%d, bytes=%d",
rrc->get_rb_name(lcid).c_str(),
rrc->get_rb_name(lcid),
srsran::to_string(rlc_mode_t::tm).c_str(),
ul_queue.size(),
ul_queue.size_bytes());
@ -186,7 +186,7 @@ void rlc_tm::write_pdu(uint8_t* payload, uint32_t nof_bytes)
buf->set_timestamp();
metrics.num_rx_pdu_bytes += nof_bytes;
metrics.num_rx_pdus++;
if (rrc->get_rb_name(lcid) == "SRB0") {
if (srsran::srb_to_lcid(srsran::lte_srb::srb0) == lcid) {
rrc->write_pdu(lcid, std::move(buf));
} else {
pdcp->write_pdu(lcid, std::move(buf));

@ -19,20 +19,13 @@
*
*/
#include "srsran/common/test_common.h"
#include "srsran/common/timers.h"
#include <iostream>
#include <random>
#include <srsran/common/tti_sync_cv.h>
#include <thread>
#define TESTASSERT(cond) \
do { \
if (!(cond)) { \
std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \
return -1; \
} \
} while (0)
using namespace srsran;
int timers_test1()
@ -51,8 +44,7 @@ int timers_test1()
// TEST: Run multiple times with the same duration
bool callback_called = false;
t.set(dur, [&callback_called](int) { callback_called = true; });
TESTASSERT(timers.get_cur_time() == 0);
t.set(dur, [&callback_called](int tid) { callback_called = true; });
for (uint32_t runs = 0; runs < 3; ++runs) {
callback_called = false;
TESTASSERT(not t.is_running());
@ -66,7 +58,6 @@ int timers_test1()
TESTASSERT(not t.is_running() and t.is_expired());
TESTASSERT(callback_called);
}
TESTASSERT(timers.get_cur_time() == 3 * dur);
// TEST: interrupt a timer. check if callback was called
callback_called = false;
@ -330,10 +321,7 @@ int timers_test5()
std::string string = "test string";
timers.defer_callback(2, [&vals, string]() {
vals.push_back(2);
if (string != "test string") {
ERROR("string was not captured correctly");
exit(-1);
}
srsran_assert(string == "test string", "string was not captured correctly");
});
}
timers.defer_callback(6, [&vals]() { vals.push_back(3); });

@ -633,6 +633,31 @@ int mac_dl_sch_pdu_unpack_and_pack_test6()
return SRSRAN_SUCCESS;
}
int mac_ul_sch_pdu_unpack_test6()
{
// Malformed MAC PDU
uint8_t tv[] = {0x04, 0xe7, 0x00, 0x80, 0x04, 0x45, 0x00, 0x00, 0xe4, 0x4b, 0x9d, 0x40, 0x00, 0x40, 0x01, 0x69, 0x28,
0xc0, 0xa8, 0x02, 0x02, 0xc0, 0xa8, 0x02, 0x01, 0x08, 0x00, 0x3a, 0xbc, 0x65, 0x4a, 0x00, 0x01, 0x94,
0x85, 0x70, 0x60, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11,
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44,
0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97};
if (pcap_handle) {
pcap_handle->write_ul_crnti_nr(tv, sizeof(tv), PCAP_CRNTI, true, PCAP_TTI);
}
srsran::mac_sch_pdu_nr pdu(true);
TESTASSERT(pdu.unpack(tv, sizeof(tv)) == SRSRAN_ERROR);
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
#if PCAP
@ -721,6 +746,11 @@ int main(int argc, char** argv)
return SRSRAN_ERROR;
}
if (mac_ul_sch_pdu_unpack_test6()) {
fprintf(stderr, "mac_ul_sch_pdu_unpack_test6() failed.\n");
return SRSRAN_ERROR;
}
if (pcap_handle) {
pcap_handle->close();
}

@ -42,6 +42,8 @@ static uint32_t mcs = 30; // Set to 30 for steering
static srsran_sch_cfg_nr_t pdsch_cfg = {};
static uint32_t nof_slots = 10;
static uint32_t rv_idx = 0;
static uint32_t delay_n = 4; // Integer delay
static float cfo_hz = 100.0f; // CFO Hz
static void usage(char* prog)
{
@ -55,13 +57,15 @@ static void usage(char* prog)
srsran_mcs_table_to_str(pdsch_cfg.sch_cfg.mcs_table));
printf("\t-R Reserve RE: [rb_begin] [rb_end] [rb_stride] [sc_mask] [symbol_mask]\n");
printf("\t-L Provide number of layers [Default %d]\n", carrier.max_mimo_layers);
printf("\t-D Delay signal an integer number of samples [Default %d samples]\n", delay_n);
printf("\t-C Frequency shift (CFO) signal in Hz [Default %+.0f Hz]\n", cfo_hz);
printf("\t-v [set srsran_verbose to debug, default none]\n");
}
static int parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "rRPpmnTLv")) != -1) {
while ((opt = getopt(argc, argv, "rRPpmnTLDCv")) != -1) {
switch (opt) {
case 'P':
carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
@ -102,6 +106,12 @@ static int parse_args(int argc, char** argv)
case 'L':
carrier.max_mimo_layers = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'D':
delay_n = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'C':
cfo_hz = strtof(argv[optind], NULL);
break;
case 'v':
srsran_verbose++;
break;
@ -186,7 +196,7 @@ int main(int argc, char** argv)
int ret = SRSRAN_ERROR;
srsran_enb_dl_nr_t enb_dl = {};
srsran_ue_dl_nr_t ue_dl = {};
srsran_pdsch_res_nr_t pdsch_res[SRSRAN_MAX_TB] = {};
srsran_pdsch_res_nr_t pdsch_res = {};
srsran_random_t rand_gen = srsran_random_init(1234);
srsran_slot_cfg_t slot = {};
struct timeval t[3] = {};
@ -196,10 +206,13 @@ int main(int argc, char** argv)
uint8_t* data_tx[SRSRAN_MAX_TB] = {};
uint8_t* data_rx[SRSRAN_MAX_CODEWORDS] = {};
cf_t* buffer = NULL;
cf_t* buffer_gnb[SRSRAN_MAX_PORTS] = {};
cf_t* buffer_ue[SRSRAN_MAX_PORTS] = {};
buffer = srsran_vec_cf_malloc(SRSRAN_SF_LEN_PRB(carrier.nof_prb));
if (buffer == NULL) {
uint32_t sf_len = SRSRAN_SF_LEN_PRB(carrier.nof_prb);
buffer_gnb[0] = srsran_vec_cf_malloc(sf_len);
buffer_ue[0] = srsran_vec_cf_malloc(sf_len);
if (buffer_gnb[0] == NULL || buffer_ue[0] == NULL) {
ERROR("Error malloc");
goto clean_exit;
}
@ -209,6 +222,7 @@ int main(int argc, char** argv)
ue_dl_args.pdsch.sch.disable_simd = false;
ue_dl_args.pdsch.sch.decoder_use_flooded = false;
ue_dl_args.pdsch.measure_evm = true;
ue_dl_args.pdsch.disable_zero_re_around_dc = true;
ue_dl_args.pdcch.disable_simd = false;
ue_dl_args.pdcch.measure_evm = true;
ue_dl_args.nof_max_prb = carrier.nof_prb;
@ -248,12 +262,12 @@ int main(int argc, char** argv)
search_space->nof_candidates[L] = srsran_pdcch_nr_max_candidates_coreset(coreset, L);
}
if (srsran_ue_dl_nr_init(&ue_dl, &buffer, &ue_dl_args)) {
if (srsran_ue_dl_nr_init(&ue_dl, buffer_ue, &ue_dl_args)) {
ERROR("Error UE DL");
goto clean_exit;
}
if (srsran_enb_dl_nr_init(&enb_dl, &buffer, &enb_dl_args)) {
if (srsran_enb_dl_nr_init(&enb_dl, buffer_gnb, &enb_dl_args)) {
ERROR("Error UE DL");
goto clean_exit;
}
@ -291,7 +305,7 @@ int main(int argc, char** argv)
goto clean_exit;
}
pdsch_res[i].payload = data_rx[i];
pdsch_res.tb[i].payload = data_rx[i];
}
srsran_softbuffer_tx_t softbuffer_tx = {};
@ -318,6 +332,7 @@ int main(int argc, char** argv)
pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers;
pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0;
pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1;
pdsch_cfg.grant.beta_dmrs = srsran_convert_dB_to_amplitude(3);
pdsch_cfg.grant.rnti_type = srsran_rnti_type_c;
pdsch_cfg.grant.rnti = 0x4601;
pdsch_cfg.grant.tb[0].rv = rv_idx;
@ -390,13 +405,29 @@ int main(int argc, char** argv)
get_time_interval(t);
pdsch_encode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec);
// Emulate channel delay
if (delay_n >= sf_len) {
ERROR("Delay exceeds SF length");
goto clean_exit;
}
srsran_vec_cf_copy(&buffer_ue[0][0], &buffer_gnb[0][delay_n], sf_len - delay_n);
srsran_vec_cf_copy(&buffer_ue[0][sf_len - delay_n], &buffer_gnb[0][0], delay_n);
// Emulate channel CFO
if (isnormal(cfo_hz) && ue_dl.fft[0].cfg.symbol_sz > 0) {
srsran_vec_apply_cfo(buffer_ue[0],
cfo_hz / (ue_dl.fft[0].cfg.symbol_sz * SRSRAN_SUBC_SPACING_NR(carrier.numerology)),
buffer_ue[0],
sf_len);
}
for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) {
pdsch_cfg.grant.tb[tb].softbuffer.rx = &softbuffer_rx;
srsran_softbuffer_rx_reset(pdsch_cfg.grant.tb[tb].softbuffer.rx);
}
gettimeofday(&t[1], NULL);
if (work_ue_dl(&ue_dl, &slot, pdsch_res) < SRSRAN_SUCCESS) {
if (work_ue_dl(&ue_dl, &slot, &pdsch_res) < SRSRAN_SUCCESS) {
ERROR("Error running UE DL");
goto clean_exit;
}
@ -404,14 +435,14 @@ int main(int argc, char** argv)
get_time_interval(t);
pdsch_decode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec);
if (pdsch_res->evm > 0.001f) {
ERROR("Error PDSCH EVM is too high %f", pdsch_res->evm);
if (pdsch_res.evm[0] > 0.02f) {
ERROR("Error PDSCH EVM is too high %f", pdsch_res.evm[0]);
goto clean_exit;
}
// Check CRC only for RV=0
if (rv_idx == 0) {
if (!pdsch_res[0].crc) {
if (!pdsch_res.tb[0].crc) {
ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs);
goto clean_exit;
}
@ -426,7 +457,7 @@ int main(int argc, char** argv)
}
}
INFO("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs, pdsch_res[0].evm);
INFO("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs, pdsch_res.evm[0]);
// Count the Tx/Rx'd number of bits
nof_bits += pdsch_cfg.grant.tb[0].tbs;
@ -456,8 +487,11 @@ clean_exit:
free(data_rx[i]);
}
}
if (buffer) {
free(buffer);
if (buffer_gnb[0]) {
free(buffer_gnb[0]);
}
if (buffer_ue[0]) {
free(buffer_ue[0]);
}
srsran_softbuffer_tx_free(&softbuffer_tx);
srsran_softbuffer_rx_free(&softbuffer_rx);

@ -85,7 +85,7 @@ public:
void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) {}
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {}
std::string get_rb_name(uint32_t lcid) { return "None"; }
const char* get_rb_name(uint32_t lcid) { return "None"; }
srslog::basic_logger& logger;

@ -80,7 +80,7 @@ public:
// RRC interface
void max_retx_attempted() { max_retx_triggered = true; }
std::string get_rb_name(uint32_t lcid) { return std::string(""); }
const char* get_rb_name(uint32_t lcid) { return ""; }
std::vector<unique_byte_buffer_t> sdus;
rlc_pcap* pcap = nullptr;

@ -63,7 +63,7 @@ public:
// RRC interface
void max_retx_attempted() {}
std::string get_rb_name(uint32_t lcid) { return std::string("TestRB"); }
const char* get_rb_name(uint32_t lcid) { return "TestRB"; }
void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; }
unique_byte_buffer_t sdus[MAX_NBUFS];

@ -389,7 +389,7 @@ public:
std::this_thread::sleep_for(std::chrono::seconds(1));
exit(1);
}
std::string get_rb_name(uint32_t rx_lcid) { return std::string("DRB1"); }
const char* get_rb_name(uint32_t rx_lcid) { return "DRB1"; }
int get_nof_rx_pdus() { return rx_pdus; }

@ -65,7 +65,7 @@ public:
// RRC interface
void max_retx_attempted() {}
std::string get_rb_name(uint32_t lcid) { return std::string(""); }
const char* get_rb_name(uint32_t lcid) { return ""; }
void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; }
uint32_t get_num_sdus() { return sdus.size(); }

@ -26,7 +26,7 @@
INCLUDES
*******************************************************************************/
#include <memory>
#include "srsran/common/lte_common.h"
#include <stdint.h>
namespace srsenb {
@ -34,28 +34,21 @@ namespace srsenb {
#define SRSENB_RRC_MAX_N_PLMN_IDENTITIES 6
#define SRSENB_N_SRB 3
#define SRSENB_N_DRB 8
#define SRSENB_N_RADIO_BEARERS 11
#define SRSENB_MAX_UES 64
const uint32_t MAX_ERAB_ID = 15;
const uint32_t MAX_NOF_ERABS = 16;
enum rb_id_t {
RB_ID_SRB0 = 0,
RB_ID_SRB1,
RB_ID_SRB2,
RB_ID_DRB1,
RB_ID_DRB2,
RB_ID_DRB3,
RB_ID_DRB4,
RB_ID_DRB5,
RB_ID_DRB6,
RB_ID_DRB7,
RB_ID_DRB8,
RB_ID_N_ITEMS,
};
inline const char* to_string(rb_id_t rb_id)
using srsran::lte_drb;
using srsran::lte_srb;
using srsran::srb_to_lcid;
inline const char* get_rb_name(uint32_t lcid)
{
const static char* names[] = {"SRB0", "SRB1", "SRB2", "DRB1", "DRB2", "DRB3", "DRB4", "DRB5", "DRB6", "DRB7", "DRB8"};
return (rb_id < RB_ID_N_ITEMS) ? names[rb_id] : "invalid bearer id";
return (srsran::is_lte_srb(lcid)) ? srsran::get_srb_name(srsran::lte_lcid_to_srb(lcid))
: srsran::get_drb_name(static_cast<srsran::lte_drb>(lcid - srsran::MAX_LTE_SRB_ID));
}
constexpr uint32_t drb_to_lcid(lte_drb drb_id)
{
return srb_to_lcid(lte_srb::srb2) + static_cast<uint32_t>(drb_id);
}
// Cat 3 UE - Max number of DL-SCH transport block bits received within a TTI

@ -103,6 +103,7 @@ struct general_args_t {
std::string eia_pref_list;
std::string eea_pref_list;
uint32_t max_mac_dl_kos;
uint32_t max_mac_ul_kos;
};
struct all_args_t {

@ -77,7 +77,9 @@ public:
// rrc_interface_mac
int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) override;
void upd_user(uint16_t new_rnti, uint16_t old_rnti) override;
void set_activity_user(uint16_t rnti, bool ack_info) override;
void set_activity_user(uint16_t rnti) override;
void set_radiolink_dl_state(uint16_t rnti, bool crc_res) override;
void set_radiolink_ul_state(uint16_t rnti, bool crc_res) override;
bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) override;
uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override;
@ -90,30 +92,35 @@ public:
void release_ue(uint16_t rnti) override;
bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) override;
bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) override;
bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) override;
void modify_erabs(uint16_t rnti,
const asn1::s1ap::erab_modify_request_s& msg,
std::vector<uint16_t>* erabs_modified,
std::vector<uint16_t>* erabs_failed_to_modify) override;
bool modify_ue_erab(uint16_t rnti,
uint8_t erab_id,
bool has_erab(uint16_t rnti, uint32_t erab_id) const override;
int get_erab_addr_in(uint16_t rnti, uint16_t erab_id, transp_addr_t& addr_in, uint32_t& teid_in) const override;
void set_aggregate_max_bitrate(uint16_t rnti, const asn1::s1ap::ue_aggregate_maximum_bitrate_s& bitrate) override;
int setup_erab(uint16_t rnti,
uint16_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos_params,
const asn1::unbounded_octstring<true>* nas_pdu);
srsran::const_span<uint8_t> nas_pdu,
const asn1::bounded_bitstring<1, 160, true, true>& addr,
uint32_t gtpu_teid_out,
asn1::s1ap::cause_c& cause) override;
int modify_erab(uint16_t rnti,
uint16_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos_params,
srsran::const_span<uint8_t> nas_pdu,
asn1::s1ap::cause_c& cause) override;
bool release_erabs(uint32_t rnti) override;
void release_erabs(uint32_t rnti,
const asn1::s1ap::erab_release_cmd_s& msg,
std::vector<uint16_t>* erabs_released,
std::vector<uint16_t>* erabs_failed_to_release) 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 ho_preparation_complete(uint16_t rnti,
bool is_success,
rrc::ho_prep_result result,
const asn1::s1ap::ho_cmd_s& msg,
srsran::unique_byte_buffer_t rrc_container) override;
uint16_t
start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg,
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container) override;
uint16_t start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg,
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container,
asn1::s1ap::cause_c& failure_cause) override;
void set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs) override;
int notify_ue_erab_updates(uint16_t rnti, srsran::const_byte_span nas_pdu) override;
// rrc_interface_pdcp
void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override;
@ -188,6 +195,7 @@ private:
typedef struct {
uint16_t rnti;
uint32_t lcid;
uint32_t arg;
srsran::unique_byte_buffer_t pdu;
} rrc_pdu;
@ -196,7 +204,8 @@ private:
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_MAC_KO_USER = 0xffff0006;
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;

@ -92,15 +92,17 @@ public:
const asn1::s1ap::erab_level_qos_params_s& qos,
const asn1::bounded_bitstring<1, 160, true, true>& addr,
uint32_t teid_out,
const asn1::unbounded_octstring<true>* nas_pdu);
bool release_erab(uint8_t erab_id);
srsran::const_span<uint8_t> nas_pdu,
asn1::s1ap::cause_c& cause);
int release_erab(uint8_t erab_id);
void release_erabs();
bool modify_erab(uint8_t erab_id,
int modify_erab(uint8_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos,
const asn1::unbounded_octstring<true>* nas_pdu);
srsran::const_span<uint8_t> nas_pdu,
asn1::s1ap::cause_c& cause);
// Methods to apply bearer updates
void add_gtpu_bearer(uint32_t erab_id);
int add_gtpu_bearer(uint32_t erab_id);
srsran::expected<uint32_t> add_gtpu_bearer(uint32_t erab_id,
uint32_t teid_out,
uint32_t addr,

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

@ -39,24 +39,25 @@ public:
uint16_t crnti;
uint16_t temp_crnti;
};
struct ho_cancel_ev {};
struct ho_cancel_ev {
asn1::s1ap::cause_c cause;
ho_cancel_ev(const asn1::s1ap::cause_c& cause_) : cause(cause_) {}
};
rrc_mobility(srsenb::rrc::ue* outer_ue);
bool fill_conn_recfg_no_ho_cmd(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg);
void handle_ue_meas_report(const asn1::rrc::meas_report_s& msg, srsran::unique_byte_buffer_t pdu);
void handle_ho_preparation_complete(bool is_success,
void handle_ho_preparation_complete(rrc::ho_prep_result result,
const asn1::s1ap::ho_cmd_s& msg,
srsran::unique_byte_buffer_t container);
bool is_ho_running() const { return not is_in_state<idle_st>(); }
// S1-Handover
bool start_s1_tenb_ho(const asn1::s1ap::ho_request_s& msg,
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container);
static uint16_t
start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg,
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container);
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container,
asn1::s1ap::cause_c& cause);
private:
// helper methods
@ -72,7 +73,10 @@ private:
const enb_cell_common& target_cell,
uint32_t src_dl_earfcn,
uint32_t src_pci);
bool apply_ho_prep_cfg(const asn1::rrc::ho_prep_info_r8_ies_s& ho_prep, const asn1::s1ap::ho_request_s& ho_req_msg);
bool apply_ho_prep_cfg(const asn1::rrc::ho_prep_info_r8_ies_s& ho_prep,
const asn1::s1ap::ho_request_s& ho_req_msg,
std::vector<asn1::s1ap::erab_item_s>& erabs_failed_to_setup,
asn1::s1ap::cause_c& cause);
rrc::ue* rrc_ue = nullptr;
rrc* rrc_enb = nullptr;
@ -81,17 +85,22 @@ private:
// vars
asn1::rrc::meas_cfg_s current_meas_cfg;
asn1::rrc::rrc_conn_recfg_complete_s pending_recfg_complete;
asn1::s1ap::cause_c failure_cause;
// events
struct ho_meas_report_ev {
uint32_t target_eci = 0;
const asn1::rrc::meas_obj_to_add_mod_s* meas_obj = nullptr;
bool direct_fwd_path = false;
};
struct ho_req_rx_ev {
const asn1::s1ap::ho_request_s* ho_req_msg;
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s* transparent_container;
};
using unsuccessful_outcome_ev = std::false_type;
struct ho_failure_ev {
asn1::s1ap::cause_c cause;
ho_failure_ev(const asn1::s1ap::cause_c& cause_) : cause(cause_) {}
};
using recfg_complete_ev = asn1::rrc::rrc_conn_recfg_complete_s;
using status_transfer_ev = asn1::s1ap::bearers_subject_to_status_transfer_list_l;
@ -105,6 +114,7 @@ private:
void enter(rrc_mobility* f, const ho_meas_report_ev& meas_report);
};
struct s1_target_ho_st {
asn1::s1ap::cause_c failure_cause;
std::vector<uint32_t> pending_tunnels;
};
struct wait_recfg_comp {};
@ -125,7 +135,7 @@ private:
private:
void handle_ho_cmd(wait_ho_cmd& s, const ho_cmd_msg& ho_cmd);
void handle_ho_cancel(const ho_cancel_ev& ev);
bool start_enb_status_transfer(const asn1::s1ap::ho_cmd_s& s1ap_ho_cmd);
asn1::s1ap::cause_c start_enb_status_transfer(const asn1::s1ap::ho_cmd_s& s1ap_ho_cmd);
rrc* rrc_enb;
rrc::ue* rrc_ue;
@ -154,6 +164,7 @@ private:
void handle_crnti_ce(intraenb_ho_st& s, const user_crnti_upd_ev& ev);
void handle_recfg_complete(intraenb_ho_st& s, const recfg_complete_ev& ev);
void handle_ho_requested(idle_st& s, const ho_req_rx_ev& ho_req);
void handle_ho_failure(const ho_failure_ev& ev);
void handle_status_transfer(s1_target_ho_st& s, const status_transfer_ev& ev);
void defer_recfg_complete(s1_target_ho_st& s, const recfg_complete_ev& ev);
void handle_recfg_complete(wait_recfg_comp& s, const recfg_complete_ev& ev);
@ -181,6 +192,7 @@ protected:
row< intraenb_ho_st, idle_st, recfg_complete_ev, &fsm::handle_recfg_complete >,
// +----------------+-------------------+---------------------+----------------------------+-------------------------+
row< s1_target_ho_st, wait_recfg_comp, status_transfer_ev, &fsm::handle_status_transfer >,
to_state< idle_st, ho_failure_ev, &fsm::handle_ho_failure >,
upd< s1_target_ho_st, recfg_complete_ev, &fsm::defer_recfg_complete >,
row< wait_recfg_comp, idle_st, recfg_complete_ev, &fsm::handle_recfg_complete >
// +----------------+-------------------+---------------------+----------------------------+-------------------------+

@ -51,7 +51,8 @@ public:
void set_activity_timeout(const activity_timeout_type_t type);
void set_rlf_timeout();
void set_activity();
void mac_ko_activity();
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);
void rlf_timer_expired();
void max_retx_reached();
@ -80,7 +81,7 @@ public:
void send_connection_reest_rej(procedure_result_code cause);
void send_connection_reconf(srsran::unique_byte_buffer_t sdu = {},
bool phy_cfg_updated = true,
const asn1::unbounded_octstring<true>* nas_pdu = nullptr);
srsran::const_byte_span nas_pdu = {});
void send_security_mode_command();
void send_ue_cap_enquiry();
void send_ue_info_req();
@ -116,16 +117,23 @@ public:
void set_bitrates(const asn1::s1ap::ue_aggregate_maximum_bitrate_s& rates);
/// Helper to check UE ERABs
bool has_erab(uint32_t erab_id) const { return bearer_list.get_erabs().count(erab_id) > 0; }
int get_erab_addr_in(uint16_t erab_id, transp_addr_t& addr_in, uint32_t& teid_in) const;
bool setup_erabs(const asn1::s1ap::erab_to_be_setup_list_ctxt_su_req_l& e);
bool setup_erabs(const asn1::s1ap::erab_to_be_setup_list_bearer_su_req_l& e);
bool release_erabs();
bool release_erab(uint32_t erab_id);
bool modify_erab(uint16_t erab_id,
int release_erab(uint32_t erab_id);
int setup_erab(uint16_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos_params,
const asn1::unbounded_octstring<true>* nas_pdu);
void notify_s1ap_ue_ctxt_setup_complete();
void notify_s1ap_ue_erab_setup_response(const asn1::s1ap::erab_to_be_setup_list_bearer_su_req_l& e);
srsran::const_span<uint8_t> nas_pdu,
const asn1::bounded_bitstring<1, 160, true, true>& addr,
uint32_t gtpu_teid_out,
asn1::s1ap::cause_c& cause);
int modify_erab(uint16_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos_params,
srsran::const_span<uint8_t> nas_pdu,
asn1::s1ap::cause_c& cause);
// Getters for PUCCH resources
int get_cqi(uint16_t* pmi_idx, uint16_t* n_pucch, uint32_t ue_cc_idx);
@ -187,7 +195,9 @@ private:
const static uint32_t UE_PCELL_CC_IDX = 0;
uint32_t consecutive_kos = 0;
// consecutive KO counter for DL and UL
uint32_t consecutive_kos_dl = 0;
uint32_t consecutive_kos_ul = 0;
ue_cell_ded_list ue_cell_list;
bearer_cfg_handler bearer_list;

@ -64,7 +64,7 @@ public:
struct tunnel {
uint16_t rnti = SRSRAN_INVALID_RNTI;
uint32_t lcid = SRSENB_N_RADIO_BEARERS;
uint32_t lcid = srsran::MAX_NOF_BEARERS;
uint32_t teid_in = 0;
uint32_t teid_out = 0;
uint32_t spgw_addr = 0;

@ -108,7 +108,7 @@ private:
void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu);
void write_pdu_pcch(srsran::unique_byte_buffer_t pdu);
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {}
std::string get_rb_name(uint32_t lcid);
const char* get_rb_name(uint32_t lcid);
};
class user_interface

@ -96,7 +96,7 @@ private:
void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) final;
void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) final;
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final {}
std::string get_rb_name(uint32_t lcid) final;
const char* get_rb_name(uint32_t lcid) final;
};
class user_interface

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

Loading…
Cancel
Save