Merge branch 'next' into agpl_next

master
Codebot 3 years ago committed by Your Name
commit 73a61b884e

@ -97,6 +97,8 @@ option(ENABLE_SRSLOG_TRACING "Enable event tracing using srslog" OFF)
option(ASSERTS_ENABLED "Enable srsRAN asserts" ON)
option(STOP_ON_WARNING "Interrupt application on warning" OFF)
option(ENABLE_ALL_TEST "Enable all unit/component test" OFF)
# Users that want to try this feature need to make sure the lto plugin is
# loaded by bintools (ar, nm, ...). Older versions of bintools will not do
# it automatically so it is necessary to use the gcc wrappers of the compiler
@ -496,13 +498,13 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
endif (ENABLE_ASAN)
if (ENABLE_TSAN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -DHAVE_TSAN")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
endif (ENABLE_TSAN)
if (ENABLE_MSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fPIE -pie")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fPIE -pie")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins -fPIE")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins -fPIE")
endif (ENABLE_MSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang")
if (ENABLE_GCOV)
@ -605,6 +607,18 @@ function(add_nr_test)
set_tests_properties(${TNAME} PROPERTIES LABELS "nr;${CTEST_LABELS}")
endfunction()
function(add_nr_advanced_test)
if (NOT ${ENABLE_ALL_TEST})
return()
endif()
add_test(${ARGN})
set(TNAME ${ARGV0})
if(${TNAME} STREQUAL NAME)
set(TNAME ${ARGV1})
endif()
set_tests_properties(${TNAME} PROPERTIES LABELS "nr;${CTEST_LABELS}")
endfunction()
########################################################################
# Add general includes and dependencies
########################################################################

@ -47,8 +47,10 @@ private:
template <typename T>
struct exp_average_fast_start {
exp_average_fast_start(T alpha_, uint32_t start_size = 100) : alpha(alpha_), start_count_size(start_size)
exp_average_fast_start(T alpha_val) : exp_average_fast_start(alpha_val, 1.0 / alpha_val) {}
exp_average_fast_start(T alpha_val, uint32_t start_size) : alpha_(alpha_val), start_count_size(start_size)
{
assert(alpha_ < 1);
assert(start_size > 0);
}
void push(T sample)
@ -57,16 +59,18 @@ struct exp_average_fast_start {
avg_ += (sample - avg_) / (count + 1);
count++;
} else {
avg_ = (1 - alpha) * avg_ + alpha * sample;
avg_ = (1 - alpha_) * avg_ + alpha_ * sample;
}
}
T value() const { return count == 0 ? 0 : avg_; }
T value() const { return count == 0 ? 0 : avg_; }
T alpha() const { return alpha_; }
bool is_exp_average_mode() const { return count >= start_count_size; }
private:
T avg_ = 0;
uint32_t count = 0;
uint32_t start_count_size;
T alpha;
T alpha_;
};
namespace detail {
@ -121,7 +125,6 @@ private:
template <typename T>
struct null_sliding_average {
null_sliding_average(uint32_t N) : window(N, null_value()) {}
void push(T sample) { window.push(sample); }
void push_hole() { window.push(null_value()); }

@ -375,7 +375,8 @@ public:
void from_uint64(uint64_t v)
{
srsran_assert(nof_words_() == 1, "ERROR: cannot convert bitset of size=%zd to uint64_t", size());
srsran_assert(v < (1U << size()), "ERROR: Provided uint64=%ld does not fit in bitset of size=%zd", v, size());
srsran_assert(
v < (1U << size()), "ERROR: Provided mask=0x%" PRIx64 " does not fit in bitset of size=%zd", v, size());
buffer[0] = v;
}

@ -29,8 +29,8 @@
namespace srsran {
using observer_id = std::size_t;
const size_t invalid_observer_id = std::numeric_limits<observer_id>::max();
using observer_id = std::size_t;
const std::size_t invalid_observer_id = std::numeric_limits<observer_id>::max();
template <typename... Args>
class observer;
@ -93,7 +93,7 @@ public:
template <typename... Args2>
observer_id subscribe(Args2&&... args)
{
size_t id = 0;
std::size_t id = 0;
for (auto& slot : observers) {
if (not static_cast<bool>(slot)) {
// empty slot found
@ -117,9 +117,9 @@ public:
return false;
}
size_t nof_observers() const
std::size_t nof_observers() const
{
size_t count = 0;
std::size_t count = 0;
for (auto& slot : observers) {
count += static_cast<bool>(slot) ? 1 : 0;
}

@ -69,7 +69,7 @@ detail::scope_exit<typename std::decay<Callable>::type> make_scope_exit(Callable
return detail::scope_exit<typename std::decay<Callable>::type>{std::forward<Callable>(callable)};
}
#define DEFER(FUNC) auto on_exit_call = make_scope_exit([&]() { FUNC })
#define DEFER(FUNC) auto on_exit_call##__LINE__ = srsran::make_scope_exit([&]() { FUNC })
} // namespace srsran

@ -26,6 +26,7 @@
#ifndef SRSRAN_EPOLL_HELPER_H
#define SRSRAN_EPOLL_HELPER_H
#include <atomic>
#include <functional>
#include <signal.h>
#include <sys/epoll.h>
@ -67,7 +68,7 @@ private:
class epoll_signal_handler : public epoll_handler
{
public:
epoll_signal_handler(bool* running_) : running(running_) {}
epoll_signal_handler(std::atomic<bool>& running_) : running(running_) {}
int handle_event(int fd, epoll_event e, int epoll_fd)
{
@ -81,7 +82,7 @@ public:
case SIGINT:
case SIGHUP:
case SIGQUIT:
*running = false;
running = false;
break;
default:
fprintf(stderr, "got signal %d\n", info.ssi_signo);
@ -91,7 +92,7 @@ public:
}
private:
bool* running = nullptr;
std::atomic<bool>& running;
};
///< Create periodic epoll timer every 1ms

@ -86,7 +86,7 @@ public:
class read_pdu_interface
{
public:
virtual int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) = 0;
virtual uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) = 0;
};
class stack_interface_phy_nr

@ -102,11 +102,11 @@ protected:
virtual void write_pdu(pcap_pdu_t& pdu) = 0;
void run_thread() final;
std::mutex mutex;
srslog::basic_logger& logger;
bool running = false;
static_blocking_queue<pcap_pdu_t, 512> queue;
uint16_t ue_id = 0;
std::mutex mutex;
srslog::basic_logger& logger;
std::atomic<bool> running = {false};
static_blocking_queue<pcap_pdu_t, 1024> queue;
uint16_t ue_id = 0;
private:
void pack_and_queue(uint8_t* payload,

@ -74,7 +74,6 @@ class multiqueue_handler
std::lock_guard<std::mutex> lock(q_mutex);
return active_;
}
void set_active(bool val)
{
std::unique_lock<std::mutex> lock(q_mutex);
@ -82,8 +81,7 @@ class multiqueue_handler
// no-op
return;
}
active_ = val;
consumer_notify_needed = true;
active_ = val;
if (not active_) {
buffer.clear();
@ -123,18 +121,14 @@ class multiqueue_handler
bool try_pop(myobj& obj)
{
std::unique_lock<std::mutex> lock(q_mutex);
if (buffer.empty()) {
consumer_notify_needed = true;
return false;
}
obj = std::move(buffer.top());
buffer.pop();
consumer_notify_needed = false;
if (nof_waiting > 0) {
lock.unlock();
cv_full.notify_one();
}
return true;
return pop_(lock, obj);
}
bool try_pop(myobj& obj, bool& try_lock_success)
{
std::unique_lock<std::mutex> lock(q_mutex, std::try_to_lock);
try_lock_success = lock.owns_lock();
return try_lock_success ? pop_(lock, obj) : false;
}
private:
@ -161,13 +155,19 @@ class multiqueue_handler
}
}
buffer.push(std::forward<T>(*o));
if (consumer_notify_needed) {
// Note: The consumer thread only needs to be notified and awaken when queues transition from empty to non-empty
// To ensure that the consumer noticed that the queue was empty before a push, we store the last
// try_pop() return in a member variable.
// Doing this reduces the contention of multiple producers for the same condition variable
return true;
}
bool pop_(std::unique_lock<std::mutex>& lock, myobj& obj)
{
if (buffer.empty()) {
return false;
}
obj = std::move(buffer.top());
buffer.pop();
if (nof_waiting > 0) {
lock.unlock();
parent->signal_pushed_data();
cv_full.notify_one();
}
return true;
}
@ -177,9 +177,8 @@ class multiqueue_handler
mutable std::mutex q_mutex;
srsran::dyn_circular_buffer<myobj> buffer;
std::condition_variable cv_full, cv_exit;
bool active_ = true;
bool consumer_notify_needed = true;
int nof_waiting = 0;
bool active_ = true;
int nof_waiting = 0;
};
public:
@ -235,9 +234,7 @@ public:
// signal deactivation to pushing threads in a non-blocking way
q.set_active(false);
}
while (wait_state) {
pushed_data = true;
cv_empty.notify_one();
while (consumer_state) {
cv_exit.wait(lock);
}
for (auto& q : queues) {
@ -258,8 +255,9 @@ public:
if (not running) {
return queue_handle();
}
for (; qidx < queues.size() and (queues[qidx].active() or (queues[qidx].capacity() != capacity_)); ++qidx)
;
while (qidx < queues.size() and (queues[qidx].active() or (queues[qidx].capacity() != capacity_))) {
++qidx;
}
// check if there is a free queue of the required size
if (qidx == queues.size()) {
@ -291,17 +289,18 @@ public:
bool wait_pop(myobj* value)
{
std::unique_lock<std::mutex> lock(mutex);
consumer_state = true;
while (running) {
if (round_robin_pop_(value)) {
consumer_state = false;
return true;
}
pushed_data = false;
wait_state = true;
while (not pushed_data) {
cv_empty.wait(lock);
}
wait_state = false;
lock.unlock();
std::this_thread::sleep_for(std::chrono::microseconds(100));
lock.lock();
}
consumer_state = false;
lock.unlock();
cv_exit.notify_one();
return false;
}
@ -316,36 +315,29 @@ private:
bool round_robin_pop_(myobj* value)
{
// Round-robin for all queues
auto it = queues.begin() + spin_idx;
auto q_it = queues.begin() + spin_idx;
uint32_t count = 0;
for (; count < queues.size(); ++count, ++it) {
if (it == queues.end()) {
it = queues.begin(); // wrap-around
for (; count < queues.size(); ++count, ++q_it) {
if (q_it == queues.end()) {
q_it = queues.begin(); // wrap-around
}
if (it->try_pop(*value)) {
bool try_lock_success = true;
if (q_it->try_pop(*value, try_lock_success)) {
spin_idx = (spin_idx + count + 1) % queues.size();
return true;
}
}
return false;
}
/// Called by the producer threads to signal the consumer to unlock in wait_pop
void signal_pushed_data()
{
{
std::lock_guard<std::mutex> lock(mutex);
if (pushed_data) {
return;
if (not try_lock_success) {
// restart RR search, as there was a collision with a producer
count = 0;
}
pushed_data = true;
}
cv_empty.notify_one();
return false;
}
mutable std::mutex mutex;
std::condition_variable cv_empty, cv_exit;
std::condition_variable cv_exit;
uint32_t spin_idx = 0;
bool running = true, pushed_data = false, wait_state = false;
bool running = true, consumer_state = false;
std::deque<input_port_impl> queues;
uint32_t default_capacity = 0;
};

@ -28,8 +28,8 @@
#define srsran_unlikely(expr) __builtin_expect(!!(expr), 0)
#define srsran_terminate(fmt, ...) \
std::fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
srslog::flush(); \
std::fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
std::abort()
#ifdef ASSERTS_ENABLED

@ -32,6 +32,7 @@
#include "srsran/adt/circular_buffer.h"
#include "srsran/adt/move_callback.h"
#include "srsran/srslog/srslog.h"
#include <atomic>
#include <condition_variable>
#include <functional>
#include <memory>
@ -54,6 +55,7 @@ public:
worker();
~worker() = default;
void setup(uint32_t id, thread_pool* parent, uint32_t prio = 0, uint32_t mask = 255);
void stop();
uint32_t get_id();
void release();
@ -63,6 +65,7 @@ public:
private:
uint32_t my_id = 0;
thread_pool* my_parent = nullptr;
std::atomic<bool> running = {true};
void run_thread();
void wait_to_start();

@ -44,6 +44,7 @@ void threads_print_self();
#ifdef __cplusplus
}
#include <atomic>
#include <string>
namespace srsran {
@ -138,10 +139,10 @@ protected:
virtual void run_period() = 0;
private:
int wakeups_missed;
int timer_fd;
int period_us;
bool run_enable;
int wakeups_missed = 0;
int timer_fd = 0;
int period_us = 0;
std::atomic<bool> run_enable = {false};
void run_thread()
{

@ -34,6 +34,7 @@
#include <algorithm>
#include <cstdint>
#include <deque>
#include <inttypes.h>
#include <limits>
#include <mutex>
@ -46,8 +47,9 @@ public:
};
/**
* Class that manages stack timers. It allows creation of unique_timers, with different ids. Each unique_timer duration,
* 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.
* The timers access/alteration is thread-safe. Just beware non-atomic uses of its getters.
* 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.
@ -62,20 +64,34 @@ public:
*/
class timer_handler
{
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;
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 size_t WHEEL_SHIFT = 16U;
constexpr static size_t WHEEL_SIZE = 1U << WHEEL_SHIFT;
constexpr static size_t WHEEL_MASK = WHEEL_SIZE - 1U;
constexpr static uint64_t STOPPED_FLAG = 0U;
constexpr static uint64_t RUNNING_FLAG = static_cast<uint64_t>(1U) << 63U;
constexpr static uint64_t EXPIRED_FLAG = static_cast<uint64_t>(1U) << 62U;
constexpr static tic_diff_t MAX_TIMER_DURATION = 0x3FFFFFFFU;
static bool decode_is_running(uint64_t value) { return (value & RUNNING_FLAG) != 0; }
static bool decode_is_expired(uint64_t value) { return (value & EXPIRED_FLAG) != 0; }
static tic_diff_t decode_duration(uint64_t value) { return (value >> 32U) & MAX_TIMER_DURATION; }
static tic_t decode_timeout(uint64_t value) { return static_cast<uint32_t>(value & 0xFFFFFFFFU); }
static uint64_t encode_state(uint64_t mode_flag, uint32_t duration, uint32_t timeout)
{
return mode_flag + (static_cast<uint64_t>(duration) << 32U) + timeout;
}
struct timer_impl : public intrusive_double_linked_list_element<>, public intrusive_forward_list_element<> {
timer_handler& parent;
// const
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;
timer_handler& parent;
// writes protected by backend lock
bool allocated = false;
std::atomic<uint64_t> state{0}; ///< read can be without lock, thus writes must be atomic
srsran::move_callback<void(uint32_t)> callback;
explicit timer_impl(timer_handler& parent_, uint32_t id_) : parent(parent_), id(id_) {}
@ -84,32 +100,38 @@ class timer_handler
timer_impl& operator=(const timer_impl&) = delete;
timer_impl& operator=(timer_impl&&) = delete;
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(); }
// unprotected
bool is_running_() const { return decode_is_running(state.load(std::memory_order_relaxed)); }
bool is_expired_() const { return decode_is_expired(state.load(std::memory_order_relaxed)); }
uint32_t duration_() const { return decode_duration(state.load(std::memory_order_relaxed)); }
bool is_set_() const { return duration_() > 0; }
tic_diff_t time_elapsed_() const
{
uint64_t state_snapshot = state.load(std::memory_order_relaxed);
bool running = decode_is_running(state_snapshot), expired = decode_is_expired(state_snapshot);
uint32_t duration = decode_duration(state_snapshot), timeout = decode_timeout(state_snapshot);
return running ? duration - (timeout - parent.cur_time) : (expired ? duration : 0);
}
bool set(uint32_t duration_)
void set(uint32_t 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;
srsran_assert(duration_ <= MAX_TIMER_DURATION,
"Invalid timer duration=%" PRIu32 ">%" PRIu32,
duration_,
MAX_TIMER_DURATION);
std::lock_guard<std::mutex> lock(parent.mutex);
set_(duration_);
}
bool set(uint32_t duration_, srsran::move_callback<void(uint32_t)> callback_)
void set(uint32_t duration_, srsran::move_callback<void(uint32_t)> callback_)
{
if (set(duration_)) {
callback = std::move(callback_);
return true;
}
return false;
srsran_assert(duration_ <= MAX_TIMER_DURATION,
"Invalid timer duration=%" PRIu32 ">%" PRIu32,
duration_,
MAX_TIMER_DURATION);
std::lock_guard<std::mutex> lock(parent.mutex);
set_(duration_);
callback = std::move(callback_);
}
void run()
@ -125,7 +147,25 @@ class timer_handler
parent.stop_timer_(*this, false);
}
void deallocate() { parent.dealloc_timer(*this); }
void deallocate()
{
std::lock_guard<std::mutex> lock(parent.mutex);
parent.dealloc_timer_(*this);
}
private:
void set_(uint32_t duration_)
{
duration_ = std::max(duration_, 1U); // the next step will be one place ahead of current one
// called in locked context
uint64_t old_state = state.load(std::memory_order_relaxed);
if (decode_is_running(old_state)) {
// if already running, just extends timer lifetime
parent.start_run_(*this, duration_);
} else {
state.store(encode_state(STOPPED_FLAG, duration_, 0), std::memory_order_relaxed);
}
}
};
public:
@ -160,17 +200,12 @@ public:
handle->set(duration_);
}
bool is_set() const { return is_valid() and handle->duration != INVALID_TIME_DIFF; }
bool is_running() const { return is_valid() and handle->is_running(); }
bool is_expired() const { return is_valid() and handle->is_expired(); }
tic_diff_t time_elapsed() const { return is_valid() ? handle->time_elapsed() : INVALID_TIME_DIFF; }
uint32_t id() const { return is_valid() ? handle->id : INVALID_ID; }
tic_diff_t duration() const { return is_valid() ? handle->duration : INVALID_TIME_DIFF; }
uint32_t id() const { return is_valid() ? handle->id : INVALID_ID; }
bool is_set() const { return is_valid() and handle->is_set_(); }
bool is_running() const { return is_valid() and handle->is_running_(); }
bool is_expired() const { return is_valid() and handle->is_expired_(); }
tic_diff_t time_elapsed() const { return is_valid() ? handle->time_elapsed_() : 0; }
tic_diff_t duration() const { return is_valid() ? handle->duration_() : 0; }
void run()
{
@ -214,13 +249,13 @@ public:
void step_all()
{
std::unique_lock<std::mutex> lock(mutex);
cur_time++;
auto& wheel_list = time_wheel[cur_time & WHEEL_MASK];
uint32_t cur_time_local = cur_time.load(std::memory_order_relaxed) + 1;
auto& wheel_list = time_wheel[cur_time_local & 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) {
if (decode_timeout(timer.state.load(std::memory_order_relaxed)) == cur_time_local) {
// stop timer (callback has to see the timer has already expired)
stop_timer_(timer, true);
@ -236,6 +271,8 @@ public:
}
}
}
cur_time.fetch_add(1, std::memory_order_relaxed);
}
void stop_all()
@ -261,6 +298,8 @@ public:
return nof_timers_running_;
}
constexpr static uint32_t max_timer_duration() { return MAX_TIMER_DURATION; }
template <typename F>
void defer_callback(uint32_t duration, const F& func)
{
@ -284,7 +323,7 @@ private:
timer_impl* t;
if (not free_list.empty()) {
t = &free_list.front();
srsran_assert(t->is_empty(), "Invalid timer id=%d state", t->id);
srsran_assert(not t->allocated, "Invalid timer id=%d state", t->id);
free_list.pop_front();
nof_free_timers--;
} else {
@ -292,63 +331,71 @@ private:
timer_list.emplace_back(*this, timer_list.size());
t = &timer_list.back();
}
t->state = timer_impl::stopped;
t->allocated = true;
return *t;
}
void dealloc_timer(timer_impl& timer)
void dealloc_timer_(timer_impl& timer)
{
std::lock_guard<std::mutex> lock(mutex);
if (timer.is_empty()) {
if (not timer.allocated) {
// already deallocated
return;
}
stop_timer_(timer, false);
timer.state = timer_impl::empty;
timer.duration = INVALID_TIME_DIFF;
timer.timeout = 0;
timer.allocated = false;
timer.state.store(encode_state(STOPPED_FLAG, 0, 0), std::memory_order_relaxed);
timer.callback = srsran::move_callback<void(uint32_t)>();
free_list.push_front(&timer);
nof_free_timers++;
// leave id unchanged.
}
void start_run_(timer_impl& timer)
void start_run_(timer_impl& timer, uint32_t duration_ = 0)
{
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) {
uint64_t timer_old_state = timer.state.load(std::memory_order_relaxed);
duration_ = duration_ == 0 ? decode_duration(timer_old_state) : duration_;
uint32_t new_timeout = cur_time.load(std::memory_order_relaxed) + duration_;
size_t new_wheel_pos = new_timeout & WHEEL_MASK;
uint32_t old_timeout = decode_timeout(timer_old_state);
bool was_running = decode_is_running(timer_old_state);
if (was_running and (old_timeout & WHEEL_MASK) == new_wheel_pos) {
// If no change in timer wheel position. Just update absolute timeout
timer.timeout = timeout;
timer.state.store(encode_state(RUNNING_FLAG, duration_, new_timeout), std::memory_order_relaxed);
return;
}
// Stop timer if it was running, removing it from wheel in the process
stop_timer_(timer, false);
if (was_running) {
time_wheel[old_timeout & WHEEL_MASK].pop(&timer);
nof_timers_running_--;
}
// Insert timer in wheel
time_wheel[new_wheel_pos].push_front(&timer);
timer.timeout = timeout;
timer.state = timer_impl::running;
timer.state.store(encode_state(RUNNING_FLAG, duration_, new_timeout), std::memory_order_relaxed);
nof_timers_running_++;
}
/// called when user manually stops timer (as an alternative to expiry)
void stop_timer_(timer_impl& timer, bool expiry)
{
if (not timer.is_running()) {
uint64_t timer_old_state = timer.state.load(std::memory_order_relaxed);
if (not decode_is_running(timer_old_state)) {
return;
}
// 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;
uint32_t old_timeout = decode_timeout(timer_old_state);
time_wheel[old_timeout & WHEEL_MASK].pop(&timer);
uint64_t new_state =
encode_state(expiry ? EXPIRED_FLAG : STOPPED_FLAG, decode_duration(timer_old_state), old_timeout);
timer.state.store(new_state, std::memory_order_relaxed);
nof_timers_running_--;
}
tic_t cur_time = 0;
size_t nof_timers_running_ = 0, nof_free_timers = 0;
std::atomic<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;

@ -0,0 +1,49 @@
/**
*
* \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_TSAN_OPTIONS_H
#define SRSRAN_TSAN_OPTIONS_H
// Options taken from Mozilla project
// abort_on_error=1 - Causes TSan to abort instead of using exit().
// halt_on_error=1 - Causes TSan to stop on the first race detected.
//
// report_signal_unsafe=0 - Required to avoid TSan deadlocks when
// receiving external signals (e.g. SIGINT manually on console).
//
// allocator_may_return_null=1 - Tell TSan to return NULL when an allocation
// fails instead of aborting the program. This allows us to handle failing
// allocations the same way we would handle them with a regular allocator and
// also uncovers potential bugs that might occur in these situations.
#ifdef __cplusplus
extern "C" {
#endif
const char* __tsan_default_options()
{
return "halt_on_error=1:abort_on_error=1:report_signal_unsafe=0"
":allocator_may_return_null=1";
}
const char* __tsan_default_suppressions()
{
// External uninstrumented libraries
return "called_from_lib:libzmq.so\n"
"called_from_lib:libpgm-5.2.so\n";
}
#ifdef __cplusplus
}
#endif
#endif // SRSRAN_TSAN_OPTIONS_H

@ -192,9 +192,15 @@ public:
* @param enb_cc_idx the eNb Cell/Carrier identifier
* @param nof_bytes the number of grants carrierd by the PUSCH message
* @param crc_res the CRC check, set to true if the message was decoded succesfully
* @param ul_nof_prbs Number of PRBs allocated to grant
* @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR* if an error occurs
*/
virtual int push_pdu(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) = 0;
virtual int push_pdu(uint32_t tti_rx,
uint16_t rnti,
uint32_t enb_cc_idx,
uint32_t nof_bytes,
bool crc_res,
uint32_t ul_nof_prbs) = 0;
virtual int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) = 0;
virtual int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) = 0;
@ -212,8 +218,7 @@ class mac_interface_rrc
{
public:
/* Provides cell configuration including SIB periodicity, etc. */
virtual int cell_cfg(const std::vector<sched_interface::cell_cfg_t>& cell_cfg) = 0;
virtual void reset() = 0;
virtual int cell_cfg(const std::vector<sched_interface::cell_cfg_t>& cell_cfg) = 0;
/* Manages UE configuration context */
virtual int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) = 0;

@ -51,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;
std::vector<uint32_t> meas_gap_offset_subframe;
uint32_t allowed_meas_bw;
};
@ -69,6 +70,7 @@ struct cell_cfg_t {
int target_pusch_sinr_db;
uint32_t initial_dl_cqi;
bool enable_phr_handling;
int min_phr_thres;
asn1::rrc::mob_ctrl_info_s::t304_e_ t304;
std::vector<scell_cfg_t> scell_list;
rrc_meas_cfg_t meas_cfg;

@ -100,6 +100,7 @@ class rrc_interface_rlc
{
public:
virtual void max_retx_attempted(uint16_t rnti) = 0;
virtual void protocol_failure(uint16_t rnti) = 0;
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0;
};

@ -68,8 +68,9 @@ public:
virtual bool user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_radio) = 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;
// Notify S1AP of RRC reconfiguration successful finish.
// Many S1AP procedures use this notification to indicate successful end (e.g InitialContextSetupRequest)
virtual void notify_rrc_reconf_complete(uint16_t rnti) = 0;
/**
* Command the s1ap to transmit a HandoverRequired message to MME.

@ -160,6 +160,7 @@ class rrc_interface_rlc_nr
public:
virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0;
virtual void max_retx_attempted(uint16_t rnti) = 0;
virtual void protocol_failure(uint16_t rnti) = 0;
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0;
virtual const char* get_rb_name(uint32_t lcid) = 0;
};

@ -38,9 +38,9 @@ struct phy_cfg_nr_t {
* SSB configuration
*/
struct ssb_cfg_t {
uint32_t periodicity_ms;
std::array<bool, SRSRAN_SSB_NOF_POSITION> position_in_burst;
srsran_subcarrier_spacing_t scs;
uint32_t periodicity_ms;
std::array<bool, SRSRAN_SSB_NOF_CANDIDATES> position_in_burst;
srsran_subcarrier_spacing_t scs;
};
srsran_tdd_config_nr_t tdd = {};

@ -54,16 +54,25 @@ public:
} cell_cfg_sib_t;
struct sched_args_t {
std::string sched_policy = "time_pf";
std::string sched_policy_args = "2";
int pdsch_mcs = -1;
int pdsch_max_mcs = 28;
int pusch_mcs = -1;
int pusch_max_mcs = 28;
uint32_t min_nof_ctrl_symbols = 1;
uint32_t max_nof_ctrl_symbols = 3;
int max_aggr_level = 3;
bool pucch_mux_enabled = false;
std::string sched_policy = "time_pf";
std::string sched_policy_args = "2";
int pdsch_mcs = -1;
int pdsch_max_mcs = 28;
int pusch_mcs = -1;
int pusch_max_mcs = 28;
uint32_t min_nof_ctrl_symbols = 1;
uint32_t max_nof_ctrl_symbols = 3;
int min_aggr_level = 0;
int max_aggr_level = 3;
bool adaptive_aggr_level = true;
bool pucch_mux_enabled = false;
float target_bler = 0.05;
float max_delta_dl_cqi = 5;
float max_delta_ul_snr = 5;
float adaptive_link_step_size = 0.001;
uint32_t min_tpc_tti_interval = 1;
float ul_snr_avg_alpha = 0.05;
int init_ul_snr_value = 5;
};
struct cell_cfg_t {
@ -80,6 +89,7 @@ public:
/* pusch configuration */
srsran_pusch_hopping_cfg_t pusch_hopping_cfg;
float target_pusch_ul_sinr;
int min_phr_thres;
bool enable_phr_handling;
bool enable_64qam;
@ -295,7 +305,7 @@ public:
virtual int ul_crc_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, bool crc) = 0;
virtual int ul_sr_info(uint32_t tti, uint16_t rnti) = 0;
virtual int ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr) = 0;
virtual int ul_phr(uint16_t rnti, int phr) = 0;
virtual int ul_phr(uint16_t rnti, int phr, uint32_t ul_nof_prb) = 0;
virtual int ul_snr_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, float snr, uint32_t ul_ch_code) = 0;
/* Run Scheduler for this tti */

@ -168,8 +168,6 @@ public:
virtual bool cell_search() = 0;
virtual bool cell_select(phy_cell_t cell) = 0;
virtual bool cell_is_camping() = 0;
virtual void enable_pregen_signals(bool enable) = 0;
};
// Combined interface for stack (MAC and RRC) to access PHY

@ -76,7 +76,7 @@ public:
/* MAC calls RLC to get RLC segment of nof_bytes length.
* Segmentation happens in this function. RLC PDU is stored in payload. */
virtual int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0;
virtual uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0;
/* MAC calls RLC to push an RLC PDU. This function is called from an independent MAC thread.
* PDU gets placed into the buffer and higher layer thread gets notified. */

@ -100,6 +100,7 @@ class rrc_interface_rlc
{
public:
virtual void max_retx_attempted() = 0;
virtual void protocol_failure() = 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;
};

@ -39,7 +39,7 @@ public:
class process_callback
{
public:
virtual void process_pdu(uint8_t* buff, uint32_t len, channel_t channel) = 0;
virtual void process_pdu(uint8_t* buff, uint32_t len, channel_t channel, int ul_nof_prbs = -1) = 0;
};
pdu_queue(srslog::basic_logger& logger) : pool(DEFAULT_POOL_SIZE), callback(NULL), logger(logger) {}
@ -47,7 +47,7 @@ public:
uint8_t* request(uint32_t len);
void deallocate(const uint8_t* pdu);
void push(const uint8_t* ptr, uint32_t len, channel_t channel = DCH);
void push(const uint8_t* ptr, uint32_t len, channel_t channel = DCH, int ul_nof_prbs = -1);
bool process_pdus();
@ -61,6 +61,7 @@ private:
uint8_t ptr[MAX_PDU_LEN];
uint32_t len;
channel_t channel;
int grant_nof_prbs;
#ifdef SRSRAN_BUFFER_POOL_LOG_ENABLED
char debug_name[128];
#endif

@ -0,0 +1,62 @@
/**
*
* \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_DMRS_PBCH_H
#define SRSRAN_DMRS_PBCH_H
#include "srsran/phy/common/phy_common_nr.h"
/**
* @brief Describes the DeModulation Reference Signals (DMRS) for NR PBCH configuration
*/
typedef struct SRSRAN_API {
uint32_t N_id; ///< Physical cell identifier
uint32_t n_hf; ///< Number of half radio frame, 0 or 1
uint32_t ssb_idx; ///< SSB candidate index
uint32_t L_max; ///< Number of SSB opportunities in half radio frame
float beta; ///< Power allocation specified in TS 38.213
srsran_subcarrier_spacing_t scs; ///< SSB configured subcarrier spacing
} srsran_dmrs_pbch_cfg_t;
/**
* @brief Describes an NR PBCH DMRS based measurement
*/
typedef struct SRSRAN_API {
float corr; ///< Normalised correlation
float epre; ///< Linear energy per resource element
float rsrp; ///< Linear RSRP
float cfo_hz; ///< CFO in Hz
float avg_delay_us; ///< Average delay in us
} srsran_dmrs_pbch_meas_t;
/**
* @brief Put NR PBCH DMRS in the SSB resource grid
* @param cfg PBCH DMRS configuration
* @param[out] ssb_grid SSB resource grid
* @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]);
/**
* @brief Estimates NR PBCH DMRS
* @param cfg PBCH DMRS configuration
* @param ssb_grid Demodulated SSB resource grid
* @param[out] ce Estimated channel
* @param[out] meas Estimated channel measurements
* @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
cf_t ce[SRSRAN_SSB_NOF_RE],
srsran_dmrs_pbch_meas_t* meas);
#endif // SRSRAN_DMRS_PBCH_H

@ -47,6 +47,12 @@ SRSRAN_API void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float va
SRSRAN_API void srsran_sequence_state_apply_f(srsran_sequence_state_t* s, const float* in, float* out, uint32_t length);
SRSRAN_API void
srsran_sequence_state_apply_c(srsran_sequence_state_t* s, const int8_t* in, int8_t* out, uint32_t length);
SRSRAN_API
void srsran_sequence_state_apply_bit(srsran_sequence_state_t* s, const uint8_t* in, uint8_t* out, uint32_t length);
SRSRAN_API void srsran_sequence_state_advance(srsran_sequence_state_t* s, uint32_t length);
typedef struct SRSRAN_API {

@ -102,7 +102,7 @@ SRSRAN_API void srsran_enb_dl_free(srsran_enb_dl_t* q);
SRSRAN_API int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell);
SRSRAN_API bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, uint32_t ncce);
SRSRAN_API bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, const srsran_dci_location_t* loc);
SRSRAN_API void srsran_enb_dl_put_base(srsran_enb_dl_t* q, srsran_dl_sf_cfg_t* dl_sf);

@ -89,7 +89,7 @@ SRSRAN_API int srsran_predecoding_diversity(cf_t* y,
SRSRAN_API int srsran_predecoding_diversity_multi(cf_t* y[SRSRAN_MAX_PORTS],
cf_t* h[SRSRAN_MAX_PORTS][SRSRAN_MAX_PORTS],
cf_t* x[SRSRAN_MAX_LAYERS],
float* csi[SRSRAN_MAX_LAYERS],
float* csi[SRSRAN_MAX_CODEWORDS],
int nof_rxant,
int nof_ports,
int nof_symbols,

@ -144,7 +144,8 @@ typedef struct {
SRSRAN_API int srsran_cqi_size(srsran_cqi_cfg_t* cfg);
SRSRAN_API int srsran_cqi_value_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_value_t* value, uint8_t* buff);
SRSRAN_API int
srsran_cqi_value_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_value_t* value, uint8_t buff[SRSRAN_CQI_MAX_BITS]);
SRSRAN_API int
srsran_cqi_value_unpack(srsran_cqi_cfg_t* cfg, uint8_t buff[SRSRAN_CQI_MAX_BITS], srsran_cqi_value_t* value);

@ -238,8 +238,9 @@ SRSRAN_API char* srsran_dci_format_string_short(srsran_dci_format_t format);
SRSRAN_API bool
srsran_location_find(const srsran_dci_location_t* locations, uint32_t nof_locations, srsran_dci_location_t x);
SRSRAN_API bool
srsran_location_find_ncce(const srsran_dci_location_t* locations, uint32_t nof_locations, uint32_t ncce);
SRSRAN_API bool srsran_location_find_location(const srsran_dci_location_t* locations,
uint32_t nof_locations,
const srsran_dci_location_t* location);
SRSRAN_API int srsran_dci_location_set(srsran_dci_location_t* c, uint32_t L, uint32_t nCCE);

@ -22,13 +22,108 @@
#ifndef SRSRAN_PBCH_NR_H
#define SRSRAN_PBCH_NR_H
#include "srsran/config.h"
#include "srsran/phy/common/phy_common_nr.h"
#include "srsran/phy/fec/crc.h"
#include "srsran/phy/fec/polar/polar_code.h"
#include "srsran/phy/fec/polar/polar_decoder.h"
#include "srsran/phy/fec/polar/polar_encoder.h"
#include "srsran/phy/fec/polar/polar_rm.h"
#include "srsran/phy/modem/modem_table.h"
/**
* @brief Descibes the NR PBCH message
* @brief NR PBCH payload size generated by higher layers, deduced from TS 38.331 MIB description
*/
#define SRSRAN_PBCH_NR_PAYLOAD_SZ 24
/**
* @brief Describes the NR PBCH object initialisation arguments
*/
typedef struct SRSRAN_API {
bool enable_encode; ///< Enable encoder
bool enable_decode; ///< Enable decoder
bool disable_simd; ///< Disable SIMD polar encoder/decoder
} srsran_pbch_nr_args_t;
/**
* @brief Describes the NR PBCH configuration
*/
typedef struct SRSRAN_API {
uint32_t N_id; ///< Physical cell identifier
srsran_subcarrier_spacing_t ssb_scs; ///< SSB Subcarrier spacing
uint32_t Lmax; ///< Number of SSB opportunities, described in TS 38.213 4.1 ...
float beta; ///< Scaling factor for PBCH symbols, set to zero for default
float beta_dmrs; ///< Scaling factor for PBCH DM-RS, set to zero for default
} srsran_pbch_nr_cfg_t;
/**
* @brief Describes the NR PBCH object initialisation arguments
*/
typedef struct SRSRAN_API {
srsran_polar_code_t code;
srsran_polar_encoder_t polar_encoder;
srsran_polar_decoder_t polar_decoder;
srsran_polar_rm_t polar_rm_tx;
srsran_polar_rm_t polar_rm_rx;
srsran_crc_t crc;
srsran_modem_table_t qpsk;
} srsran_pbch_nr_t;
/**
* @brief Describes the PBCH message
*/
typedef struct SRSRAN_API {
void* TBD;
uint8_t payload[SRSRAN_PBCH_NR_PAYLOAD_SZ]; ///< Actual PBCH payload provided by higher layers
uint8_t sfn_4lsb; ///< SFN 4 LSB
uint8_t ssb_idx; ///< SS/PBCH blocks index described in TS 38.213 4.1
uint8_t k_ssb_msb; ///< Subcarrier offset MSB described in TS 38.211 7.4.3.1
bool hrf; ///< Half Radio Frame bit
bool crc; ///< Decoder only, it is true only if the received CRC matches
} srsran_pbch_msg_nr_t;
/**
* @brief Initialises an NR PBCH object with the provided arguments
* @param q NR PBCH object
* @param args Arguments providing the desired configuration
* @return SRSRAN_SUCCESS if initialization is successful, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_pbch_nr_init(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args);
/**
* @brief Deallocates an NR PBCH object
* @param q NR PBCH object
*/
SRSRAN_API void srsran_pbch_nr_free(srsran_pbch_nr_t* q);
/**
* @brief Encodes an NR PBCH message into a SSB resource grid
* @param q NR PBCH object
* @param cfg NR PBCH configuration
* @param msg NR PBCH message to transmit
* @param[out] ssb_grid SSB resource grid
* @return SRSRAN_SUCCESS if encoding is successful, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
const srsran_pbch_nr_cfg_t* cfg,
const srsran_pbch_msg_nr_t* msg,
cf_t ssb_grid[SRSRAN_SSB_NOF_RE]);
/**
* @brief Decodes an NR PBCH message in the SSB resource grid
* @param q NR PBCH object
* @param cfg NR PBCH configuration
* @param ssb_idx SSB candidate index
* @param[in] ssb_grid SSB resource grid
* @param[in] ce Channel estimates for the SSB resource grid
* @param msg NR PBCH message received
* @return SRSRAN_SUCCESS if decoding is successful, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
const srsran_pbch_nr_cfg_t* cfg,
uint32_t ssb_idx,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
const cf_t ce[SRSRAN_SSB_NOF_RE],
srsran_pbch_msg_nr_t* msg);
SRSRAN_API uint32_t srsran_pbch_msg_info(const srsran_pbch_msg_nr_t* msg, char* str, uint32_t str_len);
#endif // SRSRAN_PBCH_NR_H

@ -104,6 +104,14 @@ SRSRAN_API int srsran_pdcch_extract_llr(srsran_pdcch_t* q,
SRSRAN_API int
srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dci_cfg_t* dci_cfg, srsran_dci_msg_t* msg);
/**
* @brief Computes decoded DCI correlation. It encodes the given DCI message and compares it with the received LLRs
* @param q PDCCH object
* @param msg Previously decoded DCI message
* @return The normalized correlation between the restored symbols and the received LLRs
*/
SRSRAN_API float srsran_pdcch_msg_corr(srsran_pdcch_t* q, srsran_dci_msg_t* msg);
SRSRAN_API int
srsran_pdcch_dci_decode(srsran_pdcch_t* q, float* e, uint8_t* data, uint32_t E, uint32_t nof_bits, uint16_t* crc);

