mirror of https://github.com/pvnis/srsRAN_4G.git
Merge branch 'next' into agpl_next
commit
d1d38d1826
@ -0,0 +1,396 @@
|
||||
/**
|
||||
*
|
||||
* \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_OPTIONAL_ARRAY_H
|
||||
#define SRSRAN_OPTIONAL_ARRAY_H
|
||||
|
||||
#include "optional.h"
|
||||
#include "span.h"
|
||||
#include "srsran/support/srsran_assert.h"
|
||||
#include <array>
|
||||
|
||||
namespace srsran {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Vec>
|
||||
class base_optional_span
|
||||
{
|
||||
using base_t = base_optional_span<Vec>;
|
||||
using T = typename Vec::value_type::value_type;
|
||||
|
||||
protected:
|
||||
template <typename Obj>
|
||||
class iterator_impl
|
||||
{
|
||||
using It = iterator_impl<Obj>;
|
||||
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = Obj;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = Obj*;
|
||||
using reference = Obj&;
|
||||
|
||||
iterator_impl() = default;
|
||||
iterator_impl(base_t* parent_, size_t idx_) : parent(parent_), idx(idx_)
|
||||
{
|
||||
if (idx < parent->vec.size() and not parent->contains(idx)) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
It& operator++()
|
||||
{
|
||||
while (++idx < parent->vec.size() and not parent->contains(idx)) {
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
It& operator--()
|
||||
{
|
||||
while (--idx < parent->vec.size() and not parent->contains(idx)) {
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference operator*() { return parent->operator[](idx); }
|
||||
pointer operator->() { return &parent->operator[](idx); }
|
||||
|
||||
bool operator==(const It& other) const { return idx == other.idx and parent == other.parent; }
|
||||
bool operator!=(const It& other) const { return not(*this == other); }
|
||||
|
||||
private:
|
||||
friend base_t;
|
||||
|
||||
base_t* parent = nullptr;
|
||||
size_t idx = std::numeric_limits<size_t>::max();
|
||||
};
|
||||
|
||||
size_t nof_elems = 0;
|
||||
Vec vec;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using iterator = iterator_impl<T>;
|
||||
using const_iterator = iterator_impl<const T>;
|
||||
|
||||
// Find first position that is empty
|
||||
size_t find_first_empty(size_t start_guess = 0)
|
||||
{
|
||||
if (nof_elems == vec.size()) {
|
||||
return vec.size();
|
||||
}
|
||||
for (size_t i = start_guess; i < vec.size(); ++i) {
|
||||
if (not vec[i].has_value()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return vec.size();
|
||||
}
|
||||
|
||||
bool contains(size_t idx) const { return idx < vec.size() and vec[idx].has_value(); }
|
||||
|
||||
T& operator[](size_t idx) { return *vec[idx]; }
|
||||
const T& operator[](size_t idx) const { return *vec[idx]; }
|
||||
|
||||
bool empty() const { return nof_elems == 0; }
|
||||
size_t size() const { return nof_elems; }
|
||||
|
||||
iterator begin() { return iterator{this, 0}; }
|
||||
iterator end() { return iterator{this, vec.size()}; }
|
||||
const_iterator begin() const { return const_iterator{this, 0}; }
|
||||
const_iterator end() const { return const_iterator{this, vec.size()}; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
this->nof_elems = 0;
|
||||
for (auto& e : *this) {
|
||||
e.reset();
|
||||
}
|
||||
}
|
||||
void erase(size_t idx)
|
||||
{
|
||||
srsran_assert(idx < this->vec.size(), "Out-of-bounds access to array: %zd>=%zd", idx, this->vec.size());
|
||||
if (this->contains(idx)) {
|
||||
this->nof_elems--;
|
||||
this->vec[idx].reset();
|
||||
}
|
||||
}
|
||||
void erase(iterator it) { erase(it.idx); }
|
||||
|
||||
template <typename U>
|
||||
void insert(size_t idx, U&& u)
|
||||
{
|
||||
srsran_assert(idx < this->vec.size(), "Out-of-bounds access to array: %zd>=%zd", idx, this->vec.size());
|
||||
this->nof_elems += this->contains(idx) ? 0 : 1;
|
||||
this->vec[idx] = std::forward<U>(u);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Vec>
|
||||
class base_optional_vector : public base_optional_span<Vec>
|
||||
{
|
||||
using base_t = base_optional_span<Vec>;
|
||||
|
||||
public:
|
||||
using value_type = typename base_optional_span<Vec>::value_type;
|
||||
using iterator = typename base_optional_span<Vec>::iterator;
|
||||
using const_iterator = typename base_optional_span<Vec>::const_iterator;
|
||||
|
||||
base_optional_vector() = default;
|
||||
base_optional_vector(const base_optional_vector&) = default;
|
||||
base_optional_vector(base_optional_vector&& other) noexcept : base_t::vec(std::move(other.vec)),
|
||||
base_t::nof_elems(other.nof_elems)
|
||||
{
|
||||
other.nof_elems = 0;
|
||||
}
|
||||
base_optional_vector& operator=(const base_optional_vector&) = default;
|
||||
base_optional_vector& operator =(base_optional_vector&& other) noexcept
|
||||
{
|
||||
this->vec = std::move(other.vec);
|
||||
this->nof_elems = other.nof_elems;
|
||||
this->nof_elems = 0;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Array of optional items. The iteration is in order of indexes and correctly skips non-present items
|
||||
* Pointer/References/Iterators remain valid throughout the object lifetime
|
||||
* NOTE: The sorted iteration and pointer validation guarantees add some overhead if the array is very fragmented
|
||||
* @tparam T type of objects
|
||||
* @tparam N static size of max nof items
|
||||
*/
|
||||
template <typename T, size_t N>
|
||||
class optional_array : public detail::base_optional_vector<std::array<optional<T>, N> >
|
||||
{};
|
||||
|
||||
/**
|
||||
* Contrarily to optional_array, this class may allocate and cause pointer/reference/iterator invalidation.
|
||||
* However, the indexes will remain valid.
|
||||
* @tparam T
|
||||
*/
|
||||
template <typename T>
|
||||
class optional_vector : public detail::base_optional_vector<std::vector<optional<T> > >
|
||||
{
|
||||
using base_t = detail::base_optional_vector<std::vector<optional<T> > >;
|
||||
|
||||
public:
|
||||
/// May allocate and cause pointer invalidation
|
||||
template <typename U>
|
||||
void insert(size_t idx, U&& u)
|
||||
{
|
||||
if (idx >= this->vec.size()) {
|
||||
this->vec.resize(idx + 1);
|
||||
}
|
||||
base_t::insert(idx, std::forward<U>(u));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class optional_span : public detail::base_optional_span<srsran::span<optional<T> > >
|
||||
{
|
||||
using base_t = detail::base_optional_span<srsran::span<optional<T> > >;
|
||||
|
||||
public:
|
||||
template <size_t N>
|
||||
optional_span(const optional_array<T, N>& ar) : base_t::vec(ar)
|
||||
{}
|
||||
optional_span(const optional_vector<T>& ar) : base_t::vec(ar) {}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
class base_split_optional_span
|
||||
{
|
||||
protected:
|
||||
using presence_type = typename std::conditional<std::is_const<T>::value, const bool, bool>::type;
|
||||
|
||||
T* ptr = nullptr;
|
||||
presence_type* present_ptr = nullptr;
|
||||
size_t len = 0;
|
||||
|
||||
template <typename Obj>
|
||||
class iterator_impl
|
||||
{
|
||||
using It = iterator_impl<Obj>;
|
||||
using Parent = typename std::
|
||||
conditional<std::is_const<Obj>::value, const base_split_optional_span<T>, base_split_optional_span<T> >::type;
|
||||
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = Obj;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = Obj*;
|
||||
using reference = Obj&;
|
||||
|
||||
iterator_impl() = default;
|
||||
iterator_impl(Parent* parent_, size_t idx_) : parent(parent_), idx(idx_)
|
||||
{
|
||||
if (idx < parent->len and not parent->contains(idx)) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
It& operator++()
|
||||
{
|
||||
while (++idx < parent->len and not parent->contains(idx)) {
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
It& operator--()
|
||||
{
|
||||
while (--idx < parent->len and not parent->contains(idx)) {
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference operator*() { return parent->operator[](idx); }
|
||||
pointer operator->() { return &parent->operator[](idx); }
|
||||
|
||||
bool operator==(const It& other) const { return idx == other.idx and parent == other.parent; }
|
||||
bool operator!=(const It& other) const { return not(*this == other); }
|
||||
|
||||
size_t get_idx() const { return idx; }
|
||||
|
||||
private:
|
||||
Parent* parent = nullptr;
|
||||
size_t idx = std::numeric_limits<size_t>::max();
|
||||
};
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using iterator = iterator_impl<T>;
|
||||
using const_iterator = iterator_impl<const T>;
|
||||
|
||||
constexpr base_split_optional_span() = default;
|
||||
template <std::size_t N>
|
||||
constexpr base_split_optional_span(value_type (&arr)[N], presence_type (&present)[N]) noexcept : ptr(arr),
|
||||
present_ptr(present),
|
||||
len(N)
|
||||
{}
|
||||
constexpr base_split_optional_span(value_type* arr, presence_type* present, size_t N) :
|
||||
ptr(arr), present_ptr(present), len(N)
|
||||
{}
|
||||
|
||||
bool contains(size_t idx) const { return idx < len and present_ptr[idx]; }
|
||||
bool empty() const
|
||||
{
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (present_ptr[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
size_t size() const
|
||||
{
|
||||
size_t c = 0;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
c += present_ptr[i] ? 1 : 0;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
size_t capacity() const { return len; }
|
||||
|
||||
const T& operator[](size_t idx) const { return ptr[idx]; }
|
||||
T& operator[](size_t idx) { return ptr[idx]; }
|
||||
const T& at(size_t idx) const
|
||||
{
|
||||
srsran_assert(contains(idx), "Access to inexistent element of index=%zd", idx);
|
||||
return ptr[idx];
|
||||
}
|
||||
T& at(size_t idx)
|
||||
{
|
||||
srsran_assert(this->contains(idx), "Access to inexistent element of index=%zd", idx);
|
||||
return this->ptr[idx];
|
||||
}
|
||||
|
||||
const_iterator begin() const { return const_iterator(this, 0); }
|
||||
const_iterator end() const { return const_iterator(this, len); }
|
||||
iterator begin() { return iterator(this, 0); }
|
||||
iterator end() { return iterator(this, this->len); }
|
||||
|
||||
// Find first position that is empty
|
||||
size_t find_first_empty(size_t start_guess = 0) { return begin().get_idx(); }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
class split_optional_span : public detail::base_split_optional_span<T>
|
||||
{
|
||||
using base_t = detail::base_split_optional_span<T>;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using const_iterator = typename base_t::const_iterator;
|
||||
using iterator = typename base_t::iterator;
|
||||
|
||||
using base_t::base_t;
|
||||
|
||||
template <typename U>
|
||||
void insert(size_t idx, U&& u)
|
||||
{
|
||||
srsran_assert(idx < this->len, "Out-of-bounds access to array: %zd>=%zd", idx, this->len);
|
||||
this->present_ptr[idx] = true;
|
||||
this->ptr[idx] = std::forward<U>(u);
|
||||
}
|
||||
void erase(size_t idx)
|
||||
{
|
||||
srsran_assert(idx < this->len, "Out-of-bounds access to array: %zd>=%zd", idx, this->len);
|
||||
this->present_ptr[idx] = false;
|
||||
}
|
||||
void erase(iterator it) { erase(it.get_idx()); }
|
||||
void clear()
|
||||
{
|
||||
for (size_t i = 0; i < this->len; ++i) {
|
||||
this->present_ptr[i] = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename U>
|
||||
class split_optional_span<const U> : public detail::base_split_optional_span<const U>
|
||||
{
|
||||
using base_t = detail::base_split_optional_span<const U>;
|
||||
using presence_type = typename base_t::presence_type;
|
||||
|
||||
public:
|
||||
using value_type = const U;
|
||||
using const_iterator = typename base_t::const_iterator;
|
||||
|
||||
using base_t::base_t;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
split_optional_span<T>
|
||||
make_optional_span(T* array,
|
||||
typename std::conditional<std::is_const<T>::value, const bool, bool>::type* present,
|
||||
size_t N)
|
||||
{
|
||||
return split_optional_span<T>(array, present, N);
|
||||
}
|
||||
template <typename T, size_t N>
|
||||
split_optional_span<T>
|
||||
make_optional_span(T (&array)[N],
|
||||
typename std::conditional<std::is_const<T>::value, const bool, bool>::type (&present)[N])
|
||||
{
|
||||
return split_optional_span<T>(array, present);
|
||||
}
|
||||
|
||||
} // namespace srsran
|
||||
|
||||
#endif // SRSRAN_OPTIONAL_ARRAY_H
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
*
|
||||
* \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_SRSRAN_TEST_H
|
||||
#define SRSRAN_SRSRAN_TEST_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "srsran_assert.h"
|
||||
|
||||
namespace srsran {
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename U>
|
||||
[[gnu::noinline, noreturn]] void assert_eq_failure(const T& expected_val, const U& actual_val)
|
||||
{
|
||||
std::string s = fmt::format("Actual value '{}' differs from expected '{}'", actual_val, expected_val);
|
||||
srsran_assertion_failure("%s", s.c_str());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[gnu::noinline, noreturn]] void assert_neq_failure(const T& actual_val)
|
||||
{
|
||||
std::string s = fmt::format("Value should not be equal to '{}'", actual_val);
|
||||
srsran_assertion_failure("%s", s.c_str());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace srsran
|
||||
|
||||
#define TESTASSERT_EQ(EXPECTED, ACTUAL) \
|
||||
(void)((EXPECTED == ACTUAL) || (::srsran::detail::assert_eq_failure(EXPECTED, ACTUAL), 0))
|
||||
|
||||
#define TESTASSERT_NEQ(EXPECTED, ACTUAL) \
|
||||
(void)((EXPECTED != ACTUAL) || (::srsran::detail::assert_neq_failure(ACTUAL), 0))
|
||||
|
||||
#define TESTASSERT(cond) srsran_assert((cond), "Fail at \"%s\"", (#cond))
|
||||
|
||||
#define TESTASSERT_SUCCESS(cond) srsran_assert((cond == SRSRAN_SUCCESS), "Operation \"%s\" was not successful", (#cond))
|
||||
|
||||
#else // __cplusplus
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define TESTASSERT(cond) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
printf("[%s][Line %d] Fail at \"%s\"\n", __FUNCTION__, __LINE__, (#cond)); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // SRSRAN_SRSRAN_TEST_H
|
@ -0,0 +1,123 @@
|
||||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2021 Software Radio Systems Limited
|
||||
*
|
||||
* By using this file, you agree to the terms and conditions set
|
||||
* forth in the LICENSE file which can be found at the top level of
|
||||
* the distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "srsran/adt/optional_array.h"
|
||||
#include "srsran/common/test_common.h"
|
||||
|
||||
namespace srsran {
|
||||
|
||||
void test_optional_array()
|
||||
{
|
||||
optional_array<int, 5> table1;
|
||||
TESTASSERT(table1.size() == 0 and table1.empty());
|
||||
|
||||
TESTASSERT(not table1.contains(0));
|
||||
table1.insert(0, 5);
|
||||
TESTASSERT(table1.size() == 1 and not table1.empty());
|
||||
table1.erase(0);
|
||||
TESTASSERT(table1.size() == 0 and table1.empty());
|
||||
table1.insert(1, 3);
|
||||
table1.insert(4, 2);
|
||||
TESTASSERT(table1.size() == 2);
|
||||
TESTASSERT(table1[4] == 2 and table1[1] == 3);
|
||||
|
||||
size_t count = 0;
|
||||
int array[] = {3, 2};
|
||||
for (int e : table1) {
|
||||
TESTASSERT(array[count++] == e);
|
||||
}
|
||||
|
||||
auto it = table1.begin();
|
||||
TESTASSERT(*it == 3);
|
||||
table1.erase(it);
|
||||
TESTASSERT(table1.size() == 1);
|
||||
}
|
||||
|
||||
void test_optional_vector()
|
||||
{
|
||||
optional_vector<int> table1;
|
||||
TESTASSERT(table1.size() == 0 and table1.empty());
|
||||
|
||||
TESTASSERT(not table1.contains(0));
|
||||
table1.insert(0, 5);
|
||||
TESTASSERT(table1.size() == 1 and not table1.empty());
|
||||
table1.erase(0);
|
||||
TESTASSERT(table1.size() == 0 and table1.empty());
|
||||
table1.insert(1, 3);
|
||||
table1.insert(4, 2);
|
||||
TESTASSERT(table1.size() == 2);
|
||||
TESTASSERT(table1[4] == 2 and table1[1] == 3);
|
||||
|
||||
size_t count = 0;
|
||||
int array[] = {3, 2};
|
||||
for (int e : table1) {
|
||||
TESTASSERT(array[count++] == e);
|
||||
}
|
||||
|
||||
auto it = table1.begin();
|
||||
TESTASSERT(*it == 3);
|
||||
table1.erase(it);
|
||||
TESTASSERT(table1.size() == 1);
|
||||
}
|
||||
|
||||
void test_split_optional_span()
|
||||
{
|
||||
constexpr size_t L = 7;
|
||||
int some_list[L] = {};
|
||||
bool some_list_presence[L] = {};
|
||||
split_optional_span<int> view(some_list, some_list_presence, L);
|
||||
|
||||
TESTASSERT(view.size() == 0 and view.empty());
|
||||
TESTASSERT(view.begin() == view.end());
|
||||
TESTASSERT(not view.contains(0));
|
||||
TESTASSERT(view.find_first_empty() == L);
|
||||
|
||||
view.insert(1, 1);
|
||||
TESTASSERT(view.size() == 1 and not view.empty());
|
||||
TESTASSERT(view.begin() != view.end() and *view.begin() == 1);
|
||||
TESTASSERT(view.contains(1));
|
||||
TESTASSERT(view[1] == 1);
|
||||
TESTASSERT(view.find_first_empty() == 1);
|
||||
|
||||
view.insert(3, 3);
|
||||
TESTASSERT(view[3] == 3);
|
||||
size_t c = 0;
|
||||
for (auto& e : view) {
|
||||
TESTASSERT(c == 0 ? e == 1 : e == 3);
|
||||
c++;
|
||||
}
|
||||
TESTASSERT(view.size() == 2);
|
||||
|
||||
view.erase(view.begin());
|
||||
TESTASSERT(view.size() == 1);
|
||||
TESTASSERT(not view.contains(1) and view.contains(3));
|
||||
|
||||
view.clear();
|
||||
TESTASSERT(view.empty());
|
||||
}
|
||||
|
||||
} // namespace srsran
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
auto& test_log = srslog::fetch_basic_logger("TEST");
|
||||
test_log.set_level(srslog::basic_levels::info);
|
||||
|
||||
srsran::test_init(argc, argv);
|
||||
|
||||
srsran::test_optional_array();
|
||||
srsran::test_optional_vector();
|
||||
srsran::test_split_optional_span();
|
||||
|
||||
printf("Success\n");
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2021 Software Radio Systems Limited
|
||||
*
|
||||
* By using this file, you agree to the terms and conditions set
|
||||
* forth in the LICENSE file which can be found at the top level of
|
||||
* the distribution.
|
||||
*
|
||||
*/
|
||||
#ifndef SRSRAN_RRC_RLF_REPORT_H_
|
||||
#define SRSRAN_RRC_RLF_REPORT_H_
|
||||
|
||||
#include "rrc_cell.h"
|
||||
#include "srsran/asn1/rrc.h"
|
||||
#include "srsran/common/common.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
using namespace asn1::rrc;
|
||||
|
||||
// RRC RLF-Report class
|
||||
class rrc_rlf_report
|
||||
{
|
||||
public:
|
||||
enum failure_type_t { rlf, hof };
|
||||
|
||||
void init(srsran::task_sched_handle task_sched);
|
||||
|
||||
// Returns true if VarRLF-Report structure has info available
|
||||
bool has_info();
|
||||
|
||||
// Called upon T304 expiry (type == hof) or Detection of radio link failure (type == rlf)
|
||||
void set_failure(meas_cell_list<meas_cell_eutra>& meas_cells, failure_type_t type);
|
||||
|
||||
// Called upon transmission of ReestablishmentRequest message
|
||||
void set_reest_gci(const asn1::fixed_bitstring<28>& gci, const asn1::rrc::plmn_id_s& plmn_id);
|
||||
|
||||
// Called upon initiation of RadioReconfiguration message including MobilityInfo IE
|
||||
void received_ho_command(const asn1::fixed_bitstring<28>& current_gci);
|
||||
|
||||
// Returns a copy of the rlf_report_r9 ASN1 struct
|
||||
rlf_report_r9_s get_report();
|
||||
|
||||
// Clears VarRLF-Report contents
|
||||
void clear();
|
||||
|
||||
private:
|
||||
asn1::fixed_bitstring<28> ho_gci;
|
||||
|
||||
bool has_event = false;
|
||||
rlf_report_r9_s rlf_report = {};
|
||||
srsran::timer_handler::unique_timer timer_conn_failure = {};
|
||||
};
|
||||
} // namespace srsue
|
||||
|
||||
#endif // SRSRAN_RRC_RLF_REPORT_H_
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
*
|
||||
* \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 "srsue/hdr/stack/rrc/rrc_common.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
uint8_t rrc_value_to_range(quant_s quant, const float value)
|
||||
{
|
||||
uint8_t range = 0;
|
||||
if (quant == quant_rsrp) {
|
||||
if (value < -140) {
|
||||
range = 0;
|
||||
} else if (value < -44) {
|
||||
range = 1u + (uint8_t)(value + 140);
|
||||
} else {
|
||||
range = 97;
|
||||
}
|
||||
} else {
|
||||
if (value < -19.5) {
|
||||
range = 0;
|
||||
} else if (value < -3) {
|
||||
range = 1u + (uint8_t)(2 * (value + 19.5));
|
||||
} else {
|
||||
range = 34;
|
||||
}
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
float rrc_range_to_value(quant_s quant, const uint8_t range)
|
||||
{
|
||||
float val = 0;
|
||||
if (quant == quant_rsrp) {
|
||||
val = -140 + (float)range;
|
||||
} else {
|
||||
val = -19.5f + (float)range / 2;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
} // namespace srsue
|
@ -0,0 +1,136 @@
|
||||
/**
|
||||
*
|
||||
* \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 "srsue/hdr/stack/rrc/rrc_rlf_report.h"
|
||||
#include "srsue/hdr/stack/rrc/rrc_common.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
void rrc_rlf_report::init(srsran::task_sched_handle task_sched)
|
||||
{
|
||||
timer_conn_failure = task_sched.get_unique_timer();
|
||||
}
|
||||
|
||||
// Returns true if VarRLF-Report structure has info available
|
||||
bool rrc_rlf_report::has_info()
|
||||
{
|
||||
return has_event;
|
||||
}
|
||||
|
||||
// Called upon T304 expiry (type == hof) or Detection of radio link failure (type == rlf)
|
||||
void rrc_rlf_report::set_failure(meas_cell_list<meas_cell_eutra>& meas_cells, failure_type_t type)
|
||||
{
|
||||
has_event = true;
|
||||
|
||||
// clear the information included in VarRLF-Report, if any
|
||||
rlf_report = {};
|
||||
|
||||
// set the plmn-Identity to the RPLMN
|
||||
|
||||
// set the measResultLastServCell to include the RSRP and RSRQ, if available, of the PCell based on
|
||||
// measurements collected up to the moment the UE detected radio link failure
|
||||
rlf_report.meas_result_last_serv_cell_r9.rsrp_result_r9 =
|
||||
rrc_value_to_range(quant_rsrp, meas_cells.serving_cell().get_rsrp());
|
||||
rlf_report.meas_result_last_serv_cell_r9.rsrq_result_r9 =
|
||||
rrc_value_to_range(quant_rsrq, meas_cells.serving_cell().get_rsrq());
|
||||
rlf_report.meas_result_last_serv_cell_r9.rsrq_result_r9_present = true;
|
||||
|
||||
// set the measResultNeighCells to include the best measured cells, other than the PCell, ordered such that
|
||||
// the best cell is listed first, and based on measurements collected up to the moment the UE detected radio
|
||||
// link failure
|
||||
if (meas_cells.nof_neighbours() > 0) {
|
||||
rlf_report.meas_result_neigh_cells_r9_present = true;
|
||||
rlf_report.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present = true;
|
||||
rlf_report.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.clear();
|
||||
meas_cells.sort_neighbour_cells();
|
||||
// It is not clear how the sorting and grouping of cells per frequency must be done.
|
||||
// We use a separate MeasResultList2EUTRA-r9 struct for each pci/frequency pair
|
||||
for (const auto& f : meas_cells) {
|
||||
meas_result2_eutra_r9_s meas2 = {};
|
||||
meas2.carrier_freq_r9 = f->get_earfcn();
|
||||
meas_result_eutra_s meas = {};
|
||||
meas.pci = f->get_pci();
|
||||
meas.meas_result.rsrp_result_present = true;
|
||||
meas.meas_result.rsrq_result_present = true;
|
||||
meas.meas_result.rsrp_result = rrc_value_to_range(quant_rsrp, f->get_rsrp());
|
||||
meas.meas_result.rsrq_result = rrc_value_to_range(quant_rsrq, f->get_rsrq());
|
||||
meas2.meas_result_list_r9.push_back(meas);
|
||||
rlf_report.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.push_back(meas2);
|
||||
}
|
||||
}
|
||||
|
||||
// set the failedPCellId to the global cell identity, if available, and otherwise to the physical cell identity and
|
||||
// carrier frequency of the PCell where radio link failure is detected;
|
||||
rlf_report.failed_pcell_id_r10.set_present(true);
|
||||
if (meas_cells.serving_cell().has_sib1()) {
|
||||
rlf_report.failed_pcell_id_r10->set_cell_global_id_r10().cell_id = meas_cells.serving_cell().get_cell_id_bit();
|
||||
rlf_report.failed_pcell_id_r10->cell_global_id_r10().plmn_id = meas_cells.serving_cell().get_plmn_asn1(0);
|
||||
} else {
|
||||
rlf_report.failed_pcell_id_r10->set_pci_arfcn_r10();
|
||||
rlf_report.failed_pcell_id_r10->pci_arfcn_r10().pci_r10 = meas_cells.serving_cell().get_pci();
|
||||
rlf_report.failed_pcell_id_r10->pci_arfcn_r10().carrier_freq_r10 = meas_cells.serving_cell().get_earfcn();
|
||||
}
|
||||
|
||||
// if an RRCConnectionReconfiguration message including the mobilityControlInfo was received before the
|
||||
// connection failure
|
||||
if (timer_conn_failure.is_running()) {
|
||||
timer_conn_failure.stop();
|
||||
|
||||
// include previousPCellId and set it to the global cell identity of the PCell where the last
|
||||
// RRCConnectionReconfiguration including the mobilityControlInfo message was received;
|
||||
rlf_report.prev_pcell_id_r10.set_present(true);
|
||||
rlf_report.prev_pcell_id_r10->cell_id = ho_gci;
|
||||
rlf_report.prev_pcell_id_r10->plmn_id = meas_cells.serving_cell().get_plmn_asn1(0);
|
||||
|
||||
// set the timeConnFailure to the elapsed time since reception of the last
|
||||
// RRCConnectionReconfiguration message including the mobilityControlInfo;
|
||||
rlf_report.time_conn_fail_r10_present = true;
|
||||
rlf_report.time_conn_fail_r10 = timer_conn_failure.time_elapsed() / 100; // 1 unit = 100 ms
|
||||
}
|
||||
|
||||
// set the connectionFailureType
|
||||
rlf_report.conn_fail_type_r10_present = true;
|
||||
rlf_report.conn_fail_type_r10 =
|
||||
type == rlf ? rlf_report_r9_s::conn_fail_type_r10_opts::rlf : rlf_report_r9_s::conn_fail_type_r10_opts::hof;
|
||||
|
||||
rlf_report.ext = true;
|
||||
}
|
||||
|
||||
void rrc_rlf_report::set_reest_gci(const asn1::fixed_bitstring<28>& gci, const asn1::rrc::plmn_id_s& plmn_id)
|
||||
{
|
||||
rlf_report.reest_cell_id_r10.set_present(true);
|
||||
rlf_report.reest_cell_id_r10->cell_id = gci;
|
||||
rlf_report.reest_cell_id_r10->plmn_id = plmn_id;
|
||||
}
|
||||
|
||||
void rrc_rlf_report::received_ho_command(const asn1::fixed_bitstring<28>& current_gci)
|
||||
{
|
||||
if (timer_conn_failure.is_valid()) {
|
||||
timer_conn_failure.stop();
|
||||
timer_conn_failure.run();
|
||||
ho_gci = current_gci;
|
||||
}
|
||||
}
|
||||
|
||||
rlf_report_r9_s rrc_rlf_report::get_report()
|
||||
{
|
||||
return rlf_report;
|
||||
}
|
||||
|
||||
// Clears VarRLF-Report contents
|
||||
void rrc_rlf_report::clear()
|
||||
{
|
||||
has_event = false;
|
||||
rlf_report = {};
|
||||
}
|
||||
|
||||
} // namespace srsue
|
@ -0,0 +1,196 @@
|
||||
/**
|
||||
*
|
||||
* \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 "srsue/hdr/stack/rrc/rrc_cell.h"
|
||||
#include "srsue/hdr/stack/rrc/rrc_rlf_report.h"
|
||||
|
||||
using namespace srsue;
|
||||
|
||||
int test_single()
|
||||
{
|
||||
srsran::task_scheduler task_sched;
|
||||
rrc_rlf_report rlf_report;
|
||||
meas_cell_list<meas_cell_eutra> list{&task_sched};
|
||||
|
||||
phy_meas_t pmeas{};
|
||||
pmeas.rsrp = -20;
|
||||
pmeas.pci = 1;
|
||||
pmeas.earfcn = 3400;
|
||||
pmeas.rsrq = -10;
|
||||
|
||||
list.add_meas_cell(pmeas);
|
||||
list.set_serving_cell(phy_cell_t{1, 3400}, false);
|
||||
|
||||
rlf_report.set_failure(list, rrc_rlf_report::rlf);
|
||||
|
||||
asn1::json_writer jw;
|
||||
asn1::rrc::rlf_report_r9_s out = rlf_report.get_report();
|
||||
out.to_json(jw);
|
||||
printf("test_single: %s\n", jw.to_string().c_str());
|
||||
|
||||
TESTASSERT(!out.meas_result_neigh_cells_r9_present);
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.size() == 0);
|
||||
TESTASSERT(out.failed_pcell_id_r10.is_present());
|
||||
TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().pci_r10 == 1);
|
||||
TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().carrier_freq_r10 = 3400);
|
||||
TESTASSERT(out.conn_fail_type_r10_present);
|
||||
TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::rlf);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int test_neighbours()
|
||||
{
|
||||
srsran::task_scheduler task_sched;
|
||||
rrc_rlf_report rlf_report;
|
||||
meas_cell_list<meas_cell_eutra> list{&task_sched};
|
||||
|
||||
phy_meas_t pmeas{};
|
||||
pmeas.rsrp = -20;
|
||||
pmeas.pci = 1;
|
||||
pmeas.earfcn = 3400;
|
||||
pmeas.rsrq = -10;
|
||||
|
||||
list.add_meas_cell(pmeas);
|
||||
pmeas.pci = 4;
|
||||
list.add_meas_cell(pmeas);
|
||||
pmeas.pci = 6;
|
||||
list.add_meas_cell(pmeas);
|
||||
list.set_serving_cell(phy_cell_t{4, 3400}, false);
|
||||
|
||||
TESTASSERT(!rlf_report.has_info());
|
||||
|
||||
rlf_report.set_failure(list, rrc_rlf_report::hof);
|
||||
|
||||
asn1::json_writer jw;
|
||||
asn1::rrc::rlf_report_r9_s out = rlf_report.get_report();
|
||||
out.to_json(jw);
|
||||
printf("test_neighbours: %s\n", jw.to_string().c_str());
|
||||
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9_present);
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present);
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.size() == 2);
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[0].carrier_freq_r9 = 3400);
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[0].meas_result_list_r9[0].pci == 1);
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[1].carrier_freq_r9 = 3400);
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[1].meas_result_list_r9[0].pci == 6);
|
||||
TESTASSERT(out.failed_pcell_id_r10.is_present());
|
||||
TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().pci_r10 == 4);
|
||||
TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().carrier_freq_r10 = 3400);
|
||||
TESTASSERT(out.conn_fail_type_r10_present);
|
||||
TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::hof);
|
||||
|
||||
TESTASSERT(rlf_report.has_info());
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int test_reest()
|
||||
{
|
||||
srsran::task_scheduler task_sched;
|
||||
rrc_rlf_report rlf_report;
|
||||
meas_cell_list<meas_cell_eutra> list{&task_sched};
|
||||
|
||||
phy_meas_t pmeas{};
|
||||
pmeas.rsrp = -20;
|
||||
pmeas.pci = 1;
|
||||
pmeas.earfcn = 3400;
|
||||
pmeas.rsrq = -10;
|
||||
|
||||
list.add_meas_cell(pmeas);
|
||||
pmeas.pci = 4;
|
||||
list.add_meas_cell(pmeas);
|
||||
pmeas.pci = 6;
|
||||
list.add_meas_cell(pmeas);
|
||||
list.set_serving_cell(phy_cell_t{4, 3400}, false);
|
||||
|
||||
TESTASSERT(!rlf_report.has_info());
|
||||
|
||||
rlf_report.set_failure(list, rrc_rlf_report::hof);
|
||||
rlf_report.set_reest_gci(list.serving_cell().get_cell_id_bit(), list.serving_cell().get_plmn_asn1(0));
|
||||
|
||||
asn1::json_writer jw;
|
||||
asn1::rrc::rlf_report_r9_s out = rlf_report.get_report();
|
||||
out.to_json(jw);
|
||||
printf("test_reest: %s\n", jw.to_string().c_str());
|
||||
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9_present);
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present);
|
||||
TESTASSERT(out.failed_pcell_id_r10.is_present());
|
||||
TESTASSERT(out.conn_fail_type_r10_present);
|
||||
TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::hof);
|
||||
TESTASSERT(out.reest_cell_id_r10.is_present());
|
||||
|
||||
TESTASSERT(rlf_report.has_info());
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int test_ho()
|
||||
{
|
||||
srsran::task_scheduler task_sched;
|
||||
rrc_rlf_report rlf_report;
|
||||
meas_cell_list<meas_cell_eutra> list{&task_sched};
|
||||
|
||||
rlf_report.init(&task_sched);
|
||||
|
||||
phy_meas_t pmeas{};
|
||||
pmeas.rsrp = -20;
|
||||
pmeas.pci = 1;
|
||||
pmeas.earfcn = 3400;
|
||||
pmeas.rsrq = -10;
|
||||
|
||||
list.add_meas_cell(pmeas);
|
||||
pmeas.pci = 4;
|
||||
list.add_meas_cell(pmeas);
|
||||
pmeas.pci = 6;
|
||||
list.add_meas_cell(pmeas);
|
||||
list.set_serving_cell(phy_cell_t{4, 3400}, false);
|
||||
|
||||
TESTASSERT(!rlf_report.has_info());
|
||||
|
||||
rlf_report.received_ho_command(list.serving_cell().get_cell_id_bit());
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
task_sched.tic();
|
||||
task_sched.run_pending_tasks();
|
||||
}
|
||||
rlf_report.set_failure(list, rrc_rlf_report::hof);
|
||||
rlf_report.set_reest_gci(list.serving_cell().get_cell_id_bit(), list.serving_cell().get_plmn_asn1(0));
|
||||
|
||||
asn1::json_writer jw;
|
||||
asn1::rrc::rlf_report_r9_s out = rlf_report.get_report();
|
||||
out.to_json(jw);
|
||||
printf("test_ho: %s\n", jw.to_string().c_str());
|
||||
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9_present);
|
||||
TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present);
|
||||
TESTASSERT(out.failed_pcell_id_r10.is_present());
|
||||
TESTASSERT(out.conn_fail_type_r10_present);
|
||||
TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::hof);
|
||||
TESTASSERT(out.reest_cell_id_r10.is_present());
|
||||
|
||||
TESTASSERT(rlf_report.has_info());
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
TESTASSERT(test_single() == SRSRAN_SUCCESS);
|
||||
TESTASSERT(test_neighbours() == SRSRAN_SUCCESS);
|
||||
TESTASSERT(test_reest() == SRSRAN_SUCCESS);
|
||||
TESTASSERT(test_ho() == SRSRAN_SUCCESS);
|
||||
printf("Success\n");
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue