added logic to insert cells from conf file into var_meas_cfg and calculate diffs between meas_objs

This commit is contained in:
Francisco Paisana 2019-10-30 20:13:53 +00:00
parent f4a0bebe1f
commit b7559171b7
6 changed files with 541 additions and 8 deletions

View File

@ -191,6 +191,13 @@ public:
T* data() { return &data_[0]; }
const T* data() const { return &data_[0]; }
T* begin() { return &data_[0]; }
T* end() { return &data_[size()]; }
const T* begin() const { return &data_[0]; }
const T* end() const { return &data_[size()]; }
using iterator = T*;
using const_iterator = T*;
private:
T* data_ = nullptr;
uint32_t size_ = 0;

View File

@ -29,6 +29,7 @@
#include "srslte/common/buffer_pool.h"
#include "srslte/common/common.h"
#include "srslte/common/log.h"
#include "srslte/common/stack_procedure.h"
#include "srslte/common/threads.h"
#include "srslte/common/timeout.h"
#include "srslte/interfaces/enb_interfaces.h"

View File

@ -27,11 +27,43 @@
namespace srsenb {
/**
* This class is responsible for storing the UE Measurement Configuration at the eNB side.
* Has the same fields as asn1::rrc::var_meas_cfg but stored in data structs that are easier to handle
*/
class var_meas_cfg_t
{
public:
explicit var_meas_cfg_t(srslte::log* log_) : rrc_log(log_) {}
using meas_cell_t = asn1::rrc::cells_to_add_mod_s;
using meas_id_t = asn1::rrc::meas_id_to_add_mod_s;
using meas_obj_t = asn1::rrc::meas_obj_to_add_mod_s;
using report_cfg_t = asn1::rrc::report_cfg_to_add_mod_s;
std::tuple<bool, meas_obj_t*, meas_cell_t*> add_cell_cfg(const meas_cell_cfg_t& cellcfg);
uint32_t get_new_obj_id();
void compute_diff_meas_cfg(const var_meas_cfg_t& target_cfg, asn1::rrc::meas_cfg_s* meas_cfg);
void compute_diff_meas_objs(const var_meas_cfg_t& target_cfg, asn1::rrc::meas_cfg_s* meas_cfg);
void compute_diff_cells(const asn1::rrc::meas_obj_eutra_s& target_it,
asn1::rrc::meas_obj_eutra_s& src_it,
asn1::rrc::meas_obj_to_add_mod_s* added_obj);
// getters
const asn1::rrc::meas_obj_to_add_mod_list_l& meas_objs() const { return var_meas.meas_obj_list; }
private:
asn1::rrc::var_meas_cfg_s var_meas;
srslte::log* rrc_log = nullptr;
};
class rrc::mobility_cfg
{
public:
explicit mobility_cfg(rrc* outer_rrc);
var_meas_cfg_t current_meas_cfg;
private:
rrc* rrc_enb = nullptr;
};
@ -39,14 +71,29 @@ private:
class rrc::ue::rrc_mobility
{
public:
rrc_mobility(srsenb::rrc::ue* outer_ue);
explicit rrc_mobility(srsenb::rrc::ue* outer_ue);
bool fill_conn_recfg_msg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg);
private:
rrc::ue* rrc_ue;
rrc* rrc_enb;
rrc::mobility_cfg* cfg;
srslte::byte_buffer_pool* pool;
srslte::log* rrc_log;
rrc::ue* rrc_ue = nullptr;
rrc* rrc_enb = nullptr;
rrc::mobility_cfg* cfg = nullptr;
srslte::byte_buffer_pool* pool = nullptr;
srslte::log* rrc_log = nullptr;
// vars
var_meas_cfg_t ue_var_meas;
class mobility_proc_t
{
public:
srslte::proc_outcome_t init() { return srslte::proc_outcome_t::yield; }
srslte::proc_outcome_t step() { return srslte::proc_outcome_t::yield; }
private:
enum class state_t { ho_started };
};
srslte::proc_t<mobility_proc_t> mobility_proc;
};
} // namespace srsenb

View File

@ -25,14 +25,346 @@
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <functional>
namespace srsenb {
#define Info(fmt, ...) rrc_log->info("Mobility: " fmt, ##__VA_ARGS__)
using namespace asn1::rrc;
namespace rrc_details {
//! extract cell id from ECI
uint32_t eci_to_cellid(uint32_t eci)
{
return eci & 0xFFu;
}
//! cell comparison based on content
bool cells_are_equal(const cells_to_add_mod_s& lhs, const cells_to_add_mod_s& rhs)
{
return lhs.cell_idx == rhs.cell_idx and lhs.pci == rhs.pci and
lhs.cell_individual_offset == rhs.cell_individual_offset;
}
//! meas field comparison based on ID solely
template <typename T, typename IdType, IdType T::*field>
struct field_id_cmp {
bool operator()(const T& lhs, const T& rhs) const { return lhs.*field < rhs.*field; }
bool operator()(const T& lhs, IdType id) const { return lhs.*field < id; }
};
using cell_id_cmp = field_id_cmp<cells_to_add_mod_s, uint8_t, &cells_to_add_mod_s::cell_idx>;
using meas_obj_id_cmp = field_id_cmp<meas_obj_to_add_mod_s, uint8_t, &meas_obj_to_add_mod_s::meas_obj_id>;
//! Find MeasObj with same earfcn
meas_obj_to_add_mod_s* find_meas_obj(meas_obj_to_add_mod_list_l& l, uint32_t earfcn)
{
auto same_earfcn = [earfcn](const meas_obj_to_add_mod_s& obj) {
return obj.meas_obj.type().value == meas_obj_to_add_mod_s::meas_obj_c_::types_opts::meas_obj_eutra and
obj.meas_obj.meas_obj_eutra().carrier_freq == earfcn;
};
auto it = std::find_if(l.begin(), l.end(), same_earfcn);
if (it == l.end()) {
return nullptr;
}
return it;
}
/** Finds a cell in this->objects based on cell_id and frequency
* return pair of (meas_obj,cell_obj). If no cell has frequency==earfcn, meas_obj=nullptr
*/
std::pair<meas_obj_to_add_mod_s*, cells_to_add_mod_s*>
find_cell(meas_obj_to_add_mod_list_l& l, uint32_t earfcn, uint8_t cell_id)
{
// find meas_obj with same earfcn
meas_obj_to_add_mod_s* obj = rrc_details::find_meas_obj(l, earfcn);
if (obj == nullptr) {
return {nullptr, nullptr};
}
// find cell with same id
auto& cells = obj->meas_obj.meas_obj_eutra().cells_to_add_mod_list;
auto it = std::lower_bound(cells.begin(), cells.end(), cell_id, rrc_details::cell_id_cmp{});
if (it == cells.end() or it->cell_idx != cell_id) {
return {obj, nullptr};
}
return {obj, it};
}
//! Adds Cell to MeasCfg MeasObjToAddMod field
cells_to_add_mod_s* meascfg_add_cell(meas_obj_eutra_s& eutra_obj, const cells_to_add_mod_s& celltoadd)
{
// create new cell_id in the provided eutra_obj.
// if the cell_id already exists, just update the fields.
// find the cell.
auto& l = eutra_obj.cells_to_add_mod_list;
auto found_it = std::lower_bound(l.begin(), l.end(), celltoadd.cell_idx, rrc_details::cell_id_cmp{});
if (found_it == l.end()) {
eutra_obj.cells_to_add_mod_list.push_back({});
found_it = &eutra_obj.cells_to_add_mod_list.back();
}
*found_it = celltoadd;
// cell_obj->cell_idx = (uint8_t)(cell_id % 3); // 0-3 // FIXME: What?
// printf("The added cell has idx=%d, pci=%d\n", cell_obj->cell_idx, cell_obj->pci);
return found_it;
}
/**
* Adds MeasObjtoAddMod to MeasCfg object
*/
meas_obj_to_add_mod_s*
meascfg_add_meas_obj(meas_cfg_s* meas_cfg, const meas_obj_to_add_mod_s& meas_obj, bool add_cells_flag)
{
meas_cfg->meas_obj_to_add_mod_list_present = true;
meas_obj_to_add_mod_list_l& l = meas_cfg->meas_obj_to_add_mod_list;
// search for meas_obj by obj_id to ensure uniqueness (assume sorted)
auto found_it = std::lower_bound(l.begin(), l.end(), meas_obj.meas_obj_id, meas_obj_id_cmp{});
// TODO: Assert dl_earfcn is the same
if (found_it == l.end()) {
l.push_back({});
found_it = &l.back();
found_it->meas_obj_id = meas_obj.meas_obj_id;
}
auto& target_eutra = found_it->meas_obj.set_meas_obj_eutra();
auto& src_eutra = meas_obj.meas_obj.meas_obj_eutra();
target_eutra.carrier_freq = src_eutra.carrier_freq;
target_eutra.offset_freq_present = src_eutra.offset_freq_present;
target_eutra.offset_freq = src_eutra.offset_freq;
target_eutra.allowed_meas_bw = src_eutra.allowed_meas_bw;
target_eutra.presence_ant_port1 = src_eutra.presence_ant_port1;
target_eutra.neigh_cell_cfg = src_eutra.neigh_cell_cfg;
if (add_cells_flag) {
for (const cells_to_add_mod_s& cell_it : src_eutra.cells_to_add_mod_list) {
rrc_details::meascfg_add_cell(target_eutra, cell_it);
}
}
return found_it;
}
//! Find difference between MeasObjs
bool meas_objs_are_equal(const meas_obj_to_add_mod_s& lhs, const meas_obj_to_add_mod_s& rhs)
{
if (lhs.meas_obj_id != rhs.meas_obj_id or lhs.meas_obj.type() != lhs.meas_obj.type()) {
return false;
}
auto &lhs_eutra = lhs.meas_obj.meas_obj_eutra(), &rhs_eutra = rhs.meas_obj.meas_obj_eutra();
if (lhs_eutra.ext or rhs_eutra.ext) {
printf("[%d] extension of measObjToAddMod not supported\n", __LINE__);
return false;
}
if (lhs_eutra.offset_freq_present != rhs_eutra.offset_freq_present or
(lhs_eutra.offset_freq_present and lhs_eutra.offset_freq != rhs_eutra.offset_freq)) {
return false;
}
if (lhs_eutra.carrier_freq != rhs_eutra.carrier_freq or not(lhs_eutra.neigh_cell_cfg == rhs_eutra.neigh_cell_cfg) or
lhs_eutra.presence_ant_port1 != rhs_eutra.presence_ant_port1 or
lhs_eutra.allowed_meas_bw != rhs_eutra.allowed_meas_bw) {
return false;
}
if (lhs_eutra.cells_to_add_mod_list.size() != rhs_eutra.cells_to_add_mod_list.size()) {
return false;
}
return std::equal(lhs_eutra.cells_to_add_mod_list.begin(),
lhs_eutra.cells_to_add_mod_list.end(),
rhs_eutra.cells_to_add_mod_list.begin(),
cells_are_equal);
}
} // namespace rrc_details
/*************************************************************************************************
* var_meas_cfg_t class
************************************************************************************************/
//! Add cell parsed in configuration file to the varMeasCfg
std::tuple<bool, var_meas_cfg_t::meas_obj_t*, var_meas_cfg_t::meas_cell_t*>
var_meas_cfg_t::add_cell_cfg(const meas_cell_cfg_t& cellcfg)
{
using namespace rrc_details;
bool inserted_flag = true;
// FIXME: cellcfg.cell_id is the ECI
uint32_t cell_id = rrc_details::eci_to_cellid(cellcfg.cell_id);
q_offset_range_e offset;
asn1::number_to_enum(offset, (int8_t)cellcfg.q_offset); // FIXME: What's the difference
std::pair<meas_obj_t*, meas_cell_t*> ret = rrc_details::find_cell(var_meas.meas_obj_list, cellcfg.earfcn, cell_id);
cells_to_add_mod_s new_cell;
new_cell.cell_idx = cell_id;
new_cell.cell_individual_offset = offset;
new_cell.pci = cellcfg.pci;
if (ret.first != nullptr) {
// there are cells with the same earfcn at least.
if (ret.second != nullptr) {
// the cell already existed.
if (ret.second->pci != cellcfg.pci or ret.second->cell_individual_offset != offset) {
// members of cell were updated
*ret.second = new_cell;
} else {
inserted_flag = false;
}
} else {
// cell_id not found. create new cell
auto& cell_list = ret.first->meas_obj.meas_obj_eutra().cells_to_add_mod_list;
cell_list.push_back(new_cell);
std::sort(cell_list.begin(), cell_list.end(), rrc_details::cell_id_cmp{});
// find cell in new position
ret.second = std::lower_bound(cell_list.begin(), cell_list.end(), new_cell.cell_idx, rrc_details::cell_id_cmp{});
}
} else {
// no measobj has been found with same earfcn, create a new one
meas_obj_t new_obj;
new_obj.meas_obj_id = get_new_obj_id();
asn1::rrc::meas_obj_eutra_s& eutra = new_obj.meas_obj.set_meas_obj_eutra();
eutra.carrier_freq = cellcfg.earfcn;
eutra.allowed_meas_bw.value = asn1::rrc::allowed_meas_bw_e::mbw6; // FIXME: What value to add here?
eutra.neigh_cell_cfg.from_number(1); // FIXME: What value?
eutra.offset_freq_present = true;
// TODO: Assert that q_offset is in ms
asn1::number_to_enum(eutra.offset_freq, cellcfg.q_offset);
eutra.cells_to_add_mod_list_present = true;
eutra.cells_to_add_mod_list.push_back(new_cell);
var_meas.meas_obj_list.push_back(new_obj);
std::sort(var_meas.meas_obj_list.begin(), var_meas.meas_obj_list.end(), rrc_details::meas_obj_id_cmp{});
// get measObj in new position
ret.first = std::lower_bound(var_meas.meas_obj_list.begin(),
var_meas.meas_obj_list.end(),
new_obj.meas_obj_id,
rrc_details::meas_obj_id_cmp{});
ret.second = &ret.first->meas_obj.meas_obj_eutra().cells_to_add_mod_list.back();
}
return {inserted_flag, ret.first, ret.second};
}
//! Find first gap in meas_obj_id and return it
uint32_t var_meas_cfg_t::get_new_obj_id()
{
meas_obj_t* prev_it = var_meas.meas_obj_list.begin();
if (prev_it != var_meas.meas_obj_list.end() and prev_it->meas_obj_id == 1) {
meas_obj_t* it = prev_it;
for (++it; it != var_meas.meas_obj_list.end(); prev_it = it, ++it) {
if (it->meas_obj_id > prev_it->meas_obj_id + 1) {
break;
}
}
}
return (prev_it == var_meas.meas_obj_list.end()) ? 1 : prev_it->meas_obj_id + 1; // starts at 1.
}
void var_meas_cfg_t::compute_diff_meas_cfg(const var_meas_cfg_t& target_cfg, asn1::rrc::meas_cfg_s* meas_cfg)
{
// TODO: Create a flag to disable changing the "this" members (useful for transparent container)
// Set a MeasConfig in the RRC Connection Reconfiguration for HO.
compute_diff_meas_objs(target_cfg, meas_cfg);
// deltaconfig_meas_reports(target_cfg, meas_cfg);
// deltaconfig_meas_ids(target_cfg, meas_cfg);
// deltaconfig_meas_quantity_config(target_cfg, meas_cfg);
meas_cfg->meas_gap_cfg_present = false; // NOTE: we do not support inter-freq. HO
meas_cfg->s_measure_present = false; // NOTE: We do not support SCells
meas_cfg->pre_regist_info_hrpd_present = false; // NOTE: not supported
meas_cfg->speed_state_pars_present = false; // NOTE: not supported
}
//! adds all the cells that got updated to MeasCfg.
void var_meas_cfg_t::compute_diff_cells(const meas_obj_eutra_s& target_it,
meas_obj_eutra_s& src_it,
meas_obj_to_add_mod_s* added_obj)
{
cells_to_add_mod_s* src_cell = src_it.cells_to_add_mod_list.begin();
const cells_to_add_mod_s* target_cell = target_it.cells_to_add_mod_list.begin();
bool src_left = src_cell != src_it.cells_to_add_mod_list.end();
bool target_left = target_cell != target_it.cells_to_add_mod_list.end();
while (src_left or target_left) {
if (not target_left or (src_left and src_cell->cell_idx < target_cell->cell_idx)) {
// a cell was removed from the eNB
// TODO: add cell to remove list
++src_cell;
} else if (not src_left or (target_left and src_cell->cell_idx > target_cell->cell_idx)) {
// a cell was added to the eNB
Info("UE has now to measure activity of (earfcn,cell_id)=(%d,%d).\n", target_cell->cell_idx, target_cell->pci);
rrc_details::meascfg_add_cell(added_obj->meas_obj.meas_obj_eutra(), *target_cell);
} else {
// check if cells are the same. if not, update.
if (not rrc_details::cells_are_equal(*src_cell, *target_cell)) {
Info("UE has now to measure activity of (earfcn,cell_id)=(%d,%d) with updated params.\n",
target_cell->cell_idx,
target_cell->pci);
rrc_details::meascfg_add_cell(added_obj->meas_obj.meas_obj_eutra(), *target_cell);
*src_cell = *target_cell;
}
++src_cell;
++target_cell;
}
src_left = src_cell != src_it.cells_to_add_mod_list.end();
target_left = target_cell != target_it.cells_to_add_mod_list.end();
}
}
//! compute diff between target_cfg and var_meas -> depending on diff, add/remove/update meas_obj in meas_cfg
void var_meas_cfg_t::compute_diff_meas_objs(const var_meas_cfg_t& target_cfg, meas_cfg_s* meas_cfg)
{
// TODO: black cells and white cells
meas_obj_t * ue_it = var_meas.meas_obj_list.begin(), *ue_end = var_meas.meas_obj_list.end();
const meas_obj_t *target_it = target_cfg.var_meas.meas_obj_list.begin(),
*target_end = target_cfg.var_meas.meas_obj_list.end();
bool ues_left = ue_it != ue_end;
bool enbs_left = target_it != target_end;
while (ues_left or enbs_left) {
if (not enbs_left or (ues_left and ue_it->meas_obj_id < target_it->meas_obj_id)) {
// an object has been removed from the target_var_meas
Info("UE can cease to measure activity in frequency earfcn=%d.\n", ue_it->meas_obj.meas_obj_eutra().carrier_freq);
// TODO: add to remove list
++ue_it;
} else if (!ues_left or (enbs_left and ue_it->meas_obj_id > target_it->meas_obj_id)) {
// a new object has been added to enb_var_meas
Info("HO: UE has now to measure activity of new frequency earfcn=%d.\n",
target_it->meas_obj.meas_obj_eutra().carrier_freq);
rrc_details::meascfg_add_meas_obj(meas_cfg, *target_it, true);
++target_it;
} else {
bool are_equal = rrc_details::meas_objs_are_equal(*ue_it, *target_it);
if (not are_equal) {
// if we found a difference in obj IDs
meas_obj_to_add_mod_s* added_obj = rrc_details::meascfg_add_meas_obj(meas_cfg, *target_it, false);
// Add cells/meas_obj if there were changes.
compute_diff_cells(target_it->meas_obj.meas_obj_eutra(), ue_it->meas_obj.meas_obj_eutra(), added_obj);
}
++ue_it;
++target_it;
}
ues_left = ue_it != ue_end;
enbs_left = target_it != target_end;
}
}
/*************************************************************************************************
* mobility_cfg class
************************************************************************************************/
rrc::mobility_cfg::mobility_cfg(rrc* outer_rrc) : rrc_enb(outer_rrc) {}
rrc::mobility_cfg::mobility_cfg(rrc* outer_rrc) : rrc_enb(outer_rrc), current_meas_cfg(outer_rrc->rrc_log)
{
// inserts all neighbor cells
if (rrc_enb->cfg.meas_cfg_present) {
for (meas_cell_cfg_t& meascell : rrc_enb->cfg.meas_cfg.meas_cells) {
current_meas_cfg.add_cell_cfg(meascell);
}
}
}
/*************************************************************************************************
* rrc_mobility class
@ -43,8 +375,38 @@ rrc::ue::rrc_mobility::rrc_mobility(rrc::ue* outer_ue) :
rrc_enb(outer_ue->parent),
cfg(outer_ue->parent->enb_mobility_cfg.get()),
pool(outer_ue->pool),
rrc_log(outer_ue->parent->rrc_log)
rrc_log(outer_ue->parent->rrc_log),
ue_var_meas(outer_ue->parent->rrc_log)
{
}
bool rrc::ue::rrc_mobility::fill_conn_recfg_msg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg)
{
// only reconfigure meas_cfg if no handover is occurring
if (mobility_proc.is_busy()) {
return false;
}
asn1::rrc::meas_cfg_s* meas_cfg = &conn_recfg->meas_cfg;
ue_var_meas.compute_diff_meas_cfg(cfg->current_meas_cfg, meas_cfg);
// if there is at least one difference, we tag a new measurement report in conn_reconf.
bool diff = meas_cfg->meas_obj_to_add_mod_list_present;
diff |= meas_cfg->meas_obj_to_rem_list_present;
diff |= meas_cfg->report_cfg_to_add_mod_list_present;
diff |= meas_cfg->report_cfg_to_rem_list_present;
diff |= meas_cfg->meas_id_to_add_mod_list_present;
diff |= meas_cfg->meas_id_to_rem_list_present;
diff |= meas_cfg->quant_cfg_present;
diff |= meas_cfg->meas_gap_cfg_present;
diff |= meas_cfg->s_measure_present;
diff |= meas_cfg->pre_regist_info_hrpd_present;
diff |= meas_cfg->speed_state_pars_present;
if (diff) {
conn_recfg->meas_cfg_present = true;
return true;
}
return false;
}
} // namespace srsenb

View File

@ -22,3 +22,5 @@
add_executable(plmn_test plmn_test.cc)
target_link_libraries(plmn_test rrc_asn1)
add_executable(rrc_mobility_test rrc_mobility_test.cc)
target_link_libraries(rrc_mobility_test srsenb_rrc rrc_asn1 srslte_common)

View File

@ -0,0 +1,114 @@
/*
* Copyright 2013-2019 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srsenb/hdr/stack/rrc/rrc_mobility.h"
#include <iostream>
#include <srslte/common/log_filter.h>
#define TESTASSERT(cond) \
do { \
if (!(cond)) { \
std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \
return -1; \
} \
} while (0)
using namespace srsenb;
using namespace asn1::rrc;
srslte::log_filter log_h("ALL");
int test_correct_insertion()
{
meas_cell_cfg_t cell1{}, cell2{}, cell3{}, cell4{};
cell1.earfcn = 3400;
cell1.pci = 1;
cell1.q_offset = 0;
cell1.cell_id = 0x19C01;
cell2 = cell1;
cell2.pci = 2;
cell2.cell_id = 0x19C02;
cell3 = cell1;
cell3.earfcn = 2850;
cell4 = cell1;
cell4.q_offset = 1;
// TEST 1: cell insertion in empty varMeasCfg
{
var_meas_cfg_t var_cfg(&log_h);
auto ret = var_cfg.add_cell_cfg(cell1);
TESTASSERT(std::get<0>(ret) and std::get<1>(ret) != nullptr);
const auto& objs = var_cfg.meas_objs();
TESTASSERT(objs.size() == 1 and objs[0].meas_obj_id == 1);
TESTASSERT(objs[0].meas_obj.type().value ==
asn1::rrc::meas_obj_to_add_mod_s::meas_obj_c_::types_opts::meas_obj_eutra);
auto& eutra = objs[0].meas_obj.meas_obj_eutra();
TESTASSERT(eutra.carrier_freq == cell1.earfcn);
TESTASSERT(eutra.cells_to_add_mod_list.size() == 1);
TESTASSERT(eutra.cells_to_add_mod_list[0].pci == cell1.pci);
TESTASSERT(eutra.cells_to_add_mod_list[0].cell_idx == (cell1.cell_id & 0xFFu));
TESTASSERT(eutra.cells_to_add_mod_list[0].cell_individual_offset.to_number() == (int8_t)round(cell1.q_offset));
}
{
var_meas_cfg_t var_cfg(&log_h);
const auto& objs = var_cfg.meas_objs();
// TEST 2: insertion of out-of-order cell ids in same earfcn
var_cfg.add_cell_cfg(cell2);
var_cfg.add_cell_cfg(cell1);
TESTASSERT(objs.size() == 1 and objs[0].meas_obj_id == 1);
auto& eutra = objs[0].meas_obj.meas_obj_eutra();
TESTASSERT(eutra.carrier_freq == cell1.earfcn);
TESTASSERT(eutra.cells_to_add_mod_list.size() == 2);
const cells_to_add_mod_s* cell_it = eutra.cells_to_add_mod_list.begin();
TESTASSERT(cell_it[0].cell_idx == (cell1.cell_id & 0xFFu));
TESTASSERT(cell_it[1].cell_idx == (cell2.cell_id & 0xFFu));
TESTASSERT(cell_it[1].pci == cell2.pci);
// TEST 3: insertion of cell in another frequency
auto ret1 = var_cfg.add_cell_cfg(cell3);
TESTASSERT(std::get<0>(ret1) and std::get<1>(ret1)->meas_obj_id == 2);
TESTASSERT(objs.size() == 2 and objs[1].meas_obj_id == 2);
const auto& eutra2 = objs[1].meas_obj.meas_obj_eutra();
TESTASSERT(eutra2.carrier_freq == cell3.earfcn);
TESTASSERT(eutra2.cells_to_add_mod_list.size() == 1);
// TEST 4: update of existing cell
auto ret2 = var_cfg.add_cell_cfg(cell4);
TESTASSERT(std::get<0>(ret2) and std::get<1>(ret2)->meas_obj_id == 1);
auto& eutra3 = objs[0].meas_obj.meas_obj_eutra();
TESTASSERT(objs.size() == 2 and objs[0].meas_obj_id == 1);
TESTASSERT(eutra3.carrier_freq == cell4.earfcn);
TESTASSERT(eutra3.cells_to_add_mod_list.size() == 2);
TESTASSERT(eutra3.cells_to_add_mod_list[0].cell_idx == (cell1.cell_id & 0xFFu));
TESTASSERT(eutra3.cells_to_add_mod_list[0].cell_individual_offset.to_number() == 1);
}
return 0;
}
int main()
{
TESTASSERT(test_correct_insertion() == 0);
return 0;
}