@ -113,7 +113,7 @@ SRSRAN_API int srsran_pusch_nr_decode(srsran_pusch_nr_t* q,
const srsran_sch_grant_nr_t* grant,
srsran_chest_dl_res_t* channel,
cf_t* sf_symbols[SRSRAN_MAX_PORTS],
srsran_pusch_res_nr_t data[SRSRAN_MAX_TB]);
srsran_pusch_res_nr_t* data[SRSRAN_MAX_TB]);
SRSRAN_API uint32_t srsran_pusch_nr_rx_info(const srsran_pusch_nr_t* q,
const srsran_sch_cfg_nr_t* cfg,

@ -46,18 +46,20 @@
/**
* @brief Maximum number of SSB positions in burst. Defined in TS 38.331 ServingCellConfigCommon, ssb-PositionsInBurst
*/
#define SRSRAN_SSB_NOF_POSITION 64
#define SRSRAN_SSB_NOF_CANDIDATES 64
/**
* @brief Describes SSB object initialization arguments
*/
typedef struct SRSRAN_API {
double max_srate_hz; ///< Maximum sampling rate in Hz, set to zero to use default
srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing
bool enable_search; ///< Enables PSS/SSS blind search
bool enable_measure; ///< Enables PSS/SSS CSI measurements and frequency domain search
bool enable_encode; ///< Enables PBCH Encoder
bool enable_decode; ///< Enables PBCH Decoder
double max_srate_hz; ///< Maximum sampling rate in Hz, set to zero to use default
srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing
bool enable_search; ///< Enables PSS/SSS blind search
bool enable_measure; ///< Enables PSS/SSS CSI measurements and frequency domain search
bool enable_encode; ///< Enables PBCH Encoder
bool enable_decode; ///< Enables PBCH Decoder
bool disable_polar_simd; ///< Disables polar encoder/decoder SIMD acceleration
float pbch_dmrs_thr; ///< NR-PBCH DMRS threshold for blind decoding, set to 0 for default
} srsran_ssb_args_t;
/**
@ -69,13 +71,12 @@ typedef struct SRSRAN_API {
double ssb_freq_hz; ///< SSB center frequency
srsran_subcarrier_spacing_t scs; ///< SSB configured Subcarrier spacing
srsran_ssb_patern_t pattern; ///< SSB pattern as defined in TS 38.313 section 4.1 Cell search
bool position[SRSRAN_SSB_NOF_POSITION]; ///< Indicates the time domain positions of the transmitted SS-blocks
srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD)
uint32_t periodicity_ms; ///< SSB periodicity in ms
float beta_pss; ////< PSS power allocation
float beta_sss; ////< SSS power allocation
float beta_pbch; ////< PBCH power allocation
float beta_pbch_dmrs; ////< PBCH DMRS power allocation
srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD)
uint32_t periodicity_ms; ///< SSB periodicity in ms
float beta_pss; ////< PSS power allocation
float beta_sss; ////< SSS power allocation
float beta_pbch; ////< PBCH power allocation
float beta_pbch_dmrs; ////< PBCH DMRS power allocation
} srsran_ssb_cfg_t;
/**
@ -86,21 +87,25 @@ typedef struct SRSRAN_API {
srsran_ssb_cfg_t cfg; ///< Stores last configuration
/// Sampling rate dependent parameters
float scs_hz; ///< Subcarrier spacing in Hz
uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate
uint32_t max_corr_sz; ///< Maximum correlation size
uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate)
uint32_t corr_sz; ///< Correlation size
uint32_t corr_window; ///< Correlation window length
int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS)
uint32_t t_offset; ///< Current SSB integer time offset (number of samples)
uint32_t cp_sz[SRSRAN_SSB_DURATION_NSYMB]; ///< CP length for each SSB symbol
float scs_hz; ///< Subcarrier spacing in Hz
uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate
uint32_t max_corr_sz; ///< Maximum correlation size
uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate)
uint32_t corr_sz; ///< Correlation size
uint32_t corr_window; ///< Correlation window length
int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS)
uint32_t cp_sz; ///< CP length for the given symbol size
/// Other parameters
uint32_t l_first[SRSRAN_SSB_NOF_CANDIDATES]; ///< Start symbol for each SSB candidate in half radio frame
uint32_t Lmax; ///< Number of SSB candidates
/// Internal Objects
srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB
srsran_dft_plan_t fft; ///< FFT object for demodulate the SSB.
srsran_dft_plan_t fft_corr; ///< FFT for correlation
srsran_dft_plan_t ifft_corr; ///< IFFT for correlation
srsran_pbch_nr_t pbch; ///< PBCH encoder and decoder
/// Frequency/Time domain temporal data
cf_t* tmp_freq; ///< Temporal frequency domain buffer
@ -132,10 +137,20 @@ SRSRAN_API void srsran_ssb_free(srsran_ssb_t* q);
SRSRAN_API int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg);
/**
* @brief Decodes PBCH in the given time domain signal
* @note It currently expects an input buffer of half radio frame
* @param q SSB object
* @param N_id Physical Cell Identifier
* @param ssb_idx SSB candidate index
* @param n_hf Number of hald radio frame, 0 or 1
* @param in Input baseband buffer
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q, const cf_t* in, srsran_pbch_msg_nr_t* msg);
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id,
uint32_t ssb_idx,
uint32_t n_hf,
const cf_t* in,
srsran_pbch_msg_nr_t* msg);
/**
* @brief Decides if the SSB object is configured and a given subframe is configured for SSB transmission
@ -149,11 +164,16 @@ SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx);
* @brief Adds SSB to a given signal in time domain
* @param q SSB object
* @param N_id Physical Cell Identifier
* @param ssb_idx SSB candidate index
* @param msg NR PBCH message to transmit
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int
srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out);
SRSRAN_API int srsran_ssb_add(srsran_ssb_t* q,
uint32_t N_id,
uint32_t ssb_idx,
const srsran_pbch_msg_nr_t* msg,
const cf_t* in,
cf_t* out);
/**
* @brief Perform cell search and measurement
@ -174,11 +194,15 @@ SRSRAN_API int srsran_ssb_csi_search(srsran_ssb_t* q,
* @brief Perform Channel State Information (CSI) measurement from the SSB
* @param q NR PSS object
* @param N_id Physical Cell Identifier
* @param ssb_idx SSB candidate index
* @param in Base-band signal
* @param meas SSB-based CSI measurement
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int
srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas);
SRSRAN_API int srsran_ssb_csi_measure(srsran_ssb_t* q,
uint32_t N_id,
uint32_t ssb_idx,
const cf_t* in,
srsran_csi_trs_measurements_t* meas);
#endif // SRSRAN_SSB_H

@ -208,7 +208,7 @@ SRSRAN_API int srsran_ue_dl_decode_pdsch(srsran_ue_dl_t* q,
SRSRAN_API int srsran_ue_dl_decode_pmch(srsran_ue_dl_t* q,
srsran_dl_sf_cfg_t* sf,
srsran_pmch_cfg_t* pmch_cfg,
srsran_pdsch_res_t* data);
srsran_pdsch_res_t data[SRSRAN_MAX_CODEWORDS]);
SRSRAN_API int srsran_ue_dl_decode_phich(srsran_ue_dl_t* q,
srsran_dl_sf_cfg_t* sf,

@ -48,6 +48,8 @@ SRSRAN_API float srsran_random_gauss_dist(srsran_random_t q, float std_dev);
SRSRAN_API bool srsran_random_bool(srsran_random_t q, float prob_true);
SRSRAN_API void srsran_random_bit_vector(srsran_random_t q, uint8_t* c, uint32_t nsamples);
SRSRAN_API void srsran_random_free(srsran_random_t q);
#ifdef __cplusplus

@ -132,16 +132,16 @@ SRSRAN_API void srsran_vec_u16_copy(uint16_t* dst, const uint16_t* src, uint32_t
SRSRAN_API void srsran_vec_i16_copy(int16_t* dst, const int16_t* src, uint32_t len);
/* print vectors */
SRSRAN_API void srsran_vec_fprint_c(FILE* stream, const cf_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_f(FILE* stream, const float* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_b(FILE* stream, const uint8_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_bs(FILE* stream, const int8_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_byte(FILE* stream, const uint8_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_i(FILE* stream, const int* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_s(FILE* stream, const int16_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_hex(FILE* stream, uint8_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_sprint_bin(char* str, const uint32_t max_str_len, const uint8_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_c(FILE* stream, const cf_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_f(FILE* stream, const float* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_b(FILE* stream, const uint8_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_bs(FILE* stream, const int8_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_byte(FILE* stream, const uint8_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_i(FILE* stream, const int* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_s(FILE* stream, const int16_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_fprint_hex(FILE* stream, uint8_t* x, const uint32_t len);
SRSRAN_API uint32_t srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, const uint32_t len);
SRSRAN_API void srsran_vec_sprint_bin(char* str, const uint32_t max_str_len, const uint8_t* x, const uint32_t len);
/* Saves/loads a vector to a file */
SRSRAN_API void srsran_vec_save_file(char* filename, const void* buffer, const uint32_t len);

@ -102,10 +102,10 @@ public:
void set_channels(const std::list<channel_cfg_t>& channels_) { available_channels = channels_; }
/**
* Finds an unused physical channel that supports the provided frequency and assigns it to logical channel
* logical_ch
* It deallocates the logical channel if it has been already allocated and it is not suitable, then finds an unused
* physical channel that supports the provided frequency and assigns it to logical channel logical_ch
* @param logical_ch logical channel index
* @param freq Frequency (in Hz) that we want to receive/transmitt
* @param freq Frequency (in Hz) that we want to receive/transmit
* @return true if a physical channel supporting this frequency was found or false otherwise
*/
bool allocate_freq(const uint32_t& logical_ch, const float& freq);
@ -147,6 +147,8 @@ private:
mutable std::mutex mutex = {};
uint32_t nof_antennas = 1;
uint32_t nof_channels_x_dev = 1;
void release_freq_(const uint32_t& logical_ch);
};
} // namespace srsran

@ -95,6 +95,7 @@ extern "C" {
#include "srsran/phy/phch/dci.h"
#include "srsran/phy/phch/dci_nr.h"
#include "srsran/phy/phch/pbch.h"
#include "srsran/phy/phch/pbch_nr.h"
#include "srsran/phy/phch/pcfich.h"
#include "srsran/phy/phch/pdcch.h"
#include "srsran/phy/phch/pdcch_nr.h"

@ -59,7 +59,7 @@ class rlc_dummy_interface : public rlc_interface_mac
public:
bool has_data_locked(const uint32_t lcid) override { return false; }
uint32_t get_buffer_state(const uint32_t lcid) override { return 0; }
int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override { return 0; }
uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override { return 0; }
void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override {}
void write_pdu_bcch_bch(srsran::unique_byte_buffer_t payload) override {}
void write_pdu_bcch_dlsch(uint8_t* payload, uint32_t nof_bytes) override {}
@ -85,8 +85,6 @@ class phy_dummy_interface : public phy_interface_rrc_lte
bool cell_search() override { return true; }
bool cell_select(phy_cell_t cell) override { return true; }
bool cell_is_camping() override { return false; }
void enable_pregen_signals(bool enable) override {}
};
} // namespace srsue

@ -89,33 +89,37 @@ public:
private:
struct push_callback {
explicit push_callback(uint32_t& unread_bytes_, uint32_t& n_sdus_) : unread_bytes(&unread_bytes_), n_sdus(&n_sdus_)
explicit push_callback(std::atomic<uint32_t>& unread_bytes_, std::atomic<uint32_t>& n_sdus_) :
unread_bytes(unread_bytes_), n_sdus(n_sdus_)
{}
void operator()(const unique_byte_buffer_t& msg)
{
*unread_bytes += msg->N_bytes;
(*n_sdus)++;
unread_bytes.fetch_add(msg->N_bytes, std::memory_order_relaxed);
n_sdus.fetch_add(1, std::memory_order_relaxed);
}
uint32_t* unread_bytes;
uint32_t* n_sdus;
std::atomic<uint32_t>& unread_bytes;
std::atomic<uint32_t>& n_sdus;
};
struct pop_callback {
explicit pop_callback(uint32_t& unread_bytes_, uint32_t& n_sdus_) : unread_bytes(&unread_bytes_), n_sdus(&n_sdus_)
explicit pop_callback(std::atomic<uint32_t>& unread_bytes_, std::atomic<uint32_t>& n_sdus_) :
unread_bytes(unread_bytes_), n_sdus(n_sdus_)
{}
void operator()(const unique_byte_buffer_t& msg)
{
if (msg == nullptr) {
return;
}
*unread_bytes -= std::min(msg->N_bytes, *unread_bytes);
*n_sdus = std::max(0, (int32_t)(*n_sdus) - 1);
// non-atomic update of both state variables
unread_bytes.fetch_sub(std::min(msg->N_bytes, unread_bytes.load(std::memory_order_relaxed)),
std::memory_order_relaxed);
n_sdus.store(std::max(0, (int32_t)(n_sdus.load(std::memory_order_relaxed)) - 1), std::memory_order_relaxed);
}
uint32_t* unread_bytes;
uint32_t* n_sdus;
std::atomic<uint32_t>& unread_bytes;
std::atomic<uint32_t>& n_sdus;
};
uint32_t unread_bytes = 0;
uint32_t n_sdus = 0;
std::atomic<uint32_t> unread_bytes = {0};
std::atomic<uint32_t> n_sdus = {0};
public:
dyn_blocking_queue<unique_byte_buffer_t, push_callback, pop_callback> queue;

@ -75,8 +75,8 @@ public:
bool has_data_locked(const uint32_t lcid);
uint32_t get_buffer_state(const uint32_t lcid);
uint32_t get_total_mch_buffer_state(uint32_t lcid);
int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);
int read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);
uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);
uint32_t read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);
int get_increment_sequence_num();
void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);
void write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu);

@ -354,7 +354,7 @@ public:
// MAC interface
bool has_data();
uint32_t get_buffer_state();
int read_pdu(uint8_t* payload, uint32_t nof_bytes);
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
rlc_bearer_metrics_t get_metrics();
@ -377,7 +377,7 @@ private:
void stop();
int write_sdu(unique_byte_buffer_t sdu);
int read_pdu(uint8_t* payload, uint32_t nof_bytes);
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void discard_sdu(uint32_t discard_sn);
bool sdu_queue_is_full();
@ -401,7 +401,7 @@ private:
void debug_state();
int required_buffer_size(rlc_amd_retx_t retx);
int required_buffer_size(const rlc_amd_retx_t& retx);
void retransmit_pdu(uint32_t sn);
// Helpers

@ -283,7 +283,7 @@ public:
virtual bool has_data() = 0;
bool is_suspended() { return suspended; };
virtual uint32_t get_buffer_state() = 0;
virtual int read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0;
virtual uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0;
virtual void write_pdu(uint8_t* payload, uint32_t nof_bytes) = 0;
virtual void set_bsr_callback(bsr_callback_t callback) = 0;
@ -329,4 +329,5 @@ private:
};
} // namespace srsran
#endif // SRSRAN_RLC_COMMON_H

@ -63,7 +63,7 @@ public:
// MAC interface
bool has_data() override;
uint32_t get_buffer_state() override;
int read_pdu(uint8_t* payload, uint32_t nof_bytes) override;
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) override;
void write_pdu(uint8_t* payload, uint32_t nof_bytes) override;
void set_bsr_callback(bsr_callback_t callback) override {}
@ -75,7 +75,7 @@ private:
srsue::pdcp_interface_rlc* pdcp = nullptr;
srsue::rrc_interface_rlc* rrc = nullptr;
bool tx_enabled = true;
std::atomic<bool> tx_enabled = {true};
rlc_bearer_metrics_t metrics = {};

@ -67,7 +67,7 @@ public:
// MAC interface
bool has_data();
uint32_t get_buffer_state();
int read_pdu(uint8_t* payload, uint32_t nof_bytes);
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
int get_increment_sequence_num();
@ -84,7 +84,7 @@ protected:
rlc_um_base_tx(rlc_um_base* parent_);
virtual ~rlc_um_base_tx();
virtual bool configure(const rlc_config_t& cfg, std::string rb_name) = 0;
int build_data_pdu(uint8_t* payload, uint32_t nof_bytes);
uint32_t build_data_pdu(uint8_t* payload, uint32_t nof_bytes);
void stop();
void reestablish();
void empty_queue();
@ -116,7 +116,7 @@ protected:
srsran::rolling_average<double> mean_pdu_latency_us;
#endif
virtual int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) = 0;
virtual uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) = 0;
// helper functions
virtual void debug_state() = 0;

@ -57,7 +57,7 @@ private:
rlc_um_lte_tx(rlc_um_base* parent_);
bool configure(const rlc_config_t& cfg, std::string rb_name);
int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes);
uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes);
uint32_t get_buffer_state();
bool sdu_queue_is_full();

@ -58,7 +58,7 @@ private:
rlc_um_nr_tx(rlc_um_base* parent_);
bool configure(const rlc_config_t& cfg, std::string rb_name);
int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes);
uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes);
uint32_t get_buffer_state();
private:

@ -1309,10 +1309,10 @@ bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrie
}
template <class bitstring_t>
static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst,
std::array<bool, SRSRAN_SSB_NOF_POSITION>& position_in_burst)
static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst,
std::array<bool, SRSRAN_SSB_NOF_CANDIDATES>& position_in_burst)
{
for (uint32_t i = 0; i < SRSRAN_SSB_NOF_POSITION; i++) {
for (uint32_t i = 0; i < SRSRAN_SSB_NOF_CANDIDATES; i++) {
if (i < ans1_position_in_burst.length()) {
position_in_burst[i] = ans1_position_in_burst.get(ans1_position_in_burst.length() - 1 - i);
} else {

@ -51,15 +51,20 @@ void thread_pool::worker::setup(uint32_t id, thread_pool* parent, uint32_t prio,
void thread_pool::worker::run_thread()
{
set_name(std::string("WORKER") + std::to_string(my_id));
while (my_parent->status[my_id] != STOP) {
while (running.load(std::memory_order_relaxed)) {
wait_to_start();
if (my_parent->status[my_id] != STOP) {
if (running.load(std::memory_order_relaxed)) {
work_imp();
finished();
}
}
}
void thread_pool::worker::stop()
{
running = false;
}
uint32_t thread_pool::worker::get_id()
{
return my_id;
@ -101,6 +106,7 @@ void thread_pool::stop()
for (uint32_t i = 0; i < nof_workers; i++) {
if (workers[i]) {
debug_thread("stop(): stopping %d\n", i);
workers[i]->stop();
status[i] = STOP;
cvar_worker[i].notify_all();
cvar_queue.notify_all();

@ -145,6 +145,11 @@ bool threads_new_rt_cpu(pthread_t* thread, void* (*start_routine)(void*), void*
}
}
// TSAN seems to have issues with thread attributes when running as normal user, disable them in that case
#if HAVE_TSAN
attr_enable = false;
#endif
int err = pthread_create(thread, attr_enable ? &attr : NULL, start_routine, arg);
if (err) {
if (EPERM == err) {

@ -559,7 +559,7 @@ bool sch_subh::is_var_len_ce()
uint16_t sch_subh::get_c_rnti()
{
return le16toh((uint16_t)payload[0] << 8 | payload[1]);
return (uint16_t)payload[0] << 8 | payload[1];
}
uint64_t sch_subh::get_con_res_id()

@ -62,12 +62,13 @@ void pdu_queue::deallocate(const uint8_t* pdu)
* This function enqueues the packet and returns quicly because ACK
* deadline is important here.
*/
void pdu_queue::push(const uint8_t* ptr, uint32_t len, channel_t channel)
void pdu_queue::push(const uint8_t* ptr, uint32_t len, channel_t channel, int grant_nof_prbs)
{
if (ptr) {
pdu_t* pdu = (pdu_t*)ptr;
pdu->len = len;
pdu->channel = channel;
pdu_t* pdu = (pdu_t*)ptr;
pdu->len = len;
pdu->channel = channel;
pdu->grant_nof_prbs = grant_nof_prbs;
if (!pdu_q.try_push(pdu)) {
logger.warning("Error pushing pdu: queue is full");
}
@ -83,7 +84,7 @@ bool pdu_queue::process_pdus()
pdu_t* pdu;
while (pdu_q.try_pop(pdu)) {
if (callback) {
callback->process_pdu(pdu->ptr, pdu->len, pdu->channel);
callback->process_pdu(pdu->ptr, pdu->len, pdu->channel, pdu->grant_nof_prbs);
}
cnt++;
have_data = true;

@ -332,15 +332,18 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf,
: srsran_refsignal_cs_nof_re(&q->csr_refs, sf, port_id);
uint32_t nsymbols = (ch_mode == SRSRAN_SF_MBSFN) ? srsran_refsignal_mbsfn_nof_symbols()
: srsran_refsignal_cs_nof_symbols(&q->csr_refs, sf, port_id);
if (nsymbols == 0) {
ERROR("Invalid number of CRS symbols\n");
return SRSRAN_ERROR;
}
uint32_t nref = npilots / nsymbols;
uint32_t fidx =
(ch_mode == SRSRAN_SF_MBSFN) ? srsran_refsignal_mbsfn_fidx(1) : srsran_refsignal_cs_fidx(q->cell, 0, port_id, 0);
cf_t* input2d[nsymbols + 2];
cf_t* tmp_noise = q->tmp_noise;
// Special case for 1 symbol
if (nsymbols == 1) {
// Special case for 1 or 2 symbol
if (nsymbols < 3) {
srsran_vec_sc_prod_cfc(q->pilot_estimates + 1, weight, tmp_noise, nref - 2);
srsran_vec_sum_ccc(q->pilot_estimates + 0, tmp_noise, tmp_noise, nref - 2);
srsran_vec_sum_ccc(q->pilot_estimates + 2, tmp_noise, tmp_noise, nref - 2);
@ -350,30 +353,21 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf,
return sum_power;
}
// Convert pilots to 2D to ease access
cf_t* input2d[4]; // The maximum number of symbols is 4
for (int i = 0; i < nsymbols; i++) {
input2d[i + 1] = &q->pilot_estimates[i * nref];
}
input2d[0] = &q->tmp_noise[nref];
if (nsymbols > 3) {
srsran_vec_sc_prod_cfc(input2d[2], 2.0f, input2d[0], nref);
srsran_vec_sub_ccc(input2d[0], input2d[4], input2d[0], nref);
} else {
srsran_vec_sc_prod_cfc(input2d[2], 1.0f, input2d[0], nref);
input2d[i] = &q->pilot_estimates[i * nref];
}
input2d[nsymbols + 1] = &q->tmp_noise[nref * 2];
if (nsymbols > 3) {
srsran_vec_sc_prod_cfc(input2d[nsymbols - 1], 2.0f, input2d[nsymbols + 1], nref);
srsran_vec_sub_ccc(input2d[nsymbols + 1], input2d[nsymbols - 3], input2d[nsymbols + 1], nref);
} else {
srsran_vec_sc_prod_cfc(input2d[nsymbols - 1], 1.0f, input2d[nsymbols + 1], nref);
}
for (int i = 1; i < nsymbols + 1; i++) {
// Compares surrounding pilots in time/frequency. It requires at least 3 symbols with pilots.
for (int i = 1; i < nsymbols - 1; i++) {
// Calculate previous and next symbol indexes offset
uint32_t offset = ((fidx < 3) ^ (i & 1)) ? 0 : 1;
// Write estimates from this symbols
srsran_vec_sc_prod_cfc(input2d[i], weight, tmp_noise, nref);
// Add the previous symbol estimates and extrapolate first/last element
srsran_vec_sum_ccc(&input2d[i - 1][0], &tmp_noise[offset], &tmp_noise[offset], nref - offset);
srsran_vec_sum_ccc(&input2d[i - 1][1 - offset], &tmp_noise[0], &tmp_noise[0], nref + offset - 1);
if (offset) {
@ -382,6 +376,7 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf,
tmp_noise[nref - 1] += 2.0f * input2d[i - 1][nref - 2] - input2d[i - 1][nref - 1];
}
// Add the next symbol estimates and extrapolate first/last element
srsran_vec_sum_ccc(&input2d[i + 1][0], &tmp_noise[offset], &tmp_noise[offset], nref - offset);
srsran_vec_sum_ccc(&input2d[i + 1][1 - offset], &tmp_noise[0], &tmp_noise[0], nref + offset - 1);
if (offset) {
@ -390,14 +385,18 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf,
tmp_noise[nref - 1] += 2.0f * input2d[i + 1][nref - 2] - input2d[i + 1][nref - 1];
}
// Scale to normalise to this symbol
srsran_vec_sc_prod_cfc(tmp_noise, 1.0f / (weight + 4.0f), tmp_noise, nref);
// Subtract this symbol
srsran_vec_sub_ccc(input2d[i], tmp_noise, tmp_noise, nref);
sum_power = srsran_vec_avg_power_cf(tmp_noise, nref);
// The left signal after the subtraction can be considered noise
sum_power += srsran_vec_avg_power_cf(tmp_noise, nref);
count++;
}
return sum_power / (float)count * sqrtf(weight + 4.0f);
return sum_power / (float)count;
}
static float estimate_noise_pss(srsran_chest_dl_t* q, cf_t* input, cf_t* ce)

@ -0,0 +1,218 @@
/**
*
* \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/phy/ch_estimation/dmrs_pbch.h"
#include "srsran/phy/common/sequence.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include <complex.h>
#include <math.h>
/*
* Number of NR PBCH DMRS resource elements present in an SSB resource grid
*/
#define DMRS_PBCH_NOF_RE 144
static uint32_t dmrs_pbch_cinit(const srsran_dmrs_pbch_cfg_t* cfg)
{
// Default values for L_max == 4
uint64_t i_ssb = (cfg->ssb_idx & 0b11U) + 4UL * cfg->n_hf; // Least 2 significant bits
if (cfg->L_max == 8 || cfg->L_max == 64) {
i_ssb = cfg->ssb_idx & 0b111U; // Least 3 significant bits
}
return SRSRAN_SEQUENCE_MOD(((i_ssb + 1UL) * (SRSRAN_FLOOR(cfg->N_id, 4UL) + 1UL) << 11UL) + ((i_ssb + 1UL) << 6UL) +
(cfg->N_id % 4));
}
int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
{
// Validate inputs
if (cfg == NULL || ssb_grid == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Calculate index shift
uint32_t v = cfg->N_id % 4;
// Calculate power allocation
float beta = M_SQRT1_2;
if (isnormal(cfg->beta)) {
beta = cfg->beta;
}
// Initialise sequence
uint32_t cinit = dmrs_pbch_cinit(cfg);
srsran_sequence_state_t sequence_state = {};
srsran_sequence_state_init(&sequence_state, cinit);
// Generate sequence
cf_t r[DMRS_PBCH_NOF_RE];
srsran_sequence_state_gen_f(&sequence_state, beta, (float*)r, DMRS_PBCH_NOF_RE * 2);
// r sequence read index
uint32_t r_idx = 0;
// Put sequence in symbol 1
for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
ssb_grid[SRSRAN_SSB_BW_SUBC * 1 + k] = r[r_idx++];
}
// Put sequence in symbol 2, lower section
for (uint32_t k = v; k < 48; k += 4) {
ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k] = r[r_idx++];
}
// Put sequence in symbol 2, upper section
for (uint32_t k = 192 + v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k] = r[r_idx++];
}
// Put sequence in symbol 3
for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
ssb_grid[SRSRAN_SSB_BW_SUBC * 3 + k] = r[r_idx++];
}
return SRSRAN_SUCCESS;
}
int dmrs_pbch_extract_lse(const srsran_dmrs_pbch_cfg_t* cfg,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
cf_t lse[DMRS_PBCH_NOF_RE])
{
// Calculate index shift
uint32_t v = cfg->N_id % 4;
// Calculate power allocation
float beta = M_SQRT1_2;
if (isnormal(cfg->beta)) {
beta = cfg->beta;
}
// Initialise sequence
uint32_t cinit = dmrs_pbch_cinit(cfg);
srsran_sequence_state_t sequence_state = {};
srsran_sequence_state_init(&sequence_state, cinit);
// Generate sequence
cf_t r[DMRS_PBCH_NOF_RE];
srsran_sequence_state_gen_f(&sequence_state, beta, (float*)r, DMRS_PBCH_NOF_RE * 2);
// r sequence read index
uint32_t r_idx = 0;
// Put sequence in symbol 1
for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 1 + k];
}
// Put sequence in symbol 2, lower section
for (uint32_t k = v; k < 48; k += 4) {
lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k];
}
// Put sequence in symbol 2, upper section
for (uint32_t k = 192 + v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k];
}
// Put sequence in symbol 3
for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 3 + k];
}
// Calculate actual least square estimates
srsran_vec_prod_conj_ccc(lse, r, lse, DMRS_PBCH_NOF_RE);
return SRSRAN_SUCCESS;
}
int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
cf_t ce[SRSRAN_SSB_NOF_RE],
srsran_dmrs_pbch_meas_t* meas)
{
// Validate inputs
if (cfg == NULL || ssb_grid == NULL || ce == NULL || meas == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Extract least square estimates
cf_t lse[DMRS_PBCH_NOF_RE];
if (dmrs_pbch_extract_lse(cfg, ssb_grid, lse) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
float scs_hz = SRSRAN_SUBC_SPACING_NR(cfg->scs);
if (!isnormal(scs_hz)) {
ERROR("Invalid SCS");
return SRSRAN_ERROR;
}
// Compute average delay in microseconds from the symbols 1 and 3 (symbol 2 does not carry PBCH in all the grid)
float avg_delay1_norm = srsran_vec_estimate_frequency(&lse[0], 60) / 4.0f;
float avg_delay3_norm = srsran_vec_estimate_frequency(&lse[84], 60) / 4.0f;
float avg_delay_norm = (avg_delay1_norm + avg_delay3_norm) / 2.0f;
float avg_delay_us = avg_delay_norm / scs_hz;
// Generate a second SSB grid with the corrected average delay
cf_t ssb_grid_corrected[SRSRAN_SSB_NOF_RE];
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
srsran_vec_apply_cfo(&ssb_grid[SRSRAN_SSB_BW_SUBC * l],
avg_delay_norm,
&ssb_grid_corrected[SRSRAN_SSB_BW_SUBC * l],
SRSRAN_SSB_BW_SUBC);
}
// Extract LSE from corrected grid
if (dmrs_pbch_extract_lse(cfg, ssb_grid_corrected, lse) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// Compute correlation of symbols 1 and 3
cf_t corr1 = srsran_vec_acc_cc(&lse[0], 60) / 60.0f;
cf_t corr3 = srsran_vec_acc_cc(&lse[84], 60) / 60.0f;
// Estimate CFO from correlation
float distance_s = srsran_symbol_distance_s(1, 3, cfg->scs);
float cfo_hz = 0.0f;
if (isnormal(distance_s)) {
cfo_hz = cargf(corr1 * conjf(corr3)) / (2.0f * (float)M_PI * distance_s);
}
// Estimate wideband gain at symbol 0
cf_t wideband_gain = (srsran_vec_acc_cc(lse, DMRS_PBCH_NOF_RE) / DMRS_PBCH_NOF_RE) *
cexpf(I * 2.0f * M_PI * srsran_symbol_offset_s(2, cfg->scs) * cfo_hz);
// Compute RSRP from correlation
float rsrp = SRSRAN_CSQABS((corr1 + corr3) / 2.0f);
// Compute EPRE
float epre = srsran_vec_avg_power_cf(lse, DMRS_PBCH_NOF_RE);
// Write measurements
meas->corr = rsrp / epre;
meas->epre = epre;
meas->rsrp = rsrp;
meas->cfo_hz = cfo_hz;
meas->avg_delay_us = avg_delay_us;
// Compute channel estimates
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
float t_s = srsran_symbol_offset_s(l, cfg->scs);
cf_t symbol_wideband_gain = cexpf(-I * 2.0f * M_PI * cfo_hz * t_s) * wideband_gain;
srsran_vec_gen_sine(symbol_wideband_gain, -avg_delay_norm, &ce[l * SRSRAN_SSB_BW_SUBC], SRSRAN_SSB_BW_SUBC);
}
return SRSRAN_SUCCESS;
}

@ -560,16 +560,13 @@ void srsran_sequence_apply_s(const int16_t* in, int16_t* out, uint32_t length, u
}
}
void srsran_sequence_apply_c(const int8_t* in, int8_t* out, uint32_t length, uint32_t seed)
void srsran_sequence_state_apply_c(srsran_sequence_state_t* s, const int8_t* in, int8_t* out, uint32_t length)
{
uint32_t x1 = sequence_x1_init; // X1 initial state is fix
uint32_t x2 = sequence_get_x2_init(seed); // loads x2 initial state
uint32_t i = 0;
if (length >= SEQUENCE_PAR_BITS) {
for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) {
uint32_t c = (uint32_t)(x1 ^ x2);
uint32_t c = (uint32_t)(s->x1 ^ s->x2);
uint32_t j = 0;
#ifdef LV_HAVE_SSE
@ -606,30 +603,34 @@ void srsran_sequence_apply_c(const int8_t* in, int8_t* out, uint32_t length, uin
}
// Step sequences
x1 = sequence_gen_LTE_pr_memless_step_par_x1(x1);
x2 = sequence_gen_LTE_pr_memless_step_par_x2(x2);
s->x1 = sequence_gen_LTE_pr_memless_step_par_x1(s->x1);
s->x2 = sequence_gen_LTE_pr_memless_step_par_x2(s->x2);
}
}
for (; i < length; i++) {
out[i] = in[i] * (((x1 ^ x2) & 1U) ? -1 : +1);
out[i] = in[i] * (((s->x1 ^ s->x2) & 1U) ? -1 : +1);
// Step sequences
x1 = sequence_gen_LTE_pr_memless_step_x1(x1);
x2 = sequence_gen_LTE_pr_memless_step_x2(x2);
s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1);
s->x2 = sequence_gen_LTE_pr_memless_step_x2(s->x2);
}
}
void srsran_sequence_apply_bit(const uint8_t* in, uint8_t* out, uint32_t length, uint32_t seed)
void srsran_sequence_apply_c(const int8_t* in, int8_t* out, uint32_t length, uint32_t seed)
{
uint32_t x1 = sequence_x1_init; // X1 initial state is fix
uint32_t x2 = sequence_get_x2_init(seed); // loads x2 initial state
srsran_sequence_state_t sequence_state;
srsran_sequence_state_init(&sequence_state, seed);
srsran_sequence_state_apply_c(&sequence_state, in, out, length);
}
void srsran_sequence_state_apply_bit(srsran_sequence_state_t* s, const uint8_t* in, uint8_t* out, uint32_t length)
{
uint32_t i = 0;
if (length >= SEQUENCE_PAR_BITS) {
for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) {
uint32_t c = (uint32_t)(x1 ^ x2);
uint32_t c = (uint32_t)(s->x1 ^ s->x2);
uint32_t j = 0;
#ifdef LV_HAVE_SSE
@ -665,20 +666,27 @@ void srsran_sequence_apply_bit(const uint8_t* in, uint8_t* out, uint32_t length,
}
// Step sequences
x1 = sequence_gen_LTE_pr_memless_step_par_x1(x1);
x2 = sequence_gen_LTE_pr_memless_step_par_x2(x2);
s->x1 = sequence_gen_LTE_pr_memless_step_par_x1(s->x1);
s->x2 = sequence_gen_LTE_pr_memless_step_par_x2(s->x2);
}
}
for (; i < length; i++) {
out[i] = in[i] ^ ((x1 ^ x2) & 1U);
out[i] = in[i] ^ ((s->x1 ^ s->x2) & 1U);
// Step sequences
x1 = sequence_gen_LTE_pr_memless_step_x1(x1);
x2 = sequence_gen_LTE_pr_memless_step_x2(x2);
s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1);
s->x2 = sequence_gen_LTE_pr_memless_step_x2(s->x2);
}
}
void srsran_sequence_apply_bit(const uint8_t* in, uint8_t* out, uint32_t length, uint32_t seed)
{
srsran_sequence_state_t sequence_state = {};
srsran_sequence_state_init(&sequence_state, seed);
srsran_sequence_state_apply_bit(&sequence_state, in, out, length);
}
void srsran_sequence_apply_packed(const uint8_t* in, uint8_t* out, uint32_t length, uint32_t seed)
{
uint32_t x1 = sequence_x1_init; // X1 initial state is fix
@ -759,7 +767,7 @@ void srsran_sequence_apply_packed(const uint8_t* in, uint8_t* out, uint32_t leng
out[i] = in[i] ^ reverse_lut[buffer & ((1U << rem8) - 1U) & 255U];
}
#else // SEQUENCE_PAR_BITS % 8 == 0
#else // SEQUENCE_PAR_BITS % 8 == 0
while (i < (length / 8 - (SEQUENCE_PAR_BITS - 1) / 8)) {
uint32_t c = (uint32_t)(x1 ^ x2);

@ -365,11 +365,11 @@ void srsran_enb_dl_put_phich(srsran_enb_dl_t* q, srsran_phich_grant_t* grant, bo
srsran_phich_encode(&q->phich, &q->dl_sf, resource, ack, q->sf_symbols);
}
bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, uint32_t ncce)
bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, const srsran_dci_location_t* loc)
{
if (SRSRAN_CFI_ISVALID(q->dl_sf.cfi)) {
return srsran_location_find_ncce(
q->common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], q->nof_common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], ncce);
return srsran_location_find_location(
q->common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], q->nof_common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], loc);
} else {
return false;
}

@ -34,11 +34,11 @@
#define DEB 0
#define TB_ITER 3
#define TB_ITER 5
#define DEFAULT_GAIN 100
#define DEFAULT_GAIN_16 1000
#define DEFAULT_GAIN_16 500
#define VITERBI_16
#ifndef LV_HAVE_AVX2
@ -558,11 +558,10 @@ int srsran_viterbi_decode_f(srsran_viterbi_t* q, float* symbols, uint8_t* data,
len = 3 * (frame_length + q->K - 1);
}
if (!q->decode_f) {
float max = 1e-9;
for (int i = 0; i < len; i++) {
if (fabs(symbols[i]) > max) {
max = fabs(symbols[i]);
}
float max = 1e-9;
uint32_t max_i = srsran_vec_max_abs_fi(symbols, len);
if (max_i < len && isnormal(symbols[max_i])) {
max = fabsf(symbols[max_i]);
}
#ifdef VITERBI_16
srsran_vec_quant_fus(symbols, q->symbols_us, q->gain_quant / max, 32767.5, 65535, len);

@ -43,7 +43,6 @@ union branchtab27 {
} Branchtab37_sse2[3];
int firstGo_16bit;
/* State info for instance of Viterbi decoder */
struct v37 {
metric_t metrics1; /* path metric buffer 1 */
@ -85,7 +84,6 @@ int init_viterbi37_avx2_16bit(void* p, int starting_state)
vp->metrics1.c[i] = 63;
clear_v37_avx2_16bit(vp);
firstGo_16bit = 1;
vp->old_metrics = &vp->metrics1;
vp->new_metrics = &vp->metrics2;
vp->dp = vp->decisions;

@ -60,7 +60,7 @@ endif(HAVE_AVX512)
### Test LDPC libs
function(ldpc_unit_tests)
foreach(i IN LISTS ARGN)
add_nr_test(NAME ${test_name}-LS${i} COMMAND ${test_command} -l${i}
add_nr_advanced_test(NAME ${test_name}-LS${i} COMMAND ${test_command} -l${i}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
)
endforeach()
@ -179,36 +179,36 @@ function(ldpc_rm_unit_tests)
math(EXPR tmpN "${N} - (${N} % ${Div})")
math(EXPR E "${Ordval}*(${tmpN})/${Div}") #twice the rate
add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N}
add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
)
math(EXPR M "${N} / 2")
# Half size buffer
add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M}
add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
)
math(EXPR Div "2*${Ordval}")
math(EXPR tmpN "${N} - (${N} % ${Div})")
math(EXPR E "${Ordval}*(${tmpN})/${Div}") #twice the rate
add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N}
add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
)
math(EXPR M "${N}/ 2")
# Half size buffer
add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M}
add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
)
math(EXPR Div "${Ordval}")
math(EXPR tmpN "2*${N} - (2*${N} % ${Div})") #Half the rate
math(EXPR E "${Ordval}*(${tmpN})/${Div}")
add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N}
add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
)
math(EXPR M "${N}/ 2")
# Half size buffer
add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M}
add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
)
endforeach()

@ -389,7 +389,6 @@ int srsran_polar_rm_tx_init(srsran_polar_rm_t* p)
int srsran_polar_rm_rx_init_f(srsran_polar_rm_t* p)
{
if (p == NULL) {
return -1;
}
@ -414,7 +413,6 @@ int srsran_polar_rm_rx_init_f(srsran_polar_rm_t* p)
int srsran_polar_rm_rx_init_s(srsran_polar_rm_t* p)
{
if (p == NULL) {
return -1;
}
@ -439,7 +437,6 @@ int srsran_polar_rm_rx_init_s(srsran_polar_rm_t* p)
int srsran_polar_rm_rx_init_c(srsran_polar_rm_t* p)
{
if (p == NULL) {
return -1;
}
@ -464,41 +461,74 @@ int srsran_polar_rm_rx_init_c(srsran_polar_rm_t* p)
void srsran_polar_rm_tx_free(srsran_polar_rm_t* q)
{
if (q != NULL) {
struct pRM_tx* qq = q->ptr;
if (q == NULL) {
return;
}
struct pRM_tx* qq = q->ptr;
if (qq == NULL) {
return;
}
if (qq->y_e) {
free(qq->y_e);
free(qq);
}
free(qq);
}
void srsran_polar_rm_rx_free_f(srsran_polar_rm_t* q)
{
if (q != NULL) {
struct pRM_rx_f* qq = q->ptr;
if (q == NULL) {
return;
}
struct pRM_rx_f* qq = q->ptr;
if (qq == NULL) {
return;
}
if (qq->y_e) {
free(qq->y_e);
// free(qq->indices);
free(qq);
}
free(qq);
}
void srsran_polar_rm_rx_free_s(srsran_polar_rm_t* q)
{
if (q != NULL) {
struct pRM_rx_s* qq = q->ptr;
if (q == NULL) {
return;
}
struct pRM_rx_s* qq = q->ptr;
if (qq == NULL) {
return;
}
if (qq->y_e) {
free(qq->y_e);
// free(qq->indices);
free(qq);
}
free(qq);
}
void srsran_polar_rm_rx_free_c(srsran_polar_rm_t* q)
{
if (q != NULL) {
struct pRM_rx_c* qq = q->ptr;
if (q == NULL) {
return;
}
struct pRM_rx_c* qq = q->ptr;
if (qq == NULL) {
return;
}
if (qq->y_e) {
free(qq->y_e);
// free(qq->indices);
free(qq);
}
free(qq);
}
int srsran_polar_rm_tx(srsran_polar_rm_t* q,
@ -538,7 +568,6 @@ int srsran_polar_rm_rx_f(srsran_polar_rm_t* q,
const uint32_t K,
const uint8_t ibil)
{
struct pRM_rx_f* pp = q->ptr;
float* y = NULL;
float* e = pp->e; // length E
@ -566,7 +595,6 @@ int srsran_polar_rm_rx_s(srsran_polar_rm_t* q,
const uint32_t K,
const uint8_t ibil)
{
struct pRM_rx_s* pp = q->ptr;
int16_t* y = NULL;
int16_t* e = pp->e;
@ -594,7 +622,6 @@ int srsran_polar_rm_rx_c(srsran_polar_rm_t* q,
const uint32_t K,
const uint8_t ibil)
{
struct pRM_rx_c* pp = q->ptr;
int8_t* y = NULL;
int8_t* e = pp->e;

@ -40,7 +40,7 @@ function(polar_tests_lite)
list(GET listE ${num} eval)
list(GET listK ${num} kval)
list(GET listI ${num} ival)
add_nr_test(NAME ${test_name}-s${S}-n${nval}-e${eval}-k${kval}-i${ival}
add_nr_advanced_test(NAME ${test_name}-s${S}-n${nval}-e${eval}-k${kval}-i${ival}
COMMAND ${test_command} -s${S} -n${nval} -e${eval} -k${kval} -i${ival}
)
endforeach()
@ -54,7 +54,7 @@ function(polar_tests)
foreach(Kval RANGE 36 164 32)
math(EXPR Emin "${Kval} + 1")
foreach(Eval RANGE ${Emin} 8192 128)
add_nr_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i0
add_nr_advanced_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i0
COMMAND ${test_command} -s${S} -k${Kval} -e${Eval} -n${nval} -i0
)
endforeach()
@ -66,7 +66,7 @@ function(polar_tests)
foreach(Kval RANGE 18 25)
math(EXPR Emin "${Kval} + 3 + 1")
foreach(Eval RANGE ${Emin} 8192 128)
add_nr_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1
add_nr_advanced_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1
COMMAND ${test_command} -s${S} -k${Kval} -e${Eval} -n${nval} -i1
)
endforeach()
@ -75,7 +75,7 @@ function(polar_tests)
foreach(Kval RANGE 32 1023 32)
math(EXPR Emin "${Kval} + 1")
foreach(Eval RANGE ${Emin} 8192 128)
add_nr_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1
add_nr_advanced_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1
COMMAND ${test_command} -s${S} -k${Kval} -e${Eval} -n${nval} -i1
)
endforeach()

@ -1389,10 +1389,12 @@ bool srsran_location_find(const srsran_dci_location_t* locations, uint32_t nof_l
return false;
}
bool srsran_location_find_ncce(const srsran_dci_location_t* locations, uint32_t nof_locations, uint32_t ncce)
bool srsran_location_find_location(const srsran_dci_location_t* locations,
uint32_t nof_locations,
const srsran_dci_location_t* location)
{
for (uint32_t i = 0; i < nof_locations; i++) {
if (locations[i].ncce == ncce) {
if (locations[i].ncce == location->ncce && locations[i].L == location->L) {
return true;
}
}

@ -49,15 +49,11 @@ bool srsran_pbch_exists(int nframe, int nslot)
return (!(nframe % 5) && nslot == 1);
}
cf_t* offset_original;
int srsran_pbch_cp(cf_t* input, cf_t* output, srsran_cell_t cell, bool put)
{
int i;
cf_t* ptr;
offset_original = input;
if (put) {
ptr = input;
output += cell.nof_prb * SRSRAN_NRE / 2 - 36;

@ -0,0 +1,677 @@
/**
*
* \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/phy/phch/pbch_nr.h"
#include "srsran/phy/common/sequence.h"
#include "srsran/phy/fec/polar/polar_chanalloc.h"
#include "srsran/phy/fec/polar/polar_interleaver.h"
#include "srsran/phy/mimo/precoding.h"
#include "srsran/phy/modem/demod_soft.h"
#include "srsran/phy/modem/mod.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/simd.h"
#include "srsran/phy/utils/vector.h"
#define PBCH_NR_DEBUG_TX(...) DEBUG("PBCH-NR Tx: " __VA_ARGS__)
#define PBCH_NR_DEBUG_RX(...) DEBUG("PBCH-NR Rx: " __VA_ARGS__)
/*
* CRC Parameters
*/
#define PBCH_NR_CRC SRSRAN_LTE_CRC24C
#define PBCH_NR_CRC_LEN 24
/*
* Polar code N_max
*/
#define PBCH_NR_POLAR_N_MAX 9U
/*
* Polar rate matching I_BIL
*/
#define PBCH_NR_POLAR_RM_IBIL 0
/*
* Number of generated payload bits, called A
*/
#define PBCH_NR_A (SRSRAN_PBCH_NR_PAYLOAD_SZ + 8)
/*
* Number of payload bits plus CRC
*/
#define PBCH_NR_K (PBCH_NR_A + PBCH_NR_CRC_LEN)
/*
* Number of Polar encoded bits
*/
#define PBCH_NR_N (1U << PBCH_NR_POLAR_N_MAX)
/*
* Number of RM bits
*/
#define PBCH_NR_E (864)
/*
* Number of symbols
*/
#define PBCH_NR_M (PBCH_NR_E / 2)
static int pbch_nr_init_encoder(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args)
{
// Skip encoder init if not requested
if (!args->enable_encode) {
return SRSRAN_SUCCESS;
}
srsran_polar_encoder_type_t encoder_type = SRSRAN_POLAR_ENCODER_PIPELINED;
#ifdef LV_HAVE_AVX2
if (!args->disable_simd) {
encoder_type = SRSRAN_POLAR_ENCODER_AVX2;
}
#endif /* LV_HAVE_AVX2 */
if (srsran_polar_encoder_init(&q->polar_encoder, encoder_type, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) {
ERROR("Error initiating polar encoder");
return SRSRAN_ERROR;
}
if (srsran_polar_rm_tx_init(&q->polar_rm_tx) < SRSRAN_SUCCESS) {
ERROR("Error initiating polar RM");
return SRSRAN_ERROR;
}
if (srsran_modem_table_lte(&q->qpsk, SRSRAN_MOD_QPSK) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
static int pbch_nr_init_decoder(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args)
{
// Skip decoder init if not requested
if (!args->enable_decode) {
return SRSRAN_SUCCESS;
}
srsran_polar_decoder_type_t decoder_type = SRSRAN_POLAR_DECODER_SSC_C;
#ifdef LV_HAVE_AVX2
if (!args->disable_simd) {
decoder_type = SRSRAN_POLAR_DECODER_SSC_C_AVX2;
}
#endif /* LV_HAVE_AVX2 */
if (srsran_polar_decoder_init(&q->polar_decoder, decoder_type, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) {
ERROR("Error initiating polar decoder");
return SRSRAN_ERROR;
}
if (srsran_polar_rm_rx_init_c(&q->polar_rm_rx) < SRSRAN_SUCCESS) {
ERROR("Error initiating polar RM");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int srsran_pbch_nr_init(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args)
{
if (q == NULL || args == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!args->enable_encode && !args->enable_decode) {
ERROR("Encoder and decoder are disabled, at least one of them shall be active");
return SRSRAN_ERROR;
}
if (pbch_nr_init_encoder(q, args) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
if (pbch_nr_init_decoder(q, args) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
if (srsran_crc_init(&q->crc, PBCH_NR_CRC, PBCH_NR_CRC_LEN) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
if (srsran_polar_code_init(&q->code) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
if (srsran_polar_code_get(&q->code, PBCH_NR_K, PBCH_NR_E, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) {
ERROR("Error Getting polar code");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
void srsran_pbch_nr_free(srsran_pbch_nr_t* q)
{
if (q == NULL) {
return;
}
srsran_polar_encoder_free(&q->polar_encoder);
srsran_polar_decoder_free(&q->polar_decoder);
srsran_polar_rm_rx_free_c(&q->polar_rm_rx);
srsran_polar_rm_tx_free(&q->polar_rm_tx);
srsran_polar_code_free(&q->code);
srsran_modem_table_free(&q->qpsk);
SRSRAN_MEM_ZERO(q, srsran_pbch_nr_t, 1);
}
/*
* Implements TS 38.212 Table 7.1.1-1: Value of PBCH payload interleaver pattern G ( j )
*/
static const uint32_t G[PBCH_NR_A] = {16, 23, 18, 17, 8, 30, 10, 6, 24, 7, 0, 5, 3, 2, 1, 4,
9, 11, 12, 13, 14, 15, 19, 20, 21, 22, 25, 26, 27, 28, 29, 31};
static void
pbch_nr_pbch_msg_pack(const srsran_pbch_nr_cfg_t* cfg, const srsran_pbch_msg_nr_t* msg, uint8_t a[PBCH_NR_A])
{
// Extract actual payload size
uint32_t A_hat = SRSRAN_PBCH_NR_PAYLOAD_SZ;
// Put SFN in a_hat[A_hat] to a_hat[A_hat + 3]
uint32_t j_sfn = 0;
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 3U) & 1U); // 4th LSB of SFN
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 2U) & 1U); // 3th LSB of SFN
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 1U) & 1U); // 2th LSB of SFN
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 0U) & 1U); // 1th LSB of SFN
// Put HRF in a_hat[A_hat + 4]
a[G[10]] = (msg->hrf ? 1 : 0);
// Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7]
if (cfg->Lmax == 64) {
a[G[11]] = (uint8_t)((msg->ssb_idx >> 5U) & 1U); // 6th bit of SSB index
a[G[12]] = (uint8_t)((msg->ssb_idx >> 4U) & 1U); // 5th bit of SSB index
a[G[13]] = (uint8_t)((msg->ssb_idx >> 3U) & 1U); // 4th bit of SSB index
} else {
a[G[11]] = (uint8_t)msg->k_ssb_msb; // 6th bit of SSB index
a[G[12]] = 0; // Reserved
a[G[13]] = 0; // Reserved
}
// Put actual payload
uint32_t j_other = 14;
for (uint32_t i = 0; i < A_hat; i++) {
if (i > 0 && i < 7) {
a[G[j_sfn++]] = msg->payload[i];
} else {
a[G[j_other++]] = msg->payload[i];
}
}
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
PBCH_NR_DEBUG_TX("Packed PBCH bits: ");
srsran_vec_fprint_byte(stdout, a, PBCH_NR_A);
}
}
static void
pbch_nr_pbch_msg_unpack(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], srsran_pbch_msg_nr_t* msg)
{
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
PBCH_NR_DEBUG_RX("Packed PBCH bits: ");
srsran_vec_fprint_byte(stdout, a, PBCH_NR_A);
}
// Extract actual payload size
uint32_t A_hat = SRSRAN_PBCH_NR_PAYLOAD_SZ;
// Put SFN in a_hat[A_hat] to a_hat[A_hat + 3]
uint32_t j_sfn = 0;
msg->sfn_4lsb = 0;
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 3U); // 4th LSB of SFN
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 2U); // 3th LSB of SFN
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 1U); // 2th LSB of SFN
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 0U); // 1th LSB of SFN
// Put HRF in a_hat[A_hat + 4]
msg->hrf = (a[G[10]] == 1);
// Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7]
if (cfg->Lmax == 64) {
msg->ssb_idx = msg->ssb_idx & 0b111;
msg->ssb_idx |= (uint8_t)(a[G[11]] << 5U); // 6th bit of SSB index
msg->ssb_idx |= (uint8_t)(a[G[12]] << 4U); // 5th bit of SSB index
msg->ssb_idx |= (uint8_t)(a[G[13]] << 3U); // 4th bit of SSB index
} else {
msg->k_ssb_msb = a[G[11]];
}
// Put actual payload
uint32_t j_other = 14;
for (uint32_t i = 0; i < A_hat; i++) {
if (i > 0 && i < 7) {
msg->payload[i] = a[G[j_sfn++]];
} else {
msg->payload[i] = a[G[j_other++]];
}
}
}
static void pbch_nr_scramble(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], uint8_t a_prime[PBCH_NR_A])
{
uint32_t i = 0;
uint32_t j = 0;
// Initialise sequence
srsran_sequence_state_t sequence_state = {};
srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id));
// Select value M
uint32_t M = PBCH_NR_A - 3;
if (cfg->Lmax == 64) {
M = PBCH_NR_A - 6;
}
// Select value v
uint32_t v = 2 * a[G[1]] + a[G[2]];
// Advance sequence
srsran_sequence_state_advance(&sequence_state, M * v);
// Generate actual sequence
uint8_t c[PBCH_NR_A] = {};
srsran_sequence_state_apply_bit(&sequence_state, c, c, PBCH_NR_A);
while (i < PBCH_NR_A) {
// a i corresponds to any one of the bits belonging to the SS/PBCH block index, the half frame index, and 2 nd and 3
// rd least significant bits of the system frame number
uint8_t s_i = c[j];
// else
if (i == G[11] || i == G[12] || i == G[13] || i == G[10] || i == G[1] || i == G[2]) {
s_i = 0;
} else {
j++;
}
a_prime[i] = a[i] ^ s_i;
i++;
}
}
static int pbch_nr_polar_encode(srsran_pbch_nr_t* q, const uint8_t c[PBCH_NR_K], uint8_t d[PBCH_NR_N])
{
// Interleave
uint8_t c_prime[SRSRAN_POLAR_INTERLEAVER_K_MAX_IL];
srsran_polar_interleaver_run_u8(c, c_prime, PBCH_NR_K, true);
// Allocate channel
uint8_t allocated[PBCH_NR_N];
srsran_polar_chanalloc_tx(c_prime, allocated, q->code.N, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set);
// Encode bits
if (srsran_polar_encoder_encode(&q->polar_encoder, allocated, d, q->code.n) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
PBCH_NR_DEBUG_TX("Allocated: ");
srsran_vec_fprint_byte(stdout, allocated, PBCH_NR_N);
}
return SRSRAN_SUCCESS;
}
static int pbch_nr_polar_decode(srsran_pbch_nr_t* q, const int8_t d[PBCH_NR_N], uint8_t c[PBCH_NR_K])
{
// Decode bits
uint8_t allocated[PBCH_NR_N];
if (srsran_polar_decoder_decode_c(&q->polar_decoder, d, allocated, q->code.n, q->code.F_set, q->code.F_set_size) <
SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
PBCH_NR_DEBUG_RX("Allocated: ");
srsran_vec_fprint_byte(stdout, allocated, PBCH_NR_N);
}
// Allocate channel
uint8_t c_prime[SRSRAN_POLAR_INTERLEAVER_K_MAX_IL];
srsran_polar_chanalloc_rx(allocated, c_prime, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set);
// Interleave
srsran_polar_interleaver_run_u8(c_prime, c, PBCH_NR_K, false);
return SRSRAN_SUCCESS;
}
static int pbch_nr_polar_rm_tx(srsran_pbch_nr_t* q, const uint8_t d[PBCH_NR_N], uint8_t o[PBCH_NR_E])
{
if (srsran_polar_rm_tx(&q->polar_rm_tx, d, o, q->code.n, PBCH_NR_E, PBCH_NR_K, PBCH_NR_POLAR_RM_IBIL) <
SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
PBCH_NR_DEBUG_TX("d: ");
srsran_vec_fprint_byte(stdout, d, PBCH_NR_N);
}
return SRSRAN_SUCCESS;
}
static int pbch_nr_polar_rm_rx(srsran_pbch_nr_t* q, const int8_t llr[PBCH_NR_E], int8_t d[PBCH_NR_N])
{
if (srsran_polar_rm_rx_c(&q->polar_rm_rx, llr, d, PBCH_NR_E, q->code.n, PBCH_NR_K, PBCH_NR_POLAR_RM_IBIL) <
SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// Negate all LLR
for (uint32_t i = 0; i < PBCH_NR_N; i++) {
d[i] *= -1;
}
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
PBCH_NR_DEBUG_RX("d: ");
srsran_vec_fprint_bs(stdout, d, PBCH_NR_N);
}
return SRSRAN_SUCCESS;
}
static void pbch_nr_scramble_tx(const srsran_pbch_nr_cfg_t* cfg,
uint32_t ssb_idx,
const uint8_t b[PBCH_NR_E],
uint8_t b_hat[PBCH_NR_E])
{
// Initialise sequence
srsran_sequence_state_t sequence_state = {};
srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id));
// Select value M
uint32_t M_bit = PBCH_NR_E;
// Select value v
uint32_t v = (ssb_idx & 0x7U);
if (cfg->Lmax == 4) {
v = ssb_idx & 0x3U;
}
// Advance sequence
srsran_sequence_state_advance(&sequence_state, v * M_bit);
// Apply sequence
srsran_sequence_state_apply_bit(&sequence_state, b, b_hat, PBCH_NR_E);
}
static void pbch_nr_scramble_rx(const srsran_pbch_nr_cfg_t* cfg,
uint32_t ssb_idx,
const int8_t b_hat[PBCH_NR_E],
int8_t b[PBCH_NR_E])
{
// Initialise sequence
srsran_sequence_state_t sequence_state = {};
srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id));
// Select value M
uint32_t M_bit = PBCH_NR_E;
// Select value v
uint32_t v = (ssb_idx & 0x7U);
if (cfg->Lmax == 4) {
v = ssb_idx & 0x3U;
}
// Advance sequence
srsran_sequence_state_advance(&sequence_state, v * M_bit);
// Apply sequence
srsran_sequence_state_apply_c(&sequence_state, b_hat, b, PBCH_NR_E);
}
static void
pbch_nr_mapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t symbols[PBCH_NR_M], cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
{
uint32_t count = 0;
// PBCH DMRS shift
uint32_t v = cfg->N_id % 4;
// Symbol 1
for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) {
// Skip DMRS
if (k % 4 == v) {
continue;
}
ssb_grid[1 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++];
}
// Symbol 2
for (uint32_t k = 0; k < 48; k++) {
// Skip DMRS
if (k % 4 == v) {
continue;
}
ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++];
}
for (uint32_t k = 192; k < SRSRAN_SSB_BW_SUBC; k++) {
// Skip DMRS
if (k % 4 == v) {
continue;
}
ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++];
}
// Symbol 3
for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) {
// Skip DMRS
if (k % 4 == v) {
continue;
}
ssb_grid[3 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++];
}
// if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
// PBCH_NR_DEBUG_TX("Symbols: ");
// srsran_vec_fprint_c(stdout, symbols, PBCH_NR_M);
// }
}
static void
pbch_nr_demapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], cf_t symbols[PBCH_NR_M])
{
uint32_t count = 0;
// PBCH DMRS shift
uint32_t v = cfg->N_id % 4;
// Symbol 1
for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) {
// Skip DMRS
if (k % 4 == v) {
continue;
}
symbols[count++] = ssb_grid[1 * SRSRAN_SSB_BW_SUBC + k];
}
// Symbol 2
for (uint32_t k = 0; k < 48; k++) {
// Skip DMRS
if (k % 4 == v) {
continue;
}
symbols[count++] = ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k];
}
for (uint32_t k = 192; k < SRSRAN_SSB_BW_SUBC; k++) {
// Skip DMRS
if (k % 4 == v) {
continue;
}
symbols[count++] = ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k];
}
// Symbol 3
for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) {
// Skip DMRS
if (k % 4 == v) {
continue;
}
symbols[count++] = ssb_grid[3 * SRSRAN_SSB_BW_SUBC + k];
}
// if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
// PBCH_NR_DEBUG_RX("Symbols: ");
// srsran_vec_fprint_c(stdout, symbols, PBCH_NR_M);
// }
}
int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
const srsran_pbch_nr_cfg_t* cfg,
const srsran_pbch_msg_nr_t* msg,
cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
{
if (q == NULL || cfg == NULL || msg == NULL || ssb_grid == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
// TS 38.212 7.1 Broadcast channel
// 7.1.1 PBCH payload generation
uint8_t a[PBCH_NR_A];
pbch_nr_pbch_msg_pack(cfg, msg, a);
// 7.1.2 Scrambling
uint8_t b[PBCH_NR_K];
pbch_nr_scramble(cfg, a, b);
// 7.1.3 Transport block CRC attachment
uint32_t checksum = srsran_crc_attach(&q->crc, b, PBCH_NR_A);
PBCH_NR_DEBUG_TX("checksum=%06x", checksum);
// 7.1.4 Channel coding
uint8_t d[PBCH_NR_N];
if (pbch_nr_polar_encode(q, b, d) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// 7.1.5 Rate matching
uint8_t f[PBCH_NR_E];
if (pbch_nr_polar_rm_tx(q, d, f) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// TS 38.211 7.3.3 Physical broadcast channel
// 7.3.3.1 Scrambling
pbch_nr_scramble_tx(cfg, msg->ssb_idx, f, f);
// 7.3.3.2 Modulation
cf_t symbols[PBCH_NR_M];
srsran_mod_modulate(&q->qpsk, f, symbols, PBCH_NR_E);
// 7.3.3.3 Mapping to physical resources
// 7.4.3.1.3 Mapping of PBCH and DM-RS within an SS/PBCH block
pbch_nr_mapping(cfg, symbols, ssb_grid);
return SRSRAN_SUCCESS;
}
int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
const srsran_pbch_nr_cfg_t* cfg,
uint32_t ssb_idx,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
const cf_t ce_grid[SRSRAN_SSB_NOF_RE],
srsran_pbch_msg_nr_t* msg)
{
if (q == NULL || cfg == NULL || msg == NULL || ssb_grid == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
// 7.3.3.3 Mapping to physical resources
// 7.4.3.1.3 Mapping of PBCH and DM-RS within an SS/PBCH block
srsran_simd_aligned cf_t symbols[PBCH_NR_M];
pbch_nr_demapping(cfg, ssb_grid, symbols);
srsran_simd_aligned cf_t ce[PBCH_NR_M];
pbch_nr_demapping(cfg, ce_grid, ce);
// Channel equalizer
if (srsran_predecoding_single(symbols, ce, symbols, NULL, PBCH_NR_M, 1.0f, 0.0f) < SRSRAN_SUCCESS) {
ERROR("Error in predecoder");
return SRSRAN_ERROR;
}
// 7.3.3.2 Modulation
int8_t llr[PBCH_NR_E];
srsran_demod_soft_demodulate_b(SRSRAN_MOD_QPSK, symbols, llr, PBCH_NR_M);
// TS 38.211 7.3.3 Physical broadcast channel
// 7.3.3.1 Scrambling
pbch_nr_scramble_rx(cfg, ssb_idx, llr, llr);
// 7.1.5 Rate matching
int8_t d[PBCH_NR_N];
if (pbch_nr_polar_rm_rx(q, llr, d) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// TS 38.212 7.1 Broadcast channel
// 7.1.4 Channel coding
uint8_t b[PBCH_NR_K];
if (pbch_nr_polar_decode(q, d, b) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// 7.1.3 Transport block CRC attachment
msg->crc = srsran_crc_match(&q->crc, b, PBCH_NR_A);
PBCH_NR_DEBUG_RX("crc=%s", msg->crc ? "OK" : "KO");
// 7.1.2 Scrambling
uint8_t a[PBCH_NR_A];
pbch_nr_scramble(cfg, b, a);
// 7.1.1 PBCH payload generation
pbch_nr_pbch_msg_unpack(cfg, a, msg);
return SRSRAN_SUCCESS;
}
uint32_t srsran_pbch_msg_info(const srsran_pbch_msg_nr_t* msg, char* str, uint32_t str_len)
{
if (msg == NULL || str == NULL || str_len == 0) {
return 0;
}
uint32_t len = 0;
len = srsran_print_check(str, str_len, len, "payload=");
len += srsran_vec_sprint_hex(&str[len], str_len - len, (uint8_t*)msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ);
len = srsran_print_check(str,
str_len,
len,
" sfn_lsb=%d ssb_idx=%d k_ssb_msb=%d hrf=%d ",
msg->sfn_4lsb,
msg->ssb_idx,
msg->k_ssb_msb,
msg->hrf);
return len;
}

@ -19,6 +19,7 @@
*
*/
#include <complex.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
@ -45,7 +46,8 @@
float srsran_pdcch_coderate(uint32_t nof_bits, uint32_t l)
{
return (float)(nof_bits + 16) / (4 * PDCCH_FORMAT_NOF_REGS(l));
static const int nof_bits_x_symbol = 2; // QPSK
return (float)(nof_bits + 16) / (nof_bits_x_symbol * PDCCH_FORMAT_NOF_REGS(l));
}
/** Initializes the PDCCH transmitter and receiver */
@ -385,12 +387,14 @@ int srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dc
uint32_t nof_bits = srsran_dci_format_sizeof(&q->cell, sf, dci_cfg, msg->format);
uint32_t e_bits = PDCCH_FORMAT_NOF_BITS(msg->location.L);
// Compute absolute mean of the LLRs
double mean = 0;
for (int i = 0; i < e_bits; i++) {
mean += fabsf(q->llr[msg->location.ncce * 72 + i]);
}
mean /= e_bits;
if (mean > 0.3) {
if (mean > 0.3f) {
ret = srsran_pdcch_dci_decode(q, &q->llr[msg->location.ncce * 72], msg->payload, e_bits, nof_bits, &msg->rnti);
if (ret == SRSRAN_SUCCESS) {
msg->nof_bits = nof_bits;
@ -418,6 +422,27 @@ int srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dc
return ret;
}
float srsran_pdcch_msg_corr(srsran_pdcch_t* q, srsran_dci_msg_t* msg)
{
if (q == NULL || msg == NULL) {
return 0.0f;
}
uint32_t E = PDCCH_FORMAT_NOF_BITS(msg->location.L);
uint32_t nof_llr = E / 2;
// Encode same decoded message and compute correlation
srsran_pdcch_dci_encode(q, msg->payload, q->e, msg->nof_bits, E, msg->rnti);
// Modulate
srsran_mod_modulate(&q->mod, q->e, q->d, E);
// Correlate
cf_t corr = srsran_vec_dot_prod_conj_ccc((cf_t*)&q->llr[msg->location.ncce * 72], q->d, nof_llr);
return cabsf(corr / nof_llr) * (float)M_SQRT1_2;
}
/** Performs PDCCH receiver processing to extract LLR for all control region. LLR bits are stored in srsran_pdcch_t
* object. DCI can be decoded from given locations in successive calls to srsran_pdcch_decode_msg()
*/

@ -491,7 +491,7 @@ int srsran_pdsch_nr_decode(srsran_pdsch_nr_t* q,
srsran_pdsch_res_nr_t* data)
{
// Check input pointers
if (!q || !cfg || !grant || !data || !sf_symbols) {
if (!q || !cfg || !grant || !data || !sf_symbols || !channel) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
@ -537,8 +537,7 @@ int srsran_pdsch_nr_decode(srsran_pdsch_nr_t* q,
// Antenna port demapping
// ... Not implemented
srsran_predecoding_type(
q->x, channel->ce, q->d, NULL, 1, 1, 1, 0, nof_re, SRSRAN_TXSCHEME_PORT0, 1.0f, channel->noise_estimate);
srsran_predecoding_single(q->x[0], channel->ce[0][0], q->d[0], NULL, nof_re, 1.0f, channel->noise_estimate);
// Layer demapping
if (grant->nof_layers > 1) {

@ -210,6 +210,8 @@ int srsran_pssch_init(srsran_pssch_t* q,
ERROR("Error allocating memory");
return SRSRAN_ERROR;
}
srsran_vec_cf_zero(q->scfdma_symbols, q->nof_data_symbols * SRSRAN_NRE * SRSRAN_MAX_PRB);
if (srsran_dft_precoding_init(&q->dft_precoder, SRSRAN_MAX_PRB, true)) {
ERROR("Error DFT precoder init");
return SRSRAN_ERROR;

@ -918,10 +918,10 @@ int srsran_pusch_nr_decode(srsran_pusch_nr_t* q,
const srsran_sch_grant_nr_t* grant,
srsran_chest_dl_res_t* channel,
cf_t* sf_symbols[SRSRAN_MAX_PORTS],
srsran_pusch_res_nr_t* data)
srsran_pusch_res_nr_t* data[SRSRAN_MAX_TB])
{
// Check input pointers
if (!q || !cfg || !grant || !data || !sf_symbols) {
if (!q || !cfg || !grant || !data || !sf_symbols || !channel) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
@ -978,8 +978,7 @@ int srsran_pusch_nr_decode(srsran_pusch_nr_t* q,
// Antenna port demapping
// ... Not implemented
srsran_predecoding_type(
q->x, channel->ce, q->d, NULL, 1, 1, 1, 0, nof_re, SRSRAN_TXSCHEME_PORT0, 1.0f, channel->noise_estimate);
srsran_predecoding_single(q->x[0], channel->ce[0][0], q->d[0], NULL, nof_re, 1.0f, channel->noise_estimate);
// Layer demapping
if (grant->nof_layers > 1) {
@ -988,7 +987,7 @@ int srsran_pusch_nr_decode(srsran_pusch_nr_t* q,
// SCH decode
for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) {
if (pusch_nr_decode_codeword(q, cfg, &grant->tb[tb], data, grant->rnti) < SRSRAN_SUCCESS) {
if (pusch_nr_decode_codeword(q, cfg, &grant->tb[tb], data[0], grant->rnti) < SRSRAN_SUCCESS) {
ERROR("Error encoding TB %d", tb);
return SRSRAN_ERROR;
}

@ -550,7 +550,17 @@ static int decode_tb(srsran_sch_t* q,
par_tx = ((uint32_t)data[cb_segm->tbs / 8 + 0]) << 16 | ((uint32_t)data[cb_segm->tbs / 8 + 1]) << 8 |
((uint32_t)data[cb_segm->tbs / 8 + 2]);
if (par_rx == par_tx && par_rx) {
// Check if all the bytes are zeros
bool all_zeros = true;
for (uint32_t i = 0; i < cb_segm->tbs / 8 && all_zeros; i++) {
all_zeros = (data[i] == 0);
}
if (all_zeros) {
INFO("Error in TB decode: it is all zeros!");
return SRSRAN_ERROR;
}
if (par_rx == par_tx) {
INFO("TB decoded OK");
return SRSRAN_SUCCESS;
} else {

@ -210,19 +210,20 @@ add_lte_test(phich_test_104 phich_test -p 4 -n 10 -e -l -g 1/2)
add_executable(pdcch_test pdcch_test.c)
target_link_libraries(pdcch_test srsran_phy)
add_lte_test(pdcch_test_6 pdcch_test -n 6)
add_lte_test(pdcch_test_15 pdcch_test -n 15)
add_lte_test(pdcch_test_25 pdcch_test -n 25)
add_lte_test(pdcch_test_50 pdcch_test -n 50)
add_lte_test(pdcch_test_75 pdcch_test -n 75)
add_lte_test(pdcch_test_100 pdcch_test -n 100)
add_lte_test(pdcch_test_6_mimo pdcch_test -n 6 -p 2)
add_lte_test(pdcch_test_15_mimo pdcch_test -n 15 -p 2)
add_lte_test(pdcch_test_25_mimo pdcch_test -n 25 -p 2)
add_lte_test(pdcch_test_50_mimo pdcch_test -n 50 -p 2)
add_lte_test(pdcch_test_75_mimo pdcch_test -n 75 -p 2)
add_lte_test(pdcch_test_100_mimo pdcch_test -n 100 -p 2)
#add_lte_test(pdcch_test_crosscarrier pdcch_test -x)
foreach (nof_prb 6 15 25 50 75 100)
foreach (nof_ports 1 2)
foreach (cfi 1 2 3)
foreach (snr auto 15 300)
set(pdcch_test_args "")
set(pdcch_test_args -n ${nof_prb} -p ${nof_ports} -f ${cfi} -S ${snr} -R 1 -F)
string(REGEX REPLACE "\ " "" test_name_args ${pdcch_test_args})
add_lte_test(pdcch_test${test_name_args} pdcch_test ${pdcch_test_args})
endforeach ()
endforeach ()
endforeach ()
endforeach ()
########################################################################
# PDSCH TEST
@ -465,11 +466,7 @@ add_lte_test(npdsch_npdcch_dci_formatN1_test npdsch_npdcch_file_test -c 0 -s 1 -
add_executable(pusch_test pusch_test.c)
target_link_libraries(pusch_test srsran_phy)
if (NOT DEFINED TEST_EXTENSION)
set(TEST_EXTENSION Normal)
endif(NOT DEFINED TEST_EXTENSION)
if (TEST_EXTENSION STREQUAL Paranoid)
if (${ENABLE_ALL_TEST})
# All valid number of PRBs for PUSCH
set(cell_n_prb_valid 1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 40 45 48 50 54 60 64 72 75 80 81 90 96 100)
@ -481,7 +478,7 @@ if (TEST_EXTENSION STREQUAL Paranoid)
set(pusch_cqi none wideband)
else (TEST_EXTENSION STREQUAL Paranoid)
else ()
set(cell_n_prb_valid 50)
set(pusch_min_mcs 0)
@ -492,7 +489,7 @@ else (TEST_EXTENSION STREQUAL Paranoid)
set(pusch_cqi none wideband)
endif (TEST_EXTENSION STREQUAL Paranoid)
endif ()
foreach (cell_n_prb 6 15 25 50 75 100)
set(pusch_cell_n_prb)

@ -48,7 +48,7 @@ uint8_t bch_payload_file[SRSRAN_BCH_PAYLOAD_LEN] = {0, 1, 1, 0, 1, 0, 0, 0, 0, 0
#define FLEN (10 * SRSRAN_SF_LEN(srsran_symbol_sz(cell.nof_prb)))
srsran_filesource_t fsrc;
cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS];
cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS];
srsran_pbch_t pbch;
srsran_ofdm_t fft;
srsran_chest_dl_t chest;

@ -42,7 +42,7 @@ int flen;
FILE* fmatlab = NULL;
srsran_filesource_t fsrc;
cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS];
cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS];
srsran_pcfich_t pcfich;
srsran_regs_t regs;
srsran_ofdm_t fft;

@ -48,7 +48,7 @@ int max_frames = 10;
srsran_dci_format_t dci_format = SRSRAN_DCI_FORMAT1A;
srsran_filesource_t fsrc;
srsran_pdcch_t pdcch;
cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS];
cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS];
srsran_regs_t regs;
srsran_ofdm_t fft;
srsran_chest_dl_t chest;

@ -24,58 +24,70 @@
#include <string.h>
#include <unistd.h>
#include "srsran/common/test_common.h"
#include "srsran/srsran.h"
srsran_cell_t cell = {.nof_prb = 6,
.nof_ports = 1,
.id = 1,
.cp = SRSRAN_CP_NORM,
.phich_resources = SRSRAN_PHICH_R_1,
.phich_length = SRSRAN_PHICH_NORM};
uint32_t cfi = 1;
uint32_t nof_rx_ant = 1;
bool print_dci_table;
srsran_dci_cfg_t dci_cfg = {};
void usage(char* prog)
// Test parameters
static uint32_t pci = 1;
static uint16_t rnti = 0x46;
static uint32_t cfi = 2;
static uint32_t nof_ports = 1;
static srsran_dci_cfg_t dci_cfg = {};
static uint32_t nof_prb = 100;
static float snr_dB = NAN;
static uint32_t repetitions = 1;
static bool false_check = false;
// Test objects
static srsran_random_t random_gen = NULL;
static srsran_pdcch_t pdcch_tx = {};
static srsran_pdcch_t pdcch_rx = {};
static srsran_chest_dl_res_t chest_dl_res = {};
static srsran_channel_awgn_t awgn = {};
static cf_t* slot_symbols[SRSRAN_MAX_PORTS] = {};
static void usage(char* prog)
{
printf("Usage: %s [cfpndxv]\n", prog);
printf("\t-c cell id [Default %d]\n", cell.id);
printf("Usage: %s [pfncxv]\n", prog);
printf("\t-c cell id [Default %d]\n", pci);
printf("\t-f cfi [Default %d]\n", cfi);
printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports);
printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb);
printf("\t-A nof_rx_ant [Default %d]\n", nof_rx_ant);
printf("\t-d Print DCI table [Default %s]\n", print_dci_table ? "yes" : "no");
printf("\t-p cell.nof_ports [Default %d]\n", nof_ports);
printf("\t-n cell.nof_prb [Default %d]\n", nof_prb);
printf("\t-x Enable/Disable Cross-scheduling [Default %s]\n", dci_cfg.cif_enabled ? "enabled" : "disabled");
printf("\t-F False detection check [Default %s]\n", false_check ? "enabled" : "disabled");
printf("\t-R Repetitions [Default %d]\n", repetitions);
printf("\t-S SNR in dB [Default %+.1f]\n", snr_dB);
printf("\t-v [set srsran_verbose to debug, 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, "cfpndvAx")) != -1) {
while ((opt = getopt(argc, argv, "pfncxvFRS")) != -1) {
switch (opt) {
case 'p':
cell.nof_ports = (uint32_t)strtol(argv[optind], NULL, 10);
nof_ports = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'f':
cfi = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'n':
cell.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'c':
cell.id = (uint32_t)strtol(argv[optind], NULL, 10);
pci = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'x':
dci_cfg.cif_enabled = !dci_cfg.cif_enabled;
break;
case 'A':
nof_rx_ant = (uint32_t)strtol(argv[optind], NULL, 10);
case 'F':
false_check = !false_check;
break;
case 'd':
print_dci_table = true;
case 'R':
repetitions = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'x':
dci_cfg.cif_enabled ^= true;
case 'S':
snr_dB = (float)strtof(argv[optind], NULL);
break;
case 'v':
srsran_verbose++;
@ -85,12 +97,32 @@ void parse_args(int argc, char** argv)
exit(-1);
}
}
printf("params - pci=%d; rnti=0x%04x; cfi=%d; nof_ports=%d; cif_enabled=%d; nof_prb=%d; snr_db=%+.1f; "
"repetitions=%d; false_check=%d;\n",
pci,
rnti,
cfi,
nof_ports,
dci_cfg.cif_enabled,
nof_prb,
snr_dB,
repetitions,
false_check);
}
int test_dci_payload_size()
static void print_dci_msg(const char* desc, const srsran_dci_msg_t* dci_msg)
{
printf("%srnti=0x%04x; format=%s; L=%d; ncce=%d; payload=",
desc,
rnti,
srsran_dci_format_string(dci_msg->format),
dci_msg->location.L,
dci_msg->location.ncce);
srsran_vec_fprint_byte(stdout, dci_msg->payload, dci_msg->nof_bits);
}
static int assert_payload_size(const srsran_cell_t* cell, srsran_dl_sf_cfg_t* dl_sf)
{
int i, j;
int x[5];
const srsran_dci_format_t formats[] = {
SRSRAN_DCI_FORMAT0, SRSRAN_DCI_FORMAT1, SRSRAN_DCI_FORMAT1A, SRSRAN_DCI_FORMAT1C, SRSRAN_DCI_FORMAT2A};
const int prb[6] = {6, 15, 25, 50, 75, 100};
@ -101,297 +133,282 @@ int test_dci_payload_size()
{27, 33, 27, 14, 42},
{28, 39, 28, 15, 48}};
srsran_dl_sf_cfg_t dl_sf;
ZERO_OBJECT(dl_sf);
srsran_cell_t cell_test;
ZERO_OBJECT(cell_test);
cell_test.nof_ports = 1;
// Skip if special options are requested
if (dci_cfg.cif_enabled || dci_cfg.multiple_csi_request_enabled) {
return SRSRAN_SUCCESS;
}
ZERO_OBJECT(dci_cfg);
// Skip if MIMO is enabled
if (cell->nof_ports > 1) {
return SRSRAN_SUCCESS;
}
printf("Testing DCI payload sizes...\n");
printf(" PRB\t0\t1\t1A\t1C\t2A\n");
for (i = 0; i < 6; i++) {
int n = prb[i];
cell_test.nof_prb = n;
for (uint32_t i = 0; i < 6; i++) {
if (prb[i] != cell->nof_prb) {
continue;
}
int n = prb[i];
for (j = 0; j < 5; j++) {
x[j] = srsran_dci_format_sizeof(&cell_test, &dl_sf, &dci_cfg, formats[j]);
uint32_t x[5];
for (uint32_t j = 0; j < 5; j++) {
x[j] = srsran_dci_format_sizeof(cell, dl_sf, &dci_cfg, formats[j]);
if (x[j] != dci_sz[i][j]) {
ERROR("Invalid DCI payload size for %s and %d PRB. Is %d and should be %d",
srsran_dci_format_string(formats[j]),
n,
x[j],
dci_sz[i][j]);
return -1;
}
}
printf(" %2d:\t%2d\t%2d\t%2d\t%2d\t%2d\n", n, x[0], x[1], x[2], x[3], x[4]);
}
printf("Ok\n");
if (print_dci_table) {
printf("dci_sz_table[101][4] = {\n");
for (i = 0; i <= 100; i++) {
printf(" {");
for (j = 0; j < 4; j++) {
cell_test.nof_prb = i;
printf("%d", srsran_dci_format_sizeof(&cell, &dl_sf, &dci_cfg, formats[j]));
if (j < 3) {
printf(", ");
}
}
if (i < 100) {
printf("},\n");
} else {
printf("}\n");
return SRSRAN_ERROR;
}
}
printf("};\n");
return SRSRAN_SUCCESS;
}
return 0;
}
typedef struct {
srsran_dci_msg_t dci_tx, dci_rx;
srsran_dci_location_t dci_location;
srsran_dci_format_t dci_format;
srsran_dci_dl_t ra_dl_tx;
srsran_dci_dl_t ra_dl_rx;
} testcase_dci_t;
static const srsran_dci_format_t formats[] = {SRSRAN_DCI_FORMAT0,
SRSRAN_DCI_FORMAT1A,
SRSRAN_DCI_FORMAT1,
SRSRAN_DCI_FORMAT2A,
SRSRAN_DCI_FORMAT2,
SRSRAN_DCI_NOF_FORMATS};
int main(int argc, char** argv)
static float get_snr_dB(uint32_t L)
{
srsran_chest_dl_res_t chest_dl_res;
srsran_pdcch_t pdcch_tx, pdcch_rx;
testcase_dci_t testcases[10];
srsran_regs_t regs;
int i;
int nof_re;
cf_t* slot_symbols[SRSRAN_MAX_PORTS];
int nof_dcis;
static const float snr_table_dB[4] = {15.0f, 6.0f, 5.0f, 0.0f};
bzero(&testcases, sizeof(testcase_dci_t) * 10);
srsran_random_t random_gen = srsran_random_init(0x1234);
if (isnormal(snr_dB) && L < 4) {
return snr_dB;
}
int ret = -1;
return snr_table_dB[L];
}
parse_args(argc, argv);
static int test_case1()
{
uint32_t nof_re = SRSRAN_NOF_RE(pdcch_tx.cell);
// Iterate all possible subframes
for (uint32_t f_idx = 0; formats[f_idx] != SRSRAN_DCI_NOF_FORMATS; f_idx++) {
srsran_dci_format_t format = formats[f_idx];
struct timeval t[3] = {};
uint64_t t_encode_us = 0;
uint64_t t_encode_count = 0;
uint64_t t_llr_us = 0;
uint64_t t_decode_us = 0;
uint64_t t_decode_count = 0;
uint32_t false_alarm_corr_count = 0;
float min_corr = INFINITY;
for (uint32_t sf_idx = 0; sf_idx < repetitions * SRSRAN_NOF_SF_X_FRAME; sf_idx++) {
srsran_dl_sf_cfg_t dl_sf_cfg = {};
dl_sf_cfg.cfi = cfi;
dl_sf_cfg.tti = sf_idx % 10240;
// Generate PDCCH locations
srsran_dci_location_t locations[SRSRAN_MAX_CANDIDATES] = {};
uint32_t locations_count = 0;
locations_count +=
srsran_pdcch_common_locations(&pdcch_tx, &locations[locations_count], SRSRAN_MAX_CANDIDATES_COM, cfi);
locations_count +=
srsran_pdcch_ue_locations(&pdcch_tx, &dl_sf_cfg, &locations[locations_count], SRSRAN_MAX_CANDIDATES_UE, rnti);
// Iterate all possible locations
for (uint32_t loc = 0; loc < locations_count; loc++) {
srsran_dci_msg_t dci_tx = {};
dci_tx.nof_bits = srsran_dci_format_sizeof(&pdcch_tx.cell, &dl_sf_cfg, &dci_cfg, format);
dci_tx.location = locations[loc];
dci_tx.format = format;
dci_tx.rnti = rnti;
// Assert DCI size
TESTASSERT(assert_payload_size(&pdcch_tx.cell, &dl_sf_cfg) == SRSRAN_SUCCESS);
// Initialise resource grid for each Tx port
for (uint32_t p = 0; p < nof_ports; p++) {
srsran_vec_cf_zero(slot_symbols[p], nof_re);
}
// Generate Tx DCI
srsran_random_bit_vector(random_gen, dci_tx.payload, dci_tx.nof_bits);
// Encode
gettimeofday(&t[1], NULL);
TESTASSERT(srsran_pdcch_encode(&pdcch_tx, &dl_sf_cfg, &dci_tx, slot_symbols) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_encode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec);
t_encode_count++;
// Set noise level according to aggregation level
float n0_dB = -get_snr_dB(locations[loc].L);
TESTASSERT(srsran_channel_awgn_set_n0(&awgn, n0_dB) == SRSRAN_SUCCESS);
chest_dl_res.noise_estimate = srsran_convert_dB_to_power(n0_dB);
// Apply AWGN
for (uint32_t p = 0; p < nof_ports; p++) {
srsran_channel_awgn_run_c(&awgn, slot_symbols[p], slot_symbols[p], nof_re);
}
// Extract LLR
gettimeofday(&t[1], NULL);
TESTASSERT(srsran_pdcch_extract_llr(&pdcch_rx, &dl_sf_cfg, &chest_dl_res, slot_symbols) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_llr_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec);
// Try decoding the PDCCH in all possible locations
for (uint32_t loc_rx = 0; loc_rx < locations_count; loc_rx++) {
// Skip location if:
// - False check is disabled and Tx/Rx dont match
// - Tx aggregation level is bigger than Rx aggregation level
if ((!false_check && loc_rx != loc) || locations[loc_rx].L < locations[loc].L) {
continue;
}
// Prepare DCI message context
srsran_dci_msg_t dci_rx = {};
dci_rx.location = locations[loc_rx];
dci_rx.format = format;
// Try to decode PDCCH message
gettimeofday(&t[1], NULL);
TESTASSERT(srsran_pdcch_decode_msg(&pdcch_rx, &dl_sf_cfg, &dci_cfg, &dci_rx) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_decode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec);
t_decode_count++;
// Compute LLR correlation
float corr = srsran_pdcch_msg_corr(&pdcch_rx, &dci_rx);
bool rnti_match = (dci_tx.rnti == dci_rx.rnti);
bool location_match = (loc == loc_rx);
bool payload_match = (memcmp(dci_tx.payload, dci_rx.payload, dci_tx.nof_bits) == 0);
bool corr_thr = corr > 0.5f;
// Skip location if the decoding is not successful in a different location than transmitted
if (!location_match && !rnti_match) {
continue;
}
// Skip location if the correlation does not surpass the threshold
if (!location_match && !corr_thr) {
false_alarm_corr_count++;
continue;
}
// Assert correlation only if location matches
if (location_match) {
TESTASSERT(corr_thr);
if (location_match && corr < min_corr) {
min_corr = corr;
}
}
if (srsran_verbose >= SRSRAN_VERBOSE_INFO || !payload_match) {
// If payload is not match and there is no logging, set logging to info and run the decoder again
if (srsran_verbose < SRSRAN_VERBOSE_INFO) {
printf("-- Detected payload was not matched, repeating decode with INFO logs (n0: %+.1f dB, corr: %f)\n",
n0_dB,
corr);
srsran_verbose = SRSRAN_VERBOSE_INFO;
srsran_pdcch_decode_msg(&pdcch_rx, &dl_sf_cfg, &dci_cfg, &dci_rx);
}
print_dci_msg("Tx: ", &dci_tx);
print_dci_msg("Rx: ", &dci_rx);
}
nof_re = SRSRAN_CP_NORM_NSYMB * cell.nof_prb * SRSRAN_NRE;
// Assert received message
TESTASSERT(payload_match);
}
}
}
if (test_dci_payload_size()) {
exit(-1);
printf("test_case_1 - format %s - passed - %.1f usec/encode; %.1f usec/llr; %.1f usec/decode; min_corr=%f; "
"false_alarm_prob=%f;\n",
srsran_dci_format_string(format),
(double)t_encode_us / (double)(t_encode_count),
(double)t_llr_us / (double)(t_encode_count),
(double)t_decode_us / (double)(t_decode_count),
min_corr,
(double)false_alarm_corr_count / (double)t_decode_count);
}
/* init memory */
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
srsran_regs_t regs;
int i;
int ret = SRSRAN_ERROR;
srsran_chest_dl_res_init(&chest_dl_res, cell.nof_prb);
parse_args(argc, argv);
random_gen = srsran_random_init(0x1234);
// Create cell
srsran_cell_t cell = {};
cell.nof_prb = nof_prb;
cell.nof_ports = nof_ports;
cell.cp = SRSRAN_CP_NORM;
cell.phich_resources = SRSRAN_PHICH_R_1;
cell.phich_length = SRSRAN_PHICH_NORM;
// Initialise channel estimates with identity matrix
if (srsran_chest_dl_res_init(&chest_dl_res, cell.nof_prb) < SRSRAN_SUCCESS) {
ERROR("Error channel estimates");
goto quit;
}
srsran_chest_dl_res_set_identity(&chest_dl_res);
// Allocate grid
uint32_t nof_re = SRSRAN_NOF_RE(cell);
for (i = 0; i < SRSRAN_MAX_PORTS; i++) {
slot_symbols[i] = srsran_vec_cf_malloc(nof_re);
if (!slot_symbols[i]) {
perror("malloc");
exit(-1);
if (slot_symbols[i] == NULL) {
ERROR("malloc");
goto quit;
}
srsran_vec_cf_zero(slot_symbols[i], nof_re);
}
if (srsran_regs_init(&regs, cell)) {
ERROR("Error initiating regs");
exit(-1);
goto quit;
}
if (srsran_pdcch_init_enb(&pdcch_tx, cell.nof_prb)) {
ERROR("Error creating PDCCH object");
exit(-1);
goto quit;
}
if (srsran_pdcch_set_cell(&pdcch_tx, &regs, cell)) {
ERROR("Error setting cell in PDCCH object");
exit(-1);
goto quit;
}
if (srsran_pdcch_init_ue(&pdcch_rx, cell.nof_prb, nof_rx_ant)) {
if (srsran_pdcch_init_ue(&pdcch_rx, cell.nof_prb, nof_ports)) {
ERROR("Error creating PDCCH object");
exit(-1);
goto quit;
}
if (srsran_pdcch_set_cell(&pdcch_rx, &regs, cell)) {
ERROR("Error setting cell in PDCCH object");
exit(-1);
goto quit;
}
/* Resource allocate init */
nof_dcis = 0;
srsran_dci_dl_t dci;
ZERO_OBJECT(dci);
dci.pid = 5;
dci.tb[0].mcs_idx = 5;
dci.tb[0].ndi = 0;
dci.tb[0].rv = 1;
dci.alloc_type = SRSRAN_RA_ALLOC_TYPE0;
dci.type0_alloc.rbg_bitmask = 0x5;
dci.cif_present = dci_cfg.cif_enabled;
if (dci_cfg.cif_enabled) {
dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7);
if (srsran_channel_awgn_init(&awgn, 0x1234) < SRSRAN_SUCCESS) {
ERROR("Error init AWGN");
goto quit;
}
/* Format 1 Test case */
if (cell.nof_ports == 1) {
testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT1;
if (dci_cfg.cif_enabled) {
dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7);
}
testcases[nof_dcis].ra_dl_tx = dci;
nof_dcis++;
/* Format 1 Test case */
dci.tb[0].mcs_idx = 15;
testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT1;
if (dci_cfg.cif_enabled) {
dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7);
}
testcases[nof_dcis].ra_dl_tx = dci;
nof_dcis++;
// Execute actual test cases
if (test_case1() < SRSRAN_SUCCESS) {
ERROR("Test case 1 failed");
goto quit;
}
/* Tx Diversity Test case */
if (cell.nof_ports > 1) {
dci.tb[1].mcs_idx = 13;
dci.tb[1].rv = 3;
dci.tb[1].ndi = true;
testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT2A;
if (dci_cfg.cif_enabled) {
dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7);
}
testcases[nof_dcis].ra_dl_tx = dci;
nof_dcis++;
}
/* CDD Spatial Multiplexing Test case */
if (cell.nof_ports > 1) {
dci.tb[1].mcs_idx = 28;
dci.tb[1].rv = 1;
dci.tb[1].ndi = false;
testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT2;
if (dci_cfg.cif_enabled) {
dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7);
}
testcases[nof_dcis].ra_dl_tx = dci;
nof_dcis++;
}
srsran_dl_sf_cfg_t dl_sf;
ZERO_OBJECT(dl_sf);
dl_sf.cfi = cfi;
for (int s = 0; s < 10; s++) {
dl_sf.tti = s;
printf("Encoding %d DCIs for sf_idx=%d\n", nof_dcis, s);
/* Execute Rx */
for (i = 0; i < nof_dcis; i++) {
testcases[i].ra_dl_tx.rnti = (uint16_t)(1234 + i);
testcases[i].ra_dl_tx.format = testcases[i].dci_format;
srsran_dci_msg_pack_pdsch(&cell, &dl_sf, &dci_cfg, &testcases[i].ra_dl_tx, &testcases[i].dci_tx);
srsran_dci_location_set(&testcases[i].dci_location, 0, (uint32_t)i);
testcases[i].dci_tx.format = testcases[i].dci_format;
testcases[i].dci_tx.location = testcases[i].dci_location;
// Enable just 1 TB per default
if (testcases[i].dci_format < SRSRAN_DCI_FORMAT2) {
for (int j = 1; j < SRSRAN_MAX_CODEWORDS; j++) {
SRSRAN_DCI_TB_DISABLE(testcases[i].ra_dl_tx.tb[j]);
}
}
if (srsran_pdcch_encode(&pdcch_tx, &dl_sf, &testcases[i].dci_tx, slot_symbols)) {
ERROR("Error encoding DCI message");
goto quit;
}
}
/* Execute 'Rx' */
if (srsran_pdcch_extract_llr(&pdcch_rx, &dl_sf, &chest_dl_res, slot_symbols)) {
ERROR("Error extracting LLRs");
goto quit;
}
/* Decode DCIs */
for (i = 0; i < nof_dcis; i++) {
testcases[i].dci_rx.format = testcases[i].dci_format;
testcases[i].dci_rx.location = testcases[i].dci_location;
if (srsran_pdcch_decode_msg(&pdcch_rx, &dl_sf, &dci_cfg, &testcases[i].dci_rx)) {
ERROR("Error decoding DCI message");
goto quit;
}
if (srsran_dci_msg_unpack_pdsch(&cell, &dl_sf, &dci_cfg, &testcases[i].dci_rx, &testcases[i].ra_dl_rx)) {
ERROR("Error unpacking DCI message");
goto quit;
}
if (testcases[i].dci_rx.rnti >= 1234 && testcases[i].dci_rx.rnti < 1234 + nof_dcis) {
testcases[i].dci_rx.rnti -= 1234;
} else {
printf("Received invalid DCI CRC %d\n", testcases[i].dci_rx.rnti);
goto quit;
}
}
/* Compare Tx and Rx */
for (i = 0; i < nof_dcis; i++) {
if (memcmp(testcases[i].dci_tx.payload, testcases[i].dci_rx.payload, testcases[i].dci_tx.nof_bits)) {
printf("Error in DCI %d: Received data does not match\n", i);
goto quit;
}
#if SRSRAN_DCI_HEXDEBUG
// Ignore Hex str
bzero(testcases[i].ra_dl_rx.hex_str, sizeof(testcases[i].ra_dl_rx.hex_str));
testcases[i].ra_dl_rx.nof_bits = 0;
#endif
// Ignore DCI location
testcases[i].ra_dl_rx.location = testcases[i].ra_dl_tx.location;
// Ignore cw_idx
for (int j = 0; j < SRSRAN_MAX_CODEWORDS; j++) {
testcases[i].ra_dl_rx.tb[j].cw_idx = testcases[i].ra_dl_tx.tb[j].cw_idx;
}
if (memcmp(&testcases[i].ra_dl_tx, &testcases[i].ra_dl_rx, sizeof(srsran_dci_dl_t))) {
uint8_t* x = (uint8_t*)&testcases[i].ra_dl_rx;
uint8_t* y = (uint8_t*)&testcases[i].ra_dl_tx;
for (int j = 0; j < sizeof(srsran_dci_dl_t); j++) {
if (x[j] != y[j]) {
printf("error in byte %d, rx=%d, tx=%d\n", j, x[j], y[j]);
}
}
printf("tx: ");
srsran_vec_fprint_byte(stdout, (uint8_t*)&testcases[i].ra_dl_tx, sizeof(srsran_dci_dl_t));
printf("rx: ");
srsran_vec_fprint_byte(stdout, (uint8_t*)&testcases[i].ra_dl_rx, sizeof(srsran_dci_dl_t));
printf("Error in RA %d: Received data does not match\n", i);
printf(" Field | Tx | Rx \n");
printf("--------------+----------+----------\n");
if (testcases[i].ra_dl_tx.cif) {
printf(" cif | %8d | %8d\n", testcases[i].ra_dl_tx.cif, testcases[i].ra_dl_rx.cif);
}
printf(" harq_process | %8d | %8d\n", testcases[i].ra_dl_tx.pid, testcases[i].ra_dl_rx.pid);
printf(" mcs_idx | %8d | %8d\n", testcases[i].ra_dl_tx.tb[0].mcs_idx, testcases[i].ra_dl_rx.tb[0].mcs_idx);
printf(" rv_idx | %8d | %8d\n", testcases[i].ra_dl_tx.tb[0].rv, testcases[i].ra_dl_rx.tb[0].rv);
printf(" ndi | %8d | %8d\n", testcases[i].ra_dl_tx.tb[0].ndi, testcases[i].ra_dl_rx.tb[0].ndi);
printf(" mcs_idx_1 | %8d | %8d\n", testcases[i].ra_dl_tx.tb[1].mcs_idx, testcases[i].ra_dl_rx.tb[1].mcs_idx);
printf(" rv_idx_1 | %8d | %8d\n", testcases[i].ra_dl_tx.tb[1].rv, testcases[i].ra_dl_rx.tb[1].rv);
printf(" ndi_1 | %8d | %8d\n", testcases[i].ra_dl_tx.tb[1].ndi, testcases[i].ra_dl_rx.tb[1].ndi);
printf(" tb_cw_swap | %8d | %8d\n", testcases[i].ra_dl_tx.tb_cw_swap, testcases[i].ra_dl_rx.tb_cw_swap);
printf(" sram_id | %8d | %8d\n", testcases[i].ra_dl_tx.sram_id, testcases[i].ra_dl_rx.sram_id);
printf(" pinfo | %8d | %8d\n", testcases[i].ra_dl_tx.pinfo, testcases[i].ra_dl_rx.pinfo);
printf(" pconf | %8d | %8d\n", testcases[i].ra_dl_tx.pconf, testcases[i].ra_dl_rx.pconf);
printf(" power_offset | %8d | %8d\n", testcases[i].ra_dl_tx.power_offset, testcases[i].ra_dl_rx.power_offset);
printf(" tpc_pucch | %8d | %8d\n", testcases[i].ra_dl_tx.tpc_pucch, testcases[i].ra_dl_rx.tpc_pucch);
goto quit;
}
}
}
ret = 0;
ret = SRSRAN_SUCCESS;
quit:
srsran_pdcch_free(&pdcch_tx);
@ -399,9 +416,12 @@ quit:
srsran_chest_dl_res_free(&chest_dl_res);
srsran_regs_free(&regs);
srsran_random_free(random_gen);
srsran_channel_awgn_free(&awgn);
for (i = 0; i < SRSRAN_MAX_PORTS; i++) {
free(slot_symbols[i]);
if (slot_symbols[i]) {
free(slot_symbols[i]);
}
}
if (ret) {
printf("Error\n");

@ -174,7 +174,7 @@ int main(int argc, char** argv)
exit(-1);
}
uint8_t* data[] = {malloc(100000)};
uint8_t* data[SRSRAN_MAX_CODEWORDS] = {malloc(100000)};
if (!data[0]) {
perror("malloc");
exit(-1);

@ -159,7 +159,7 @@ static int check_softbits(srsran_pdsch_t* pdsch_enb,
if (!pdsch_ue->llr_is_8bit && !tb_cw_swap) {
// Scramble
srsran_sequence_pdsch_apply_c(pdsch_ue->e[tb],
srsran_sequence_pdsch_apply_s(pdsch_ue->e[tb],
pdsch_ue->e[tb],
rnti,
pdsch_cfg->grant.tb[tb].cw_idx,
@ -177,6 +177,10 @@ static int check_softbits(srsran_pdsch_t* pdsch_enb,
rx_bytes[i] = w;
}
if (memcmp(pdsch_ue->e[tb], pdsch_enb->e[tb], pdsch_cfg->grant.tb[tb].nof_bits / 8) != 0) {
printf("tx=");
srsran_vec_fprint_byte(stdout, pdsch_enb->e[tb], pdsch_cfg->grant.tb[tb].nof_bits / 8);
printf("rx=");
srsran_vec_fprint_byte(stdout, pdsch_ue->e[tb], pdsch_cfg->grant.tb[tb].nof_bits / 8);
ret = SRSRAN_ERROR;
}
}
@ -204,6 +208,7 @@ int main(int argc, char** argv)
srsran_chest_dl_res_t chest_res;
srsran_pdsch_res_t pdsch_res[SRSRAN_MAX_CODEWORDS];
srsran_random_t random_gen = srsran_random_init(0x1234);
srsran_crc_t crc_tb;
/* Initialise to zeros */
ZERO_OBJECT(softbuffers_tx);
@ -221,6 +226,7 @@ int main(int argc, char** argv)
ZERO_OBJECT(chest);
ZERO_OBJECT(chest_res);
ZERO_OBJECT(pdsch_res);
ZERO_OBJECT(crc_tb);
parse_args(argc, argv);
@ -393,11 +399,19 @@ int main(int argc, char** argv)
}
}
if (srsran_crc_init(&crc_tb, SRSRAN_LTE_CRC24A, 24) < SRSRAN_SUCCESS) {
ERROR("Error initiating CRC24A");
goto quit;
}
// Generate random data
for (int tb = 0; tb < SRSRAN_MAX_CODEWORDS; tb++) {
if (pdsch_cfg.grant.tb[tb].enabled) {
for (int byte = 0; byte < pdsch_cfg.grant.tb[tb].tbs / 8; byte++) {
data_tx[tb][byte] = (uint8_t)srsran_random_uniform_int_dist(random_gen, 0, 255);
}
// Attach CRC for making sure TB with 0 CRC are detected
srsran_crc_attach_byte(&crc_tb, data_tx[tb], pdsch_cfg.grant.tb[tb].tbs - 24);
}
}
@ -508,10 +522,12 @@ int main(int argc, char** argv)
for (int tb = 0; tb < SRSRAN_MAX_CODEWORDS; tb++) {
if (pdsch_cfg.grant.tb[tb].enabled) {
if (check_softbits(&pdsch_tx, &pdsch_rx, &pdsch_cfg, subframe, tb)) {
printf("TB%d: The received softbits in subframe %d DO NOT match the encoded bits (crc=%d)\n",
tb,
subframe,
pdsch_res[tb].crc);
ERROR("TB%d: The received softbits in subframe %d DO NOT match the encoded bits (crc=%d)\n",
tb,
subframe,
pdsch_res[tb].crc);
ret = SRSRAN_ERROR;
goto quit;
} else {
for (int byte = 0; byte < pdsch_cfg.grant.tb[tb].tbs / 8; byte++) {
if (data_tx[tb][byte] != data_rx[tb][byte]) {

@ -48,7 +48,7 @@ int numsubframe = 0;
FILE* fmatlab = NULL;
srsran_filesource_t fsrc;
cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS];
cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS];
srsran_phich_t phich;
srsran_regs_t regs;
srsran_ofdm_t fft;

@ -216,9 +216,12 @@ int main(int argc, char** argv)
SRSRAN_DCI_TB_DISABLE(dci.tb[1]);
srsran_ra_dl_dci_to_grant(&cell, &dl_sf, SRSRAN_TM1, false, &dci, &pmch_cfg.pdsch_cfg.grant);
srsran_pdsch_res_t pdsch_res;
pdsch_res.payload = data;
ret = srsran_ue_dl_decode_pmch(&ue_dl, &dl_sf, &pmch_cfg, &pdsch_res);
srsran_pdsch_res_t pdsch_res = {};
pdsch_res.payload = data;
srsran_pdsch_res_t pdsch_res_vec[SRSRAN_MAX_CODEWORDS];
pdsch_res_vec[0] = pdsch_res;
ret = srsran_ue_dl_decode_pmch(&ue_dl, &dl_sf, &pmch_cfg, pdsch_res_vec);
if (pdsch_res.crc == 1) {
printf("PMCH Decoded OK!\n");
} else if (pdsch_res.crc == 0) {

@ -164,7 +164,7 @@ int main(int argc, char** argv)
}
srsran_sci_info(&sci, sci_msg, sizeof(sci_msg));
fprintf(stdout, "%s", sci_msg);
fprintf(stdout, "%s\n", sci_msg);
if (sci.riv == riv_txed) {
ret = SRSRAN_SUCCESS;
}

@ -296,7 +296,7 @@ int main(int argc, char** argv)
if (srsran_pscch_decode(&pscch, equalized_sf_buffer, sci_rx, pscch_prb_start_idx) == SRSRAN_SUCCESS) {
if (srsran_sci_format0_unpack(&sci, sci_rx) == SRSRAN_SUCCESS) {
srsran_sci_info(&sci, sci_msg, sizeof(sci_msg));
fprintf(stdout, "%s", sci_msg);
fprintf(stdout, "%s\n", sci_msg);
sci_decoded = true;
num_decoded_sci++;
@ -357,7 +357,7 @@ int main(int argc, char** argv)
if (srsran_pscch_decode(&pscch, equalized_sf_buffer, sci_rx, pscch_prb_start_idx) == SRSRAN_SUCCESS) {
if (srsran_sci_format1_unpack(&sci, sci_rx) == SRSRAN_SUCCESS) {
srsran_sci_info(&sci, sci_msg, sizeof(sci_msg));
fprintf(stdout, "%s", sci_msg);
fprintf(stdout, "%s\n", sci_msg);
num_decoded_sci++;

@ -89,7 +89,7 @@ int test_uci_cqi_pucch(void)
int ret = SRSRAN_SUCCESS;
__attribute__((aligned(256))) uint8_t o_bits[SRSRAN_UCI_MAX_CQI_LEN_PUCCH] = {0};
__attribute__((aligned(256))) uint8_t e_bits[SRSRAN_UCI_CQI_CODED_PUCCH_B] = {0};
__attribute__((aligned(256))) int16_t e_symb[SRSRAN_UCI_CQI_CODED_PUCCH_B] = {0};
__attribute__((aligned(256))) int16_t e_symb[SRSRAN_CQI_MAX_BITS] = {0};
__attribute__((aligned(256))) uint8_t d_bits[SRSRAN_UCI_MAX_CQI_LEN_PUCCH] = {0};
srsran_uci_cqi_pucch_t uci_cqi_pucch = {0};

@ -275,7 +275,9 @@ 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) <
srsran_pusch_res_nr_t* data_rx_vec[SRSRAN_MAX_TB] = {};
data_rx_vec[0] = &data_rx;
if (srsran_pusch_nr_decode(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &chest, sf_symbols, data_rx_vec) <
SRSRAN_SUCCESS) {
ERROR("Error encoding");
goto clean_exit;

@ -193,8 +193,10 @@ int main(int argc, char** argv)
srsran_pusch_cfg_t cfg;
srsran_softbuffer_tx_t softbuffer_tx;
srsran_softbuffer_rx_t softbuffer_rx;
srsran_crc_t crc_tb;
ZERO_OBJECT(uci_data_tx);
ZERO_OBJECT(crc_tb);
bzero(&cfg, sizeof(srsran_pusch_cfg_t));
@ -292,9 +294,12 @@ int main(int argc, char** argv)
srsran_softbuffer_tx_reset(&softbuffer_tx);
srsran_softbuffer_rx_reset(&softbuffer_rx);
// Generate random data
for (uint32_t i = 0; i < cfg.grant.tb.tbs / 8; i++) {
data[i] = (uint8_t)srsran_random_uniform_int_dist(random_h, 0, 255);
}
// Attach CRC for making sure TB with 0 CRC are detected
srsran_crc_attach_byte(&crc_tb, data, cfg.grant.tb.tbs - 24);
for (uint32_t a = 0; a < uci_data_tx.cfg.ack[0].nof_acks; a++) {
uci_data_tx.value.ack.ack_value[a] = (uint8_t)srsran_random_uniform_int_dist(random_h, 0, 1);

@ -129,7 +129,7 @@ int16_t srsran_uci_decode_cqi_pucch(srsran_uci_cqi_pucch_t* q,
uint8_t* cqi_data,
uint32_t cqi_len)
{
if (cqi_len < SRSRAN_UCI_MAX_CQI_LEN_PUCCH && b_bits != NULL && cqi_data != NULL) {
if (q != NULL && cqi_len < SRSRAN_UCI_MAX_CQI_LEN_PUCCH && b_bits != NULL && cqi_data != NULL) {
uint32_t max_w = 0;
int32_t max_corr = INT32_MIN;
uint32_t nwords = 1 << SRSRAN_UCI_MAX_CQI_LEN_PUCCH;
@ -655,9 +655,10 @@ int srsran_uci_decode_ack_ri(srsran_pusch_cfg_t* cfg,
uint32_t Qm = srsran_mod_bits_x_symbol(cfg->grant.tb.mod);
int16_t llr_acc[32] = {}; ///< LLR accumulator
uint32_t nof_acc =
(nof_bits == 1) ? Qm : (nof_bits == 2) ? Qm * 3 : SRSRAN_FEC_BLOCK_SIZE; ///< Number of required LLR
uint32_t count_acc = 0; ///< LLR counter
uint32_t nof_acc = (nof_bits == 1) ? Qm
: (nof_bits == 2) ? Qm * 3
: SRSRAN_FEC_BLOCK_SIZE; ///< Number of required LLR
uint32_t count_acc = 0; ///< LLR counter
for (uint32_t i = 0; i < Qprime; i++) {
if (is_ri) {

@ -24,7 +24,9 @@
#include <unistd.h>
#include "rf_blade_imp.h"
#include "srsran/srsran.h"
#include "srsran/phy/common/timestamp.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#define UNUSED __attribute__((unused))
#define CONVERT_BUFFER_SIZE (240 * 1024)

@ -23,7 +23,7 @@
#include "rf_dev.h"
#include "srsran/phy/rf/rf.h"
#include "srsran/srsran.h"
#include "srsran/phy/utils/debug.h"
int rf_get_available_devices(char** devnames, int max_strlen)
{
@ -184,7 +184,9 @@ int srsran_rf_close(srsran_rf_t* rf)
{
// Stop gain thread
if (rf->thread_gain_run) {
pthread_mutex_lock(&rf->mutex);
rf->thread_gain_run = false;
pthread_mutex_unlock(&rf->mutex);
pthread_cond_signal(&rf->cond);
pthread_join(rf->thread_gain, NULL);
}

@ -26,7 +26,9 @@
#include "rf_helper.h"
#include "rf_soapy_imp.h"
#include "srsran/srsran.h"
#include "srsran/phy/common/phy_common.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include <SoapySDR/Device.h>
#include <SoapySDR/Formats.h>

@ -28,7 +28,7 @@
#include <uhd/usrp/multi_usrp.hpp>
#include "rf_helper.h"
#include "srsran/srsran.h"
#include "srsran/phy/utils/debug.h"
#include "rf_uhd_generic.h"
#include "rf_uhd_imp.h"

@ -606,12 +606,7 @@ int rf_zmq_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking,
return rf_zmq_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs);
}
int rf_zmq_recv_with_time_multi(void* h,
void* data[4],
uint32_t nsamples,
bool blocking,
time_t* secs,
double* frac_secs)
int rf_zmq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs)
{
int ret = SRSRAN_ERROR;
@ -683,7 +678,7 @@ int rf_zmq_recv_with_time_multi(void* h,
rf_zmq_info(handler->id, " - next tx time: %d + %.3f\n", ts_tx.full_secs, ts_tx.frac_secs);
// Leave time for the Tx to transmit
usleep((1000000 * nsamples_baserate) / handler->base_srate);
usleep((1000000UL * nsamples_baserate) / handler->base_srate);
// check for tx gap if we're also transmitting on this radio
for (int i = 0; i < handler->nof_channels; i++) {

@ -26,11 +26,20 @@
#include <string.h>
#include <zmq.h>
bool is_rx_running(rf_zmq_rx_t* q)
{
bool running = false;
pthread_mutex_lock(&q->mutex);
running = q->running;
pthread_mutex_unlock(&q->mutex);
return running;
}
static void* rf_zmq_async_rx_thread(void* h)
{
rf_zmq_rx_t* q = (rf_zmq_rx_t*)h;
while (q->sock && q->running) {
while (q->sock && is_rx_running(q)) {
int nbytes = 0;
int n = SRSRAN_ERROR;
uint8_t dummy = 0xFF;
@ -39,7 +48,7 @@ static void* rf_zmq_async_rx_thread(void* h)
// Send request if socket type is REQUEST
if (q->socket_type == ZMQ_REQ) {
while (n < 0 && q->running) {
while (n < 0 && is_rx_running(q)) {
rf_zmq_info(q->id, " - tx'ing rx request\n");
n = zmq_send(q->sock, &dummy, sizeof(dummy), 0);
if (n < 0) {
@ -53,7 +62,7 @@ static void* rf_zmq_async_rx_thread(void* h)
}
// Receive baseband
for (n = (n < 0) ? 0 : -1; n < 0 && q->running;) {
for (n = (n < 0) ? 0 : -1; n < 0 && is_rx_running(q);) {
n = zmq_recv(q->sock, q->temp_buffer, ZMQ_MAX_BUFFER_SIZE, 0);
if (n == -1) {
if (rf_zmq_handle_error(q->id, "asynchronous rx baseband receive")) {
@ -77,7 +86,7 @@ static void* rf_zmq_async_rx_thread(void* h)
n = -1;
// Try to write in ring buffer
while (n < 0 && q->running) {
while (n < 0 && is_rx_running(q)) {
n = srsran_ringbuffer_write_timed(&q->ringbuffer, q->temp_buffer, nbytes, q->trx_timeout_ms);
if (n == SRSRAN_ERROR_TIMEOUT && q->log_trx_timeout) {
fprintf(stderr, "Error: timeout writing samples to ringbuffer after %dms\n", q->trx_timeout_ms);
@ -262,13 +271,18 @@ bool rf_zmq_rx_match_freq(rf_zmq_rx_t* q, uint32_t freq_hz)
void rf_zmq_rx_close(rf_zmq_rx_t* q)
{
rf_zmq_info(q->id, "Closing ...\n");
pthread_mutex_lock(&q->mutex);
q->running = false;
pthread_mutex_unlock(&q->mutex);
if (q->thread) {
pthread_join(q->thread, NULL);
pthread_detach(q->thread);
}
pthread_mutex_destroy(&q->mutex);
srsran_ringbuffer_free(&q->ringbuffer);
if (q->temp_buffer) {

@ -232,7 +232,11 @@ bool rf_zmq_tx_match_freq(rf_zmq_tx_t* q, uint32_t freq_hz)
void rf_zmq_tx_close(rf_zmq_tx_t* q)
{
pthread_mutex_lock(&q->mutex);
q->running = false;
pthread_mutex_unlock(&q->mutex);
pthread_mutex_destroy(&q->mutex);
if (q->zeros) {
free(q->zeros);

@ -20,7 +20,10 @@
*/
#include "rf_zmq_imp.h"
#include "srsran/srsran.h"
#include "srsran/common/tsan_options.h"
#include "srsran/phy/common/timestamp.h"
#include "srsran/phy/utils/debug.h"
#include <complex.h>
#include <pthread.h>
#include <srsran/phy/common/phy_common.h>
#include <stdlib.h>

@ -20,6 +20,7 @@
*/
#include "srsran/phy/sync/ssb.h"
#include "srsran/phy/ch_estimation/dmrs_pbch.h"
#include "srsran/phy/sync/pss_nr.h"
#include "srsran/phy/sync/sss_nr.h"
#include "srsran/phy/utils/debug.h"
@ -42,6 +43,11 @@
*/
#define SSB_CORR_SZ(SYMB_SZ) SRSRAN_MIN(1U << (uint32_t)ceil(log2((double)(SYMB_SZ)) + 3.0), 1U << 13U)
/*
* Default NR-PBCH DMRS normalised correlation (RSRP/EPRE) threshold
*/
#define SSB_PBCH_DMRS_DEFAULT_CORR_THR 0.6f
static int ssb_init_corr(srsran_ssb_t* q)
{
// Initialise correlation only if it is enabled
@ -62,6 +68,25 @@ static int ssb_init_corr(srsran_ssb_t* q)
return SRSRAN_SUCCESS;
}
static int ssb_init_pbch(srsran_ssb_t* q)
{
srsran_pbch_nr_args_t args = {};
args.enable_encode = q->args.enable_encode;
args.enable_decode = q->args.enable_decode;
args.disable_simd = q->args.disable_polar_simd;
if (!args.enable_encode && !args.enable_decode) {
return SRSRAN_SUCCESS;
}
if (srsran_pbch_nr_init(&q->pbch, &args) < SRSRAN_SUCCESS) {
ERROR("Error init NR PBCH");
return SRSRAN_SUCCESS;
}
return SRSRAN_SUCCESS;
}
int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args)
{
// Verify input parameters
@ -73,9 +98,8 @@ int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args)
q->args = *args;
// Check if the maximum sampling rate is in range, force default otherwise
if (!isnormal(q->args.max_srate_hz) || q->args.max_srate_hz < 0.0) {
q->args.max_srate_hz = SRSRAN_SSB_DEFAULT_MAX_SRATE_HZ;
}
q->args.max_srate_hz = (!isnormal(q->args.max_srate_hz)) ? SRSRAN_SSB_DEFAULT_MAX_SRATE_HZ : q->args.max_srate_hz;
q->args.pbch_dmrs_thr = (!isnormal(q->args.pbch_dmrs_thr)) ? SSB_PBCH_DMRS_DEFAULT_CORR_THR : q->args.pbch_dmrs_thr;
q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(q->args.min_scs);
q->max_symbol_sz = (uint32_t)round(q->args.max_srate_hz / q->scs_hz);
@ -95,6 +119,11 @@ int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args)
return SRSRAN_ERROR;
}
// PBCH
if (ssb_init_pbch(q) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
@ -127,11 +156,12 @@ void srsran_ssb_free(srsran_ssb_t* q)
srsran_dft_plan_free(&q->fft);
srsran_dft_plan_free(&q->fft_corr);
srsran_dft_plan_free(&q->ifft_corr);
srsran_pbch_nr_free(&q->pbch);
SRSRAN_MEM_ZERO(q, srsran_ssb_t, 1);
}
static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
{
// Case A - 15 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes of { 2 , 8 } + 14 ⋅ n . For
// carrier frequencies smaller than or equal to 3 GHz, n = 0 , 1 . For carrier frequencies within FR1 larger than 3
@ -153,7 +183,7 @@ static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t ind
return count;
}
static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
{
// Case B - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 ⋅ n .
// For carrier frequencies smaller than or equal to 3 GHz, n = 0 . For carrier frequencies within FR1 larger than 3
@ -175,7 +205,7 @@ static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t ind
return count;
}
static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
{
// Case C - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 2 , 8 } +14 ⋅ n .
// - For paired spectrum operation
@ -202,7 +232,7 @@ static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t ind
return count;
}
static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
{
// Case D - 120 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 ⋅ n .
// For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 , 7 , 8 , 10 , 11 , 12 , 13 , 15 , 16 , 17 , 18 .
@ -219,7 +249,7 @@ static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t ind
return count;
}
static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
{
// Case E - 240 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes
//{ 8 , 12 , 16 , 20 , 32 , 36 , 40 , 44 } + 56 ⋅ n . For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 ,
@ -237,9 +267,8 @@ static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t ind
return count;
}
static int ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t ssb_i)
static uint32_t ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
{
uint32_t indexes[SRSRAN_SSB_NOF_POSITION];
uint32_t Lmax = 0;
switch (cfg->pattern) {
@ -260,24 +289,8 @@ static int ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t ssb_i)
break;
case SRSRAN_SSB_PATTERN_INVALID:
ERROR("Invalid case");
return SRSRAN_ERROR;
}
uint32_t ssb_count = 0;
for (uint32_t i = 0; i < Lmax; i++) {
// There is a SSB transmission opportunity
if (cfg->position[i]) {
// Return the SSB transmission in burst
if (ssb_i == ssb_count) {
return (int)indexes[i];
}
ssb_count++;
}
}
return SRSRAN_ERROR;
return Lmax;
}
// Modulates a given symbol l and stores the time domain signal in q->tmp_time
@ -378,6 +391,23 @@ static int ssb_setup_corr(srsran_ssb_t* q)
return SRSRAN_SUCCESS;
}
static inline int ssb_get_t_offset(srsran_ssb_t* q, uint32_t ssb_idx)
{
// Get baseband time offset from the begining of the half radio frame to the first symbol
if (ssb_idx >= SRSRAN_SSB_NOF_CANDIDATES) {
ERROR("Invalid SSB candidate index (%d)", ssb_idx);
return SRSRAN_ERROR;
}
float t_offset_s = srsran_symbol_offset_s(q->l_first[ssb_idx], q->cfg.scs);
if (isnan(t_offset_s) || isinf(t_offset_s) || t_offset_s < 0.0f) {
ERROR("Invalid first symbol (l_first=%d)", q->l_first[ssb_idx]);
return SRSRAN_ERROR;
}
return (int)round(t_offset_s * q->cfg.srate_hz);
}
int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
{
// Verify input parameters
@ -389,34 +419,15 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(cfg->scs);
// Get first symbol
int l_begin = ssb_first_symbol(cfg, 0);
if (l_begin < SRSRAN_SUCCESS) {
// set it to 2 in case it is not selected
l_begin = 2;
}
q->Lmax = ssb_first_symbol(cfg, q->l_first);
float t_offset_s = srsran_symbol_offset_s((uint32_t)l_begin, cfg->scs);
if (isnan(t_offset_s) || isinf(t_offset_s) || t_offset_s < 0.0f) {
ERROR("Invalid first symbol (l_first=%d)", l_begin);
return SRSRAN_ERROR;
}
// Calculate SSB symbol size and integer offset
// Calculate SSB symbol size and integer frequency offset
double freq_offset_hz = cfg->ssb_freq_hz - cfg->center_freq_hz;
uint32_t symbol_sz = (uint32_t)round(cfg->srate_hz / q->scs_hz);
q->f_offset = (int32_t)round(freq_offset_hz / q->scs_hz);
q->t_offset = (uint32_t)round(t_offset_s * cfg->srate_hz);
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
uint32_t l_real = l + (uint32_t)l_begin;
uint32_t ref_cp_sz = 144U;
if (l_real == 0 || l_real == SRSRAN_EXT_CP_SYMBOL(cfg->scs)) {
ref_cp_sz = 160U;
}
q->cp_sz[l] = (ref_cp_sz * symbol_sz) / 2048U;
}
// Calculate cyclic prefix
q->cp_sz = (144U * symbol_sz) / 2048U;
// Calculate SSB sampling error and check
double ssb_srate_error_Hz = ((double)symbol_sz * q->scs_hz) - cfg->srate_hz;
@ -508,7 +519,12 @@ bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx)
return (sf_idx % q->cfg.periodicity_ms == 0);
}
int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out)
int srsran_ssb_add(srsran_ssb_t* q,
uint32_t N_id,
uint32_t ssb_idx,
const srsran_pbch_msg_nr_t* msg,
const cf_t* in,
cf_t* out)
{
// Verify input parameters
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || in == NULL || out == NULL) {
@ -537,27 +553,47 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m
}
// Put PBCH DMRS
// ...
srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {};
pbch_dmrs_cfg.N_id = N_id;
pbch_dmrs_cfg.n_hf = msg->hrf ? 0 : 1;
pbch_dmrs_cfg.ssb_idx = msg->ssb_idx;
pbch_dmrs_cfg.L_max = q->Lmax;
pbch_dmrs_cfg.beta = 0.0f;
if (srsran_dmrs_pbch_put(&pbch_dmrs_cfg, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Error putting PBCH DMRS");
return SRSRAN_ERROR;
}
// Put PBCH payload
// ...
srsran_pbch_nr_cfg_t pbch_cfg = {};
pbch_cfg.N_id = N_id;
pbch_cfg.ssb_scs = q->cfg.scs;
pbch_cfg.Lmax = q->Lmax;
if (srsran_pbch_nr_encode(&q->pbch, &pbch_cfg, msg, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Error encoding PBCH");
return SRSRAN_ERROR;
}
// Select start symbol from SSB candidate index
int t_offset = ssb_get_t_offset(q, ssb_idx);
if (t_offset < SRSRAN_SUCCESS) {
ERROR("Invalid SSB candidate index");
return SRSRAN_ERROR;
}
// Select input/ouput pointers considering the time offset in the slot
const cf_t* in_ptr = &in[q->t_offset];
cf_t* out_ptr = &out[q->t_offset];
const cf_t* in_ptr = &in[t_offset];
cf_t* out_ptr = &out[t_offset];
// For each SSB symbol, modulate
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
// Get CP length
uint32_t cp_len = q->cp_sz[l];
// Map SSB in resource grid and perform IFFT
ssb_modulate_symbol(q, ssb_grid, l);
// Add cyclic prefix to input;
srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - cp_len], out_ptr, cp_len);
in_ptr += cp_len;
out_ptr += cp_len;
srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - q->cp_sz], out_ptr, q->cp_sz);
in_ptr += q->cp_sz;
out_ptr += q->cp_sz;
// Add symbol to the input baseband
srsran_vec_sum_ccc(in_ptr, q->tmp_time, out_ptr, q->symbol_sz);
@ -572,21 +608,18 @@ static int ssb_demodulate(srsran_ssb_t* q, const cf_t* in, uint32_t t_offset, cf
{
const cf_t* in_ptr = &in[t_offset];
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
// Get CP length
uint32_t cp_len = q->cp_sz[l];
// Advance half CP, to avoid inter symbol interference
in_ptr += SRSRAN_FLOOR(cp_len, 2);
in_ptr += SRSRAN_FLOOR(q->cp_sz, 2);
// Copy FFT window in temporal time domain buffer
srsran_vec_cf_copy(q->tmp_time, in_ptr, q->symbol_sz);
in_ptr += q->symbol_sz + SRSRAN_CEIL(cp_len, 2);
in_ptr += q->symbol_sz + SRSRAN_CEIL(q->cp_sz, 2);
// Convert to frequency domain
srsran_dft_run_guru_c(&q->fft);
// Compensate half CP delay
srsran_vec_apply_cfo(q->tmp_freq, SRSRAN_CEIL(cp_len, 2) / (float)(q->symbol_sz), q->tmp_freq, q->symbol_sz);
srsran_vec_apply_cfo(q->tmp_freq, SRSRAN_CEIL(q->cp_sz, 2) / (float)(q->symbol_sz), q->tmp_freq, q->symbol_sz);
// Select symbol in grid
cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC];
@ -620,7 +653,7 @@ ssb_measure(srsran_ssb_t* q, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_
uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id);
uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id);
// Extract PSS LSE
// Extract PSS and SSS LSE
cf_t pss_lse[SRSRAN_PSS_NR_LEN];
cf_t sss_lse[SRSRAN_SSS_NR_LEN];
if (srsran_pss_nr_extract_lse(ssb_grid, N_id_2, pss_lse) < SRSRAN_SUCCESS ||
@ -798,8 +831,8 @@ int srsran_ssb_csi_search(srsran_ssb_t* q,
}
// Remove CP offset prior demodulation
if (t_offset >= q->cp_sz[0]) {
t_offset -= q->cp_sz[0];
if (t_offset >= q->cp_sz) {
t_offset -= q->cp_sz;
} else {
t_offset = 0;
}
@ -834,7 +867,11 @@ int srsran_ssb_csi_search(srsran_ssb_t* q,
return SRSRAN_SUCCESS;
}
int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas)
int srsran_ssb_csi_measure(srsran_ssb_t* q,
uint32_t N_id,
uint32_t ssb_idx,
const cf_t* in,
srsran_csi_trs_measurements_t* meas)
{
// Verify inputs
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || meas == NULL || !isnormal(q->scs_hz)) {
@ -842,14 +879,19 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
}
if (!q->args.enable_measure) {
ERROR("SSB is not configured for measure");
ERROR("SSB is not configured to measure");
return SRSRAN_ERROR;
}
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
// Select start symbol from SSB candidate index
int t_offset = ssb_get_t_offset(q, ssb_idx);
if (t_offset < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// Demodulate
if (ssb_demodulate(q, in, q->t_offset, ssb_grid) < SRSRAN_SUCCESS) {
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_demodulate(q, in, (uint32_t)t_offset, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Error demodulating");
return SRSRAN_ERROR;
}
@ -862,3 +904,71 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
return SRSRAN_SUCCESS;
}
int srsran_ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id,
uint32_t ssb_idx,
uint32_t n_hf,
const cf_t* in,
srsran_pbch_msg_nr_t* msg)
{
// Verify inputs
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || msg == NULL || !isnormal(q->scs_hz)) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_decode) {
ERROR("SSB is not configured to decode");
return SRSRAN_ERROR;
}
// Select start symbol from SSB candidate index
int t_offset = ssb_get_t_offset(q, ssb_idx);
if (t_offset < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// Demodulate
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_demodulate(q, in, (uint32_t)t_offset, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Error demodulating");
return SRSRAN_ERROR;
}
// Prepare PBCH DMRS configuration
srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {};
pbch_dmrs_cfg.N_id = N_id;
pbch_dmrs_cfg.n_hf = n_hf;
pbch_dmrs_cfg.ssb_idx = ssb_idx;
pbch_dmrs_cfg.L_max = q->Lmax;
pbch_dmrs_cfg.beta = 0.0f;
pbch_dmrs_cfg.scs = q->cfg.scs;
// Compute PBCH channel estimates
srsran_dmrs_pbch_meas_t meas = {};
cf_t ce[SRSRAN_SSB_NOF_RE] = {};
if (srsran_dmrs_pbch_estimate(&pbch_dmrs_cfg, ssb_grid, ce, &meas) < SRSRAN_SUCCESS) {
ERROR("Error estimating channel");
return SRSRAN_ERROR;
}
// Compare measurement with threshold
if (meas.corr < q->args.pbch_dmrs_thr) {
msg->crc = false;
return SRSRAN_SUCCESS;
}
// Prepare PBCH configuration
srsran_pbch_nr_cfg_t pbch_cfg = {};
pbch_cfg.N_id = N_id;
pbch_cfg.ssb_scs = q->cfg.scs;
pbch_cfg.Lmax = q->Lmax;
// Decode
if (srsran_pbch_nr_decode(&q->pbch, &pbch_cfg, ssb_idx, ssb_grid, ce, msg) < SRSRAN_SUCCESS) {
ERROR("Error decoding PBCH");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}

@ -136,10 +136,13 @@ add_test(cfo_test_2 cfo_test -f 0.99849 -n 1000)
########################################################################
# NE TEST
# NR TEST
########################################################################
add_executable(ssb_measure_test ssb_measure_test.c)
target_link_libraries(ssb_measure_test srsran_phy)
add_nr_test(ssb_measure_test ssb_measure_test)
add_test(ssb_measure_test ssb_measure_test)
add_executable(ssb_decode_test ssb_decode_test.c)
target_link_libraries(ssb_decode_test srsran_phy)
add_nr_test(ssb_decode_test ssb_decode_test)

@ -0,0 +1,210 @@
/**
*
* \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/common/test_common.h"
#include "srsran/phy/channel/ch_awgn.h"
#include "srsran/phy/sync/ssb.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include <complex.h>
#include <getopt.h>
#include <srsran/phy/utils/random.h>
#include <stdlib.h>
// NR parameters
static uint32_t carrier_nof_prb = 52;
static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz;
static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz;
// Channel parameters
static cf_t wideband_gain = 1.0f + 0.5 * I;
static int32_t delay_n = 1;
static float cfo_hz = 1000.0f;
static float n0_dB = -10.0f;
// Test context
static srsran_random_t random_gen = NULL;
static srsran_channel_awgn_t awgn = {};
static double srate_hz = 0.0f; // Base-band sampling rate
static uint32_t hf_len = 0; // Half-frame length
static cf_t* buffer = NULL; // Base-band buffer
static void usage(char* prog)
{
printf("Usage: %s [v]\n", prog);
printf("\t-v [set srsran_verbose to debug, default none]\n");
}
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "v")) != -1) {
switch (opt) {
case 'v':
srsran_verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
static void run_channel()
{
// Delay
for (uint32_t i = 0; i < hf_len; i++) {
buffer[i] = buffer[(i + delay_n) % hf_len];
}
// CFO
srsran_vec_apply_cfo(buffer, -cfo_hz / srate_hz, buffer, hf_len);
// AWGN
srsran_channel_awgn_run_c(&awgn, buffer, buffer, hf_len);
// Wideband gain
srsran_vec_sc_prod_ccc(buffer, wideband_gain, buffer, hf_len);
}
static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg, uint32_t ssb_idx)
{
// Default all to zero
SRSRAN_MEM_ZERO(pbch_msg, srsran_pbch_msg_nr_t, 1);
// Generate payload
srsran_random_bit_vector(random_gen, pbch_msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ);
pbch_msg->ssb_idx = ssb_idx;
}
static int test_case_1(srsran_ssb_t* ssb)
{
// For benchmarking purposes
uint64_t t_encode_usec = 0;
uint64_t t_decode_usec = 0;
// SSB configuration
srsran_ssb_cfg_t ssb_cfg = {};
ssb_cfg.srate_hz = srate_hz;
ssb_cfg.center_freq_hz = 3.5e9;
ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3;
ssb_cfg.scs = ssb_scs;
ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C;
TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS);
// For each PCI...
uint64_t count = 0;
for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += 23) {
for (uint32_t ssb_idx = 0; ssb_idx < ssb->Lmax; ssb_idx++, count++) {
struct timeval t[3] = {};
// Build PBCH message
srsran_pbch_msg_nr_t pbch_msg_tx = {};
gen_pbch_msg(&pbch_msg_tx, ssb_idx);
// Print encoded PBCH message
char str[512] = {};
srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str));
INFO("test_case_1 - encoded pci=%d %s", pci, str);
// Initialise baseband
srsran_vec_cf_zero(buffer, hf_len);
// Add the SSB base-band
gettimeofday(&t[1], NULL);
TESTASSERT(srsran_ssb_add(ssb, pci, ssb_idx, &pbch_msg_tx, buffer, buffer) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_encode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Run channel
run_channel();
// Decode
gettimeofday(&t[1], NULL);
srsran_pbch_msg_nr_t pbch_msg_rx = {};
TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, ssb_idx, 0, buffer, &pbch_msg_rx) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Print decoded PBCH message
srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str));
INFO("test_case_1 - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO");
// Assert PBCH message CRC
TESTASSERT(pbch_msg_rx.crc);
}
}
INFO("test_case_1 - %.1f usec/encode; %.1f usec/decode;",
(double)t_encode_usec / (double)(count),
(double)t_decode_usec / (double)(count));
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
int ret = SRSRAN_ERROR;
parse_args(argc, argv);
random_gen = srsran_random_init(1234);
srate_hz = (double)SRSRAN_SUBC_SPACING_NR(carrier_scs) * srsran_min_symbol_sz_rb(carrier_nof_prb);
hf_len = (uint32_t)ceil(srate_hz * (5.0 / 1000.0));
buffer = srsran_vec_cf_malloc(hf_len);
srsran_ssb_t ssb = {};
srsran_ssb_args_t ssb_args = {};
ssb_args.enable_encode = true;
ssb_args.enable_decode = true;
if (buffer == NULL) {
ERROR("Malloc");
goto clean_exit;
}
if (srsran_channel_awgn_init(&awgn, 0x0) < SRSRAN_SUCCESS) {
ERROR("AWGN");
goto clean_exit;
}
if (srsran_channel_awgn_set_n0(&awgn, n0_dB) < SRSRAN_SUCCESS) {
ERROR("AWGN");
goto clean_exit;
}
if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) {
ERROR("Init");
goto clean_exit;
}
if (test_case_1(&ssb) != SRSRAN_SUCCESS) {
ERROR("test case failed");
}
ret = SRSRAN_SUCCESS;
clean_exit:
srsran_random_free(random_gen);
srsran_ssb_free(&ssb);
srsran_channel_awgn_free(&awgn);
if (buffer) {
free(buffer);
}
return ret;
}

@ -100,6 +100,7 @@ static int assert_measure(const srsran_csi_trs_measurements_t* meas)
static int test_case_1(srsran_ssb_t* ssb)
{
// For benchmarking purposes
uint64_t t_add_usec = 0;
uint64_t t_find_usec = 0;
uint64_t t_meas_usec = 0;
@ -110,7 +111,6 @@ static int test_case_1(srsran_ssb_t* ssb)
ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3;
ssb_cfg.scs = ssb_scs;
ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C;
ssb_cfg.position[0] = true; // Rest to false
TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS);
@ -124,7 +124,11 @@ static int test_case_1(srsran_ssb_t* ssb)
srsran_vec_cf_zero(buffer, sf_len);
// Add the SSB base-band
TESTASSERT(srsran_ssb_add(ssb, pci, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS);
gettimeofday(&t[1], NULL);
TESTASSERT(srsran_ssb_add(ssb, pci, 0, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_add_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Run channel
run_channel();
@ -149,7 +153,7 @@ static int test_case_1(srsran_ssb_t* ssb)
// Measure
gettimeofday(&t[1], NULL);
srsran_csi_trs_measurements_t meas = {};
TESTASSERT(srsran_ssb_csi_measure(ssb, pci, buffer, &meas) == SRSRAN_SUCCESS);
TESTASSERT(srsran_ssb_csi_measure(ssb, pci, 0, buffer, &meas) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_meas_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
@ -161,7 +165,8 @@ static int test_case_1(srsran_ssb_t* ssb)
TESTASSERT(assert_measure(&meas) == SRSRAN_SUCCESS);
}
INFO("test_case_1 - %.1f usec/search; Max srate %.1f MSps; %.1f usec/measurement",
INFO("test_case_1 - %.1f usec/encode; %.1f usec/search; Max srate %.1f MSps; %.1f usec/measurement",
(double)t_add_usec / (double)SRSRAN_NOF_NID_NR,
(double)t_find_usec / (double)SRSRAN_NOF_NID_NR,
(double)sf_len * (double)SRSRAN_NOF_NID_NR / (double)t_find_usec,
(double)t_meas_usec / (double)SRSRAN_NOF_NID_NR);

@ -449,8 +449,16 @@ static int dci_blind_search(srsran_ue_dl_t* q,
return SRSRAN_ERROR;
}
// Check if RNTI is matched
if ((dci_msg[nof_dci].rnti == rnti) && (dci_msg[nof_dci].nof_bits > 0)) {
dci_msg[nof_dci].rnti = rnti;
// Compute decoded message correlation to drastically reduce false alarm probability
float corr = srsran_pdcch_msg_corr(&q->pdcch, &dci_msg[nof_dci]);
// Skip candidate if the threshold is not reached
// 0.5 is set from pdcch_test
if (!isnormal(corr) || corr < 0.5f) {
continue;
}
// Look for the messages found and apply the new format if the location is common
if (search_in_common && (dci_cfg->multiple_csi_request_enabled || dci_cfg->srs_request_enabled)) {
@ -461,8 +469,8 @@ static int dci_blind_search(srsran_ue_dl_t* q,
* that only the PDCCH in the common search space is transmitted by the primary cell.
*/
// Find a matching ncce in the common SS
if (srsran_location_find_ncce(
q->current_ss_common.loc, q->current_ss_common.nof_locations, dci_msg[nof_dci].location.ncce)) {
if (srsran_location_find_location(
q->current_ss_common.loc, q->current_ss_common.nof_locations, &dci_msg[nof_dci].location)) {
srsran_dci_cfg_t cfg = *dci_cfg;
srsran_dci_cfg_set_common_ss(&cfg);
// if the payload size is the same that it would have in the common SS (only Format0/1A is allowed there)

@ -582,11 +582,11 @@ int srsran_ue_dl_nr_decode_pdsch(srsran_ue_dl_nr_t* q,
return SRSRAN_SUCCESS;
}
int srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q,
const srsran_sch_cfg_nr_t* cfg,
const srsran_pdsch_res_nr_t* res,
char* str,
uint32_t str_len)
int srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q,
const srsran_sch_cfg_nr_t* cfg,
const srsran_pdsch_res_nr_t res[SRSRAN_MAX_CODEWORDS],
char* str,
uint32_t str_len)
{
int len = 0;

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

Loading…
Cancel
Save