srsRAN/srsue/src/stack/rrc/rrc_meas.cc

1139 lines
44 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 "srsue/hdr/stack/rrc/rrc_meas.h"
#include "srslte/asn1/rrc_asn1.h"
#include "srsue/hdr/stack/rrc/rrc.h"
/************************************************************************
*
*
* RRC Measurements
*
*
************************************************************************/
using namespace srslte;
using namespace asn1::rrc;
namespace srsue {
void rrc::rrc_meas::init(rrc* rrc_ptr_)
{
rrc_ptr = rrc_ptr_;
meas_report_list.init(rrc_ptr_);
meas_cfg.init(rrc_ptr_);
reset();
}
void rrc::rrc_meas::reset()
{
meas_cfg.reset();
rrc_ptr->phy->meas_stop();
}
// Perform Layer 3 filtering 5.5.3.2
float rrc::rrc_meas::rsrp_filter(const float new_value, const float avg_value)
{
phy_quant_t f = meas_cfg.get_filter_a();
return std::isnormal(avg_value) ? SRSLTE_VEC_EMA(new_value, avg_value, f.rsrp) : new_value;
}
// Perform Layer 3 filtering 5.5.3.2
float rrc::rrc_meas::rsrq_filter(const float new_value, const float avg_value)
{
phy_quant_t f = meas_cfg.get_filter_a();
return std::isnormal(avg_value) ? SRSLTE_VEC_EMA(new_value, avg_value, f.rsrq) : new_value;
}
/* Instruct PHY to start measurement on every configured frequency */
void rrc::rrc_meas::update_phy()
{
std::list<meas_obj_eutra_s> objects = meas_cfg.get_active_objects();
rrc_ptr->phy->meas_stop();
for (const auto& obj : objects) {
// Concatenate cells indicated by enodeb with discovered neighbours
std::set<uint32_t> neighbour_pcis = rrc_ptr->get_cells(obj.carrier_freq);
for (const auto& cell : obj.cells_to_add_mod_list) {
neighbour_pcis.insert(cell.pci);
}
// Instruct PHY to look for cells IDs on this frequency. If neighbour_pcis is empty it will look for new cells
rrc_ptr->phy->set_cells_to_meas(obj.carrier_freq, neighbour_pcis);
}
}
/* Parses MeasConfig object from RRCConnectionReconfiguration message and applies configuration
* as per section 5.5.2
*/
bool rrc::rrc_meas::parse_meas_config(const rrc_conn_recfg_r8_ies_s* mob_reconf_r8,
bool is_ho_reest,
uint32_t src_earfcn)
{
std::lock_guard<std::mutex> lock(meas_cfg_mutex);
bool ret = true;
if (mob_reconf_r8->meas_cfg_present) {
ret = meas_cfg.parse_meas_config(&mob_reconf_r8->meas_cfg, is_ho_reest, src_earfcn);
} else {
cell_t* serv_cell = rrc_ptr->get_serving_cell();
if (serv_cell != nullptr) {
// Run 5.5.6.1 if we don't receive Measurement configuration
meas_cfg.ho_reest_finish(src_earfcn, serv_cell->get_earfcn());
} else {
log_h->warning("MEAS: Could not call ho_reest_finish because serving_cell is null\n");
}
}
update_phy();
return ret;
}
void rrc::rrc_meas::run_tti(const uint32_t tti)
{
std::lock_guard<std::mutex> lock(meas_cfg_mutex);
// Evaluate triggers and send reports for events Section 5.5.4
meas_cfg.eval_triggers();
meas_cfg.report_triggers();
}
uint8_t rrc::rrc_meas::value_to_range(const report_cfg_eutra_s::trigger_quant_opts::options quant, const float value)
{
uint8_t range = 0;
switch (quant) {
case report_cfg_eutra_s::trigger_quant_opts::rsrp:
if (value < -140) {
range = 0;
} else if (value < -44) {
range = 1u + (uint8_t)(value + 140);
} else {
range = 97;
}
break;
case report_cfg_eutra_s::trigger_quant_opts::rsrq:
if (value < -19.5) {
range = 0;
} else if (value < -3) {
range = 1u + (uint8_t)(2 * (value + 19.5));
} else {
range = 34;
}
break;
default:
break;
}
return range;
}
float rrc::rrc_meas::range_to_value(const report_cfg_eutra_s::trigger_quant_opts::options quant, const uint8_t range)
{
float val = 0;
switch (quant) {
case report_cfg_eutra_s::trigger_quant_opts::rsrp:
val = -140 + (float)range;
break;
case report_cfg_eutra_s::trigger_quant_opts::rsrq:
val = -19.5f + (float)range / 2;
break;
default:
break;
}
return val;
}
uint8_t rrc::rrc_meas::offset_val(const meas_obj_eutra_s& meas_obj)
{
return meas_obj.offset_freq_present ? meas_obj.offset_freq.to_number() : 0;
}
asn1::dyn_array<cells_to_add_mod_s>::iterator rrc::rrc_meas::find_pci_in_meas_obj(meas_obj_eutra_s& meas_obj,
const uint32_t pci)
{
return std::find_if(meas_obj.cells_to_add_mod_list.begin(),
meas_obj.cells_to_add_mod_list.end(),
[&pci](const cells_to_add_mod_s& c) { return c.pci == pci; });
}
/*
*
* var_meas_report_list subclass
*
*/
void rrc::rrc_meas::var_meas_report_list::init(rrc* rrc_ptr_)
{
rrc_ptr = rrc_ptr_;
timers = rrc_ptr_->timers;
}
/* Generate report procedure 5.5.5 */
void rrc::rrc_meas::var_meas_report_list::generate_report(const uint32_t measId)
{
cell_t* serv_cell = rrc_ptr->get_serving_cell();
if (serv_cell == nullptr) {
log_h->warning("MEAS: Serving cell not set when evaluating triggers\n");
return;
}
ul_dcch_msg_s ul_dcch_msg;
ul_dcch_msg.msg.set_c1().set_meas_report().crit_exts.set_c1().set_meas_report_r8();
meas_results_s* report = &ul_dcch_msg.msg.c1().meas_report().crit_exts.c1().meas_report_r8().meas_results;
report->meas_id = (uint8_t)measId;
report->meas_result_pcell.rsrp_result =
value_to_range(report_cfg_eutra_s::trigger_quant_opts::rsrp, serv_cell->get_rsrp());
report->meas_result_pcell.rsrq_result =
value_to_range(report_cfg_eutra_s::trigger_quant_opts::rsrq, serv_cell->get_rsrq());
log_h->info("MEAS: Generate report MeasId=%d, Pcell rsrp=%f rsrq=%f\n",
report->meas_id,
serv_cell->get_rsrp(),
serv_cell->get_rsrq());
meas_result_list_eutra_l& neigh_list = report->meas_result_neigh_cells.set_meas_result_list_eutra();
var_meas_report& var_meas = varMeasReportList.at(measId);
// sort cells by RSRP
std::sort(var_meas.cell_triggered_list.begin(),
var_meas.cell_triggered_list.end(),
[this](phy_interface_rrc_lte::phy_cell_t a, phy_interface_rrc_lte::phy_cell_t b) {
return rrc_ptr->get_cell_rsrp(a.earfcn, a.pci) > rrc_ptr->get_cell_rsrp(b.earfcn, b.pci);
});
// set the measResultNeighCells to include the best neighbouring cells up to maxReportCells in accordance with
// the following
for (auto& cell : var_meas.cell_triggered_list) {
// report neighbour cells only
if (cell.pci == serv_cell->get_pci() && cell.earfcn == serv_cell->get_earfcn()) {
log_h->info("MEAS: skipping serving cell in report neighbour=%d, pci=%d, earfcn=%d, rsrp=%+.1f, rsrq=%+.1f\n",
neigh_list.size(),
cell.pci,
var_meas.carrier_freq,
rrc_ptr->get_cell_rsrp(var_meas.carrier_freq, cell.pci),
rrc_ptr->get_cell_rsrq(var_meas.carrier_freq, cell.pci));
continue;
}
if (neigh_list.size() <= var_meas.report_cfg.max_report_cells) {
float rsrp_value = rrc_ptr->get_cell_rsrp(var_meas.carrier_freq, cell.pci);
float rsrq_value = rrc_ptr->get_cell_rsrq(var_meas.carrier_freq, cell.pci);
meas_result_eutra_s rc = {};
// Set quantity to report
switch (var_meas.report_cfg.report_quant.value) {
case report_cfg_eutra_s::report_quant_opts::both:
rc.meas_result.rsrp_result_present = true;
rc.meas_result.rsrq_result_present = true;
break;
case report_cfg_eutra_s::report_quant_opts::same_as_trigger_quant:
switch (var_meas.report_cfg.trigger_quant.value) {
case report_cfg_eutra_s::trigger_quant_opts::rsrp:
rc.meas_result.rsrp_result_present = true;
break;
case report_cfg_eutra_s::trigger_quant_opts::rsrq:
rc.meas_result.rsrp_result_present = true;
break;
default:
break;
}
break;
default:
break;
}
rc.pci = (uint16_t)cell.pci;
rc.meas_result.rsrp_result = value_to_range(report_cfg_eutra_s::trigger_quant_opts::rsrp, rsrp_value);
rc.meas_result.rsrq_result = value_to_range(report_cfg_eutra_s::trigger_quant_opts::rsrq, rsrq_value);
log_h->info("MEAS: Adding to report neighbour=%d, pci=%d, earfcn=%d, rsrp=%+.1f, rsrq=%+.1f\n",
neigh_list.size(),
rc.pci,
var_meas.carrier_freq,
rsrp_value,
rsrq_value);
neigh_list.push_back(rc);
}
}
report->meas_result_neigh_cells_present = neigh_list.size() > 0;
var_meas.nof_reports_sent++;
if (var_meas.periodic_timer.is_valid()) {
var_meas.periodic_timer.stop();
}
// if the numberOfReportsSent as defined within the VarMeasReportList for this measId is less than the
// reportAmount as defined within the corresponding reportConfig for this measId
if (var_meas.nof_reports_sent < var_meas.report_cfg.report_amount.to_number()) {
// start the periodical reporting timer with the value of reportInterval as defined within the corresponding
// reportConfig for this measId
if (var_meas.periodic_timer.is_valid()) {
var_meas.periodic_timer.run();
}
} else {
if (var_meas.periodic_timer.is_valid()) {
var_meas.periodic_timer.clear();
}
// else if the triggerType is set to periodical:
if (var_meas.report_cfg.trigger_type.type().value == report_cfg_eutra_s::trigger_type_c_::types::periodical) {
// remove the entry within the VarMeasReportList for this measId
remove_varmeas_report(measId);
meas_cfg->remove_measId(measId);
}
}
// Send to lower layers
rrc_ptr->send_srb1_msg(ul_dcch_msg);
}
void rrc::rrc_meas::var_meas_report_list::remove_all_varmeas_reports()
{
varMeasReportList.clear();
}
void rrc::rrc_meas::var_meas_report_list::remove_varmeas_report(const uint32_t measId)
{
if (varMeasReportList.count(measId)) {
varMeasReportList.erase(measId);
}
}
bool rrc::rrc_meas::var_meas_report_list::is_timer_expired(const uint32_t measId)
{
if (varMeasReportList.count(measId)) {
if (varMeasReportList.at(measId).periodic_timer.is_valid()) {
return varMeasReportList.at(measId).periodic_timer.is_expired();
}
}
return false;
}
void rrc::rrc_meas::var_meas_report_list::set_measId(const uint32_t measId,
const uint32_t carrier_freq,
const report_cfg_eutra_s& report_cfg,
const cell_triggered_t& cell_triggered_list)
{
// Create entry if it doesn't exist.
if (!varMeasReportList.count(measId)) {
varMeasReportList[measId].nof_reports_sent = 0;
}
// The ReportInterval is applicable if the UE performs periodical reporting (i.e. when reportAmount exceeds 1), for
// triggerType event as well as for triggerType
// periodical
if (!varMeasReportList.at(measId).periodic_timer.is_valid() && report_cfg.report_amount.to_number() > 1) {
varMeasReportList.at(measId).periodic_timer = timers->get_unique_timer();
varMeasReportList.at(measId).periodic_timer.set(report_cfg.report_interv.to_number());
}
varMeasReportList.at(measId).report_cfg = std::move(report_cfg);
varMeasReportList.at(measId).carrier_freq = carrier_freq;
varMeasReportList.at(measId).nof_reports_sent = 0;
upd_measId(measId, cell_triggered_list);
}
void rrc::rrc_meas::var_meas_report_list::upd_measId(const uint32_t measId, const cell_triggered_t& cell_triggered_list)
{
if (varMeasReportList.count(measId)) {
varMeasReportList.at(measId).cell_triggered_list = std::move(cell_triggered_list);
}
}
cell_triggered_t rrc::rrc_meas::var_meas_report_list::get_measId_cells(const uint32_t measId)
{
if (varMeasReportList.count(measId)) {
return varMeasReportList.at(measId).cell_triggered_list;
} else {
return {};
}
}
void rrc::rrc_meas::var_meas_cfg::report_triggers()
{
// for each measId included in the measIdList within VarMeasConfig
for (auto& m : measIdList) {
if (!reportConfigList.count(m.second.report_cfg_id) || !measObjectsList.count(m.second.meas_obj_id)) {
log_h->error("MEAS: Computing report triggers. MeasId=%d has invalid report or object settings\n", m.first);
continue;
}
report_cfg_eutra_s& report_cfg = reportConfigList.at(m.second.report_cfg_id);
meas_obj_eutra_s& meas_obj = measObjectsList.at(m.second.meas_obj_id);
if (report_cfg.trigger_type.type() == report_cfg_eutra_s::trigger_type_c_::types::event) {
// if the triggerType is set to event and if the entry condition applicable for this event,
{
bool new_cell_trigger = false;
cell_triggered_t cells_triggered_list = meas_report->get_measId_cells(m.first);
for (auto& cell : trigger_state[m.first]) {
if (cell.second.is_enter_equal(report_cfg.trigger_type.event().time_to_trigger.to_number())) {
// Do not add if already exists
if (std::find_if(cells_triggered_list.begin(),
cells_triggered_list.end(),
[&cell](const phy_interface_rrc_lte::phy_cell_t& c) { return cell.first == c.pci; }) ==
cells_triggered_list.end()) {
cells_triggered_list.push_back({cell.first, meas_obj.carrier_freq});
new_cell_trigger = true;
}
}
}
if (new_cell_trigger) {
// include a measurement reporting entry within the VarMeasReportList for this measId (nof_reports reset
// inside) include the concerned cell(s) in the cellsTriggeredList defined within the VarMeasReportList
meas_report->set_measId(m.first, meas_obj.carrier_freq, report_cfg, cells_triggered_list);
// initiate the measurement reporting procedure, as specified in 5.5.5;
meas_report->generate_report(m.first);
}
}
{
// if the triggerType is set to event and if the leaving condition applicable for this event is fulfilled ...
cell_triggered_t cells_triggered_list = meas_report->get_measId_cells(m.first);
// remove the concerned cell(s) in the cellsTriggeredList defined within the VarMeasReportList
auto it = cells_triggered_list.begin();
while (it != cells_triggered_list.end()) {
if (trigger_state[m.first][it->pci].is_exit_equal(
report_cfg.trigger_type.event().time_to_trigger.to_number())) {
it = cells_triggered_list.erase(it);
meas_report->upd_measId(m.first, cells_triggered_list);
// if reportOnLeave is set to TRUE for the corresponding reporting configuration
if (report_cfg.trigger_type.event().event_id.type() == eutra_event_s::event_id_c_::types::event_a3 &&
report_cfg.trigger_type.event().event_id.event_a3().report_on_leave) {
// initiate the measurement reporting procedure, as specified in 5.5.5;
meas_report->generate_report(m.first);
}
// if the cellsTriggeredList defined within the VarMeasReportList for this measId is empty:
if (cells_triggered_list.empty()) {
remove_varmeas_report(m.first);
}
} else {
it++;
}
}
}
}
// upon expiry of the periodical reporting timer for this measId
if (meas_report->is_timer_expired(m.first)) {
meas_report->generate_report(m.first);
}
}
}
bool rrc::rrc_meas::var_meas_cfg::is_rsrp(report_cfg_eutra_s::trigger_quant_opts::options q)
{
return q == report_cfg_eutra_s::trigger_quant_opts::rsrp;
}
/* Evaluate event trigger conditions for each cell 5.5.4 */
void rrc::rrc_meas::var_meas_cfg::eval_triggers()
{
cell_t* serv_cell = rrc_ptr->get_serving_cell();
if (serv_cell == nullptr) {
log_h->warning("MEAS: Serving cell not set when evaluating triggers\n");
return;
}
uint32_t serving_earfcn = serv_cell->get_earfcn();
uint32_t serving_pci = serv_cell->get_pci();
// Obtain serving cell specific offset
float Ofs = 0;
float Ocs = 0;
auto serving_obj = std::find_if(
measObjectsList.begin(), measObjectsList.end(), [serving_earfcn](const std::pair<uint32_t, meas_obj_eutra_s>& c) {
return c.second.carrier_freq == serving_earfcn;
});
if (serving_obj != measObjectsList.end()) {
Ofs = offset_val(serving_obj->second);
auto serving_cell_off = find_pci_in_meas_obj(serving_obj->second, serving_pci);
if (serving_cell_off != serving_obj->second.cells_to_add_mod_list.end()) {
Ocs = serving_cell_off->cell_individual_offset;
}
}
for (auto& m : measIdList) {
if (!reportConfigList.count(m.second.report_cfg_id) || !measObjectsList.count(m.second.meas_obj_id)) {
log_h->error("MEAS: Computing report triggers. MeasId=%d has invalid report or object settings\n", m.first);
continue;
}
log_h->debug("MEAS: Calculating trigger for MeasId=%d, ObjectId=%d, ReportId=%d\n",
m.first,
m.second.meas_obj_id,
m.second.report_cfg_id);
report_cfg_eutra_s& report_cfg = reportConfigList.at(m.second.report_cfg_id);
meas_obj_eutra_s& meas_obj = measObjectsList.at(m.second.meas_obj_id);
double hyst = 0.5 * report_cfg.trigger_type.event().hysteresis;
float Ms = is_rsrp(report_cfg.trigger_quant.value) ? serv_cell->get_rsrp() : serv_cell->get_rsrq();
if (!std::isnormal(Ms)) {
log_h->warning("MEAS: Serving cell Ms=%f invalid when evaluating triggers\n", Ms);
return;
}
eutra_event_s::event_id_c_ event_id = report_cfg.trigger_type.event().event_id;
if (report_cfg.trigger_type.type() == report_cfg_eutra_s::trigger_type_c_::types::event) {
// A1 & A2 are for serving cell only
if (event_id.type().value < eutra_event_s::event_id_c_::types::event_a3) {
float thresh = 0.0;
bool enter_condition = false;
bool exit_condition = false;
if (event_id.type() == eutra_event_s::event_id_c_::types::event_a1) {
if (event_id.event_a1().a1_thres.type().value == thres_eutra_c::types::thres_rsrp) {
thresh = range_to_value(report_cfg.trigger_quant, event_id.event_a1().a1_thres.thres_rsrp());
} else {
thresh = range_to_value(report_cfg.trigger_quant, event_id.event_a1().a1_thres.thres_rsrq());
}
enter_condition = Ms - hyst > thresh;
exit_condition = Ms + hyst < thresh;
} else {
if (event_id.event_a2().a2_thres.type() == thres_eutra_c::types::thres_rsrp) {
thresh = range_to_value(report_cfg.trigger_quant, event_id.event_a2().a2_thres.thres_rsrp());
} else {
thresh = range_to_value(report_cfg.trigger_quant, event_id.event_a2().a2_thres.thres_rsrq());
}
enter_condition = Ms + hyst < thresh;
exit_condition = Ms - hyst > thresh;
}
trigger_state[m.first][serving_pci].event_condition(enter_condition, exit_condition);
log_h->debug("MEAS: eventId=%s, Ms=%.2f, hyst=%.2f, Thresh=%.2f, enter_condition=%d, exit_condition=%d\n",
event_id.type().to_string().c_str(),
Ms,
hyst,
thresh,
enter_condition,
exit_condition);
// Rest are evaluated for every cell in frequency
} else {
auto cells = rrc_ptr->get_cells(meas_obj.carrier_freq);
for (auto& pci : cells) {
log_h->debug("MEAS: eventId=%s, pci=%d, earfcn=%d\n",
event_id.type().to_string().c_str(),
pci,
meas_obj.carrier_freq);
float Ofn = offset_val(meas_obj);
float Ocn = 0;
// If the cell was provided by the configuration, check if it has an individual q_offset
auto n = find_pci_in_meas_obj(meas_obj, pci);
if (n != meas_obj.cells_to_add_mod_list.end()) {
Ocn = n->cell_individual_offset.to_number();
}
float Mn = 0;
if (is_rsrp(report_cfg.trigger_quant.value)) {
Mn = rrc_ptr->get_cell_rsrp(meas_obj.carrier_freq, pci);
} else {
Mn = rrc_ptr->get_cell_rsrq(meas_obj.carrier_freq, pci);
}
double Off = 0;
float thresh = 0, th1 = 0, th2 = 0;
bool enter_condition = false;
bool exit_condition = false;
uint8_t range, range2;
switch (event_id.type().value) {
case eutra_event_s::event_id_c_::types::event_a3:
Off = 0.5 * event_id.event_a3().a3_offset;
enter_condition = Mn + Ofn + Ocn - hyst > Ms + Ofs + Ocs + Off;
exit_condition = Mn + Ofn + Ocn + hyst < Ms + Ofs + Ocs + Off;
break;
case eutra_event_s::event_id_c_::types::event_a4:
if (event_id.event_a4().a4_thres.type() == thres_eutra_c::types::thres_rsrp) {
range = event_id.event_a4().a4_thres.thres_rsrp();
} else {
range = event_id.event_a4().a4_thres.thres_rsrq();
}
thresh = range_to_value(report_cfg.trigger_quant.value, range);
enter_condition = Mn + Ofn + Ocn - hyst > thresh;
exit_condition = Mn + Ofn + Ocn + hyst < thresh;
break;
case eutra_event_s::event_id_c_::types::event_a5:
if (event_id.event_a5().a5_thres1.type() == thres_eutra_c::types::thres_rsrp) {
range = event_id.event_a5().a5_thres1.thres_rsrp();
} else {
range = event_id.event_a5().a5_thres1.thres_rsrq();
}
if (event_id.event_a5().a5_thres2.type() == thres_eutra_c::types::thres_rsrp) {
range2 = event_id.event_a5().a5_thres2.thres_rsrp();
} else {
range2 = event_id.event_a5().a5_thres2.thres_rsrq();
}
th1 = range_to_value(report_cfg.trigger_quant.value, range);
th2 = range_to_value(report_cfg.trigger_quant.value, range2);
enter_condition = (Ms + hyst < th1) && (Mn + Ofn + Ocn - hyst > th2);
exit_condition = (Ms - hyst > th1) && (Mn + Ofn + Ocn + hyst < th2);
break;
default:
log_h->error("Error event %s not implemented\n", event_id.type().to_string().c_str());
}
trigger_state[m.first][pci].event_condition(enter_condition, exit_condition);
log_h->debug(
"MEAS: eventId=%s, pci=%d, Ms=%.2f, hyst=%.2f, Thresh=%.2f, enter_condition=%d, exit_condition=%d\n",
event_id.type().to_string().c_str(),
pci,
Ms,
hyst,
thresh,
enter_condition,
exit_condition);
}
}
}
}
}
/***
*
* varMeasConfig class
*
*
*/
void rrc::rrc_meas::var_meas_cfg::init(rrc* rrc_ptr_)
{
rrc_ptr = rrc_ptr_;
rrc_ptr = rrc_ptr_;
}
void rrc::rrc_meas::var_meas_cfg::reset()
{
measIdList.clear();
measObjectsList.clear();
reportConfigList.clear();
}
rrc::rrc_meas::phy_quant_t rrc::rrc_meas::var_meas_cfg::get_filter_a()
{
return filter_a;
}
// stop the periodical reporting timer or timer T321, whichever one is running, and reset the associated
// information (e.g. timeToTrigger) for this measId
// All these is done automatically by the destructor
void rrc::rrc_meas::var_meas_cfg::remove_measId(const uint32_t measId)
{
measIdList.erase(measId);
}
void rrc::rrc_meas::var_meas_cfg::remove_varmeas_report(const uint32_t meas_id)
{
meas_report->remove_varmeas_report(meas_id);
trigger_state.erase(meas_id);
}
std::list<meas_obj_eutra_s> rrc::rrc_meas::var_meas_cfg::get_active_objects()
{
std::list<meas_obj_eutra_s> r;
for (auto& m : measIdList) {
if (measObjectsList.count(m.second.meas_obj_id)) {
r.push_back(measObjectsList.at(m.second.meas_obj_id));
}
}
if (log_h->get_level() == LOG_LEVEL_DEBUG) {
log_h->debug("MEAS: Returning %lu active objects\n", r.size());
for (auto& o : r) {
log_h->debug("MEAS: carrier_freq=%d, %u cells\n", o.carrier_freq, o.cells_to_add_mod_list.size());
}
}
// we do a copy of all the structs here but this function is only called during reconfiguration
return r;
}
// Procedure upon handover or reestablishment 5.5.6.1
void rrc::rrc_meas::var_meas_cfg::ho_reest_finish(const uint32_t src_earfcn, const uint32_t dst_earfcn)
{
log_h->info(
"MEAS: Actions upon handover and reestablishment, src_earfcn=%d, dst_earfcn=%d\n", src_earfcn, dst_earfcn);
// for each measId included in the measIdList within VarMeasConfig, if the triggerType is set to periodical, remove
// this measId from the measIdList within VarMeasConfig
{
auto it = measIdList.begin();
while (it != measIdList.end()) {
if (reportConfigList.count(it->second.report_cfg_id) &&
reportConfigList.at(it->second.report_cfg_id).trigger_type.type().value ==
report_cfg_eutra_s::trigger_type_c_::types_opts::periodical) {
it = measIdList.erase(it);
} else {
it++;
}
}
}
if (log_h->get_level() == LOG_LEVEL_DEBUG) {
log_h->debug("MEAS: measId before HO\n");
for (auto& m : measIdList) {
log_h->debug("MEAS: measId=%d, measObjectId=%d\n", m.first, m.second.meas_obj_id);
}
}
// if the procedure was triggered due to inter-frequency handover or successful re-establishment to an inter-
// frequency cell
if (src_earfcn != dst_earfcn) {
auto src_obj = std::find_if(
measObjectsList.begin(), measObjectsList.end(), [&src_earfcn](const std::pair<uint32_t, meas_obj_eutra_s>& c) {
return c.second.carrier_freq == src_earfcn;
});
auto dst_obj = std::find_if(
measObjectsList.begin(), measObjectsList.end(), [&dst_earfcn](const std::pair<uint32_t, meas_obj_eutra_s>& c) {
return c.second.carrier_freq == dst_earfcn;
});
if (dst_obj != measObjectsList.end()) {
for (auto& m : measIdList) {
// if the measId value is linked to the measObjectId value corresponding to the source carrier frequency
if (m.second.meas_obj_id == src_obj->first) {
// link this measId value to the measObjectId value corresponding to the target carrier frequency
m.second.meas_obj_id = dst_obj->first;
// else if the measId value is linked to the measObjectId value corresponding to the target carrier
// frequency
} else if (m.second.meas_obj_id == dst_obj->first) {
// link this measId value to the measObjectId value corresponding to the source carrier frequency
m.second.meas_obj_id = src_obj->first;
}
}
} else {
// remove all measId values that are linked to the measObjectId value corresponding to the source carrier
// frequency
auto it = measIdList.begin();
while (it != measIdList.end()) {
if (it->second.meas_obj_id == src_obj->first) {
it = measIdList.erase(it);
} else {
it++;
}
}
}
}
if (log_h->get_level() == LOG_LEVEL_DEBUG) {
log_h->debug("MEAS: measId after HO\n");
for (auto& m : measIdList) {
log_h->debug("MEAS: measId=%d, measObjectId=%d\n", m.first, m.second.meas_obj_id);
}
}
meas_report->remove_all_varmeas_reports();
trigger_state.clear();
}
// Measurement object removal 5.5.2.4
void rrc::rrc_meas::var_meas_cfg::measObject_removal(const meas_obj_to_rem_list_l& list)
{
for (auto& l : list) {
if (measObjectsList.count(l)) {
// Remove entry from measObjectList
measObjectsList.erase(l);
// Remove all entries in measIdList associated with this objectId
auto it = measIdList.begin();
while (it != measIdList.end()) {
if (it->second.meas_obj_id == l) {
log_h->info("MEAS: Removed measId=%d\n", it->first);
remove_varmeas_report(it->first); // Remove report before `it` is updated with the next pointer
it = measIdList.erase(it);
} else {
it++;
}
}
log_h->info("MEAS: Removed measObjectId=%d\n", l);
}
}
}
// Measurement object addition/modification Section 5.5.2.5
void rrc::rrc_meas::var_meas_cfg::measObject_addmod(const meas_obj_to_add_mod_list_l& list)
{
for (auto& l : list) {
if (l.meas_obj.type().value == meas_obj_to_add_mod_s::meas_obj_c_::types_opts::meas_obj_eutra) {
bool entry_exists = measObjectsList.count(l.meas_obj_id) > 0;
const meas_obj_eutra_s& cfg_obj = l.meas_obj.meas_obj_eutra();
if (!entry_exists) {
// add a new entry for the received measObject to the measObjectList within VarMeasConfig
measObjectsList.emplace(l.meas_obj_id, cfg_obj);
}
meas_obj_eutra_s& local_obj = measObjectsList.at(l.meas_obj_id);
// if an entry with the matching measObjectId exists in the measObjectList within the VarMeasConfig
if (entry_exists) {
// Combine the new cells with the existing ones and remove the cells indicated in config
{
// Remove cells
if (cfg_obj.cells_to_rem_list_present) {
log_h->debug("MEAS: Removing %d cells\n", cfg_obj.cells_to_rem_list.size());
cells_to_add_mod_list_l new_list;
for (auto& local_cell : local_obj.cells_to_add_mod_list) {
// If not in the list to remove, copy to new list
if (std::find(cfg_obj.cells_to_rem_list.begin(), cfg_obj.cells_to_rem_list.end(), local_cell.cell_idx) ==
cfg_obj.cells_to_rem_list.end()) {
new_list.push_back(local_cell);
}
}
local_obj.cells_to_add_mod_list = new_list;
if (log_h->get_level() == LOG_LEVEL_DEBUG) {
for (auto& c : local_obj.cells_to_add_mod_list) {
log_h->debug("MEAS: cell idx=%d, pci=%d, q_offset=%d\n",
c.cell_idx,
c.pci,
c.cell_individual_offset.value);
}
}
}
if (cfg_obj.cells_to_add_mod_list_present) {
for (auto& new_cell : cfg_obj.cells_to_add_mod_list) {
// If an entry with the matching cellIndex exists in the local object cellsToAddModList:
auto it =
std::find_if(local_obj.cells_to_add_mod_list.begin(),
local_obj.cells_to_add_mod_list.end(),
[&new_cell](const cells_to_add_mod_s& c) { return c.cell_idx == new_cell.cell_idx; });
if (it != local_obj.cells_to_add_mod_list.end()) {
// If the new cell exists, copy it
*it = new_cell;
} else {
// otherwise add it
local_obj.cells_to_add_mod_list.push_back(new_cell);
}
}
}
}
// Do the same with black list
{
if (cfg_obj.black_cells_to_add_mod_list_present) {
black_cells_to_add_mod_list_l new_list;
for (auto& local_cell : local_obj.black_cells_to_add_mod_list) {
// If doesn't exists in cells to rem
if (std::find(cfg_obj.black_cells_to_rem_list.begin(),
cfg_obj.black_cells_to_rem_list.end(),
local_cell.cell_idx) == cfg_obj.black_cells_to_rem_list.end()) {
new_list.push_back(local_cell);
}
}
local_obj.black_cells_to_add_mod_list = new_list;
}
if (cfg_obj.black_cells_to_add_mod_list_present) {
for (auto& new_cell : cfg_obj.black_cells_to_add_mod_list) {
// If an entry with the matching cellIndex exists in the local object blackCellsToAddModList:
auto it = std::find_if(
local_obj.black_cells_to_add_mod_list.begin(),
local_obj.black_cells_to_add_mod_list.end(),
[&new_cell](const black_cells_to_add_mod_s& c) { return c.cell_idx == new_cell.cell_idx; });
if (it != local_obj.black_cells_to_add_mod_list.end()) {
// copy the new entry
*it = new_cell;
} else {
local_obj.black_cells_to_add_mod_list.push_back(new_cell);
}
}
}
}
// for each measId associated with this measObjectId in the measIdList within the VarMeasConfig
for (auto& m : measIdList) {
if (m.second.meas_obj_id == l.meas_obj_id) {
remove_varmeas_report(m.first);
}
}
}
log_h->info("MEAS: %s objectId=%d, carrier_freq=%d, %u cells, %u black-listed cells\n",
!entry_exists ? "Added" : "Modified",
l.meas_obj_id,
local_obj.carrier_freq,
local_obj.cells_to_add_mod_list.size(),
local_obj.black_cells_to_add_mod_list.size());
if (log_h->get_level() == LOG_LEVEL_DEBUG) {
for (auto& c : local_obj.cells_to_add_mod_list) {
log_h->debug(
"MEAS: cell idx=%d, pci=%d, q_offset=%d\n", c.cell_idx, c.pci, c.cell_individual_offset.value);
}
for (auto& b : local_obj.black_cells_to_add_mod_list) {
log_h->debug("MEAS: black-listed cell idx=%d\n", b.cell_idx);
}
}
} else {
log_h->error("Unsupported measObject type: %s\n", l.meas_obj.type().to_string().c_str());
}
}
}
// perform the reporting configuration removal procedure as specified in 5.5.2.6;
void rrc::rrc_meas::var_meas_cfg::reportConfig_removal(const report_cfg_to_rem_list_l& list)
{
for (auto& l : list) {
if (reportConfigList.count(l)) {
// Remove entry from measObjectList
reportConfigList.erase(l);
// Remove all entries in measIdList associated with this objectId
auto it = measIdList.begin();
while (it != measIdList.end()) {
if (it->second.report_cfg_id == l) {
log_h->info("MEAS: Removed measId=%d\n", it->first);
remove_varmeas_report(it->first); // Remove report before `it` is updated with the next pointer
it = measIdList.erase(it);
} else {
it++;
}
}
log_h->info("MEAS: Removed reportObjectId=%d\n", l);
}
}
}
// perform the reporting configuration addition/ modification procedure as specified in 5.5.2.7
void rrc::rrc_meas::var_meas_cfg::reportConfig_addmod(const report_cfg_to_add_mod_list_l& list)
{
for (auto& l : list) {
if (l.report_cfg.type() == report_cfg_to_add_mod_s::report_cfg_c_::types_opts::report_cfg_eutra) {
if (l.report_cfg.report_cfg_eutra().trigger_type.type().value ==
report_cfg_eutra_s::trigger_type_c_::types_opts::event) {
bool entry_exists = reportConfigList.count(l.report_cfg_id) > 0;
if (entry_exists) {
reportConfigList.at(l.report_cfg_id) = l.report_cfg.report_cfg_eutra();
// for each measId associated with this reportConfigId in the measIdList within the VarMeasConfig
for (auto& m : measIdList) {
if (m.second.report_cfg_id == l.report_cfg_id) {
remove_varmeas_report(m.first);
}
}
} else {
reportConfigList.emplace(l.report_cfg_id, l.report_cfg.report_cfg_eutra());
}
log_h->info("MEAS: %s reportConfig id=%d, event-type=%s, time-to-trigger=%d ms, reportInterval=%d\n",
!entry_exists ? "Added" : "Modified",
l.report_cfg_id,
l.report_cfg.report_cfg_eutra().trigger_type.event().event_id.type().to_string().c_str(),
l.report_cfg.report_cfg_eutra().trigger_type.event().time_to_trigger.to_number(),
l.report_cfg.report_cfg_eutra().report_interv.to_number());
if (entry_exists) {
log_debug_trigger_value(l.report_cfg.report_cfg_eutra().trigger_type.event().event_id);
}
} else {
log_h->error("MEAS: Periodical reports not supported. Received in reportConfigId=%d\n", l.report_cfg_id);
}
} else {
log_h->error("MEAS: Unsupported reportConfig type: %s\n", l.report_cfg.type().to_string().c_str());
}
}
}
// Warning: Use for Test debug purposes only. Assumes thresholds in RSRP
void rrc::rrc_meas::var_meas_cfg::log_debug_trigger_value(const eutra_event_s::event_id_c_& e)
{
if (log_h->get_level() == LOG_LEVEL_DEBUG) {
switch (e.type()) {
case eutra_event_s::event_id_c_::types_opts::event_a1:
log_h->debug("MEAS: A1-threshold=%.1f dBm\n",
range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a1().a1_thres.thres_rsrp()));
break;
case eutra_event_s::event_id_c_::types_opts::event_a2:
log_h->debug("MEAS: A2-threshold=%.1f dBm\n",
range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a2().a2_thres.thres_rsrp()));
break;
case eutra_event_s::event_id_c_::types_opts::event_a3:
log_h->debug("MEAS: A3-offset=%.1f dB\n",
range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a3().a3_offset));
break;
case eutra_event_s::event_id_c_::types_opts::event_a4:
log_h->debug("MEAS: A4-threshold=%.1f dBm\n",
range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a4().a4_thres.thres_rsrp()));
break;
default:
log_h->debug("MEAS: Unsupported\n");
break;
}
}
}
// perform the measurement identity removal procedure as specified in 5.5.2.2
void rrc::rrc_meas::var_meas_cfg::measId_removal(const meas_id_to_rem_list_l& list)
{
for (auto& l : list) {
// for each measId included in the received measIdToRemoveList that is part of the current UE configuration in
// varMeasConfig
if (measIdList.count(l)) {
measIdList.erase(l);
remove_varmeas_report(l);
log_h->info("MEAS: Removed measId=%d\n", l);
}
}
}
// perform the measurement identity addition/ modification procedure as specified in 5.5.2.3
void rrc::rrc_meas::var_meas_cfg::measId_addmod(const meas_id_to_add_mod_list_l& list)
{
for (auto& l : list) {
// configure only if report and object exists
if (reportConfigList.count(l.report_cfg_id) == 0) {
log_h->error("MEAS: Adding measId=%d, reportConfigId=%d doesn't exist\n", l.meas_id, l.report_cfg_id);
continue;
}
if (measObjectsList.count(l.meas_obj_id) == 0) {
log_h->error("MEAS: Adding measId=%d, measObjectId=%d doesn't exist\n", l.meas_id, l.meas_obj_id);
continue;
}
// add/replace entry in VarMeasConfig. Safe to use [] operator here
measIdList[l.meas_id] = l;
// remove the measurement reporting entry for this measId from the VarMeasReportList, if included
remove_varmeas_report(l.meas_id);
log_h->info(
"MEAS: AddMod measId=%d, measObjectId=%d, reportConfigId=%d\n", l.meas_id, l.meas_obj_id, l.report_cfg_id);
}
}
// perform the quantity configuration procedure as specified in 5.5.2.8;
void rrc::rrc_meas::var_meas_cfg::quantity_config(const quant_cfg_s& cfg)
{
// set the parameter quantityConfig within VarMeasConfig to the received value of quantityConfig
// we compute filter coefficients here
if (cfg.quant_cfg_eutra_present) {
uint32_t k_rsrp = 0, k_rsrq = 0;
if (cfg.quant_cfg_eutra.filt_coef_rsrp_present) {
k_rsrp = cfg.quant_cfg_eutra.filt_coef_rsrp.to_number();
} else {
k_rsrp = filt_coef_e(filt_coef_e::fc4).to_number();
}
if (cfg.quant_cfg_eutra.filt_coef_rsrq_present) {
k_rsrq = cfg.quant_cfg_eutra.filt_coef_rsrq.to_number();
} else {
k_rsrq = filt_coef_e(filt_coef_e::fc4).to_number();
}
filter_a.rsrp = powf(0.5f, (float)k_rsrp / 4.0f);
filter_a.rsrq = powf(0.5f, (float)k_rsrq / 4.0f);
log_h->info("MEAS: Quantity configuration k_rsrp=%d, k_rsrq=%d\n", k_rsrp, k_rsrq);
// for each measId included in the measIdList within VarMeasConfig
for (auto& m : measIdList) {
remove_varmeas_report(m.first);
}
}
}
bool rrc::rrc_meas::var_meas_cfg::parse_meas_config(const meas_cfg_s* cfg, bool is_ho_reest, uint32_t src_earfcn)
{
// if the received measConfig includes the measObjectToRemoveList
if (cfg->meas_obj_to_rem_list_present) {
measObject_removal(cfg->meas_obj_to_rem_list);
}
// if the received measConfig includes the measObjectToAddModList
if (cfg->meas_obj_to_add_mod_list_present) {
measObject_addmod(cfg->meas_obj_to_add_mod_list);
}
// if the received measConfig includes the reportConfigToRemoveList
if (cfg->report_cfg_to_rem_list_present) {
reportConfig_removal(cfg->report_cfg_to_rem_list);
}
// if the received measConfig includes the reportConfigToAddModList
if (cfg->report_cfg_to_add_mod_list_present) {
reportConfig_addmod(cfg->report_cfg_to_add_mod_list);
}
// if the received measConfig includes the quantityConfig
if (cfg->quant_cfg_present) {
quantity_config(cfg->quant_cfg);
}
// if the received measConfig includes the measIdToRemoveList
if (cfg->meas_id_to_rem_list_present) {
measId_removal(cfg->meas_id_to_rem_list);
}
// if the received measConfig includes the measIdToAddModList
if (cfg->meas_id_to_add_mod_list_present) {
measId_addmod(cfg->meas_id_to_add_mod_list);
}
// if the received measConfig includes the s-Measure
if (cfg->s_measure_present) {
// set the parameter s-Measure within VarMeasConfig to the lowest value of the RSRP ranges indicated by the
// received value of s-Measure
if (cfg->s_measure) {
s_measure_value = range_to_value(report_cfg_eutra_s::trigger_quant_opts::options::rsrp, cfg->s_measure);
}
}
// According to 5.5.6.1, if the new configuration after a HO/Reest does not configure the target frequency, we need to
// swap frequencies with source
if (is_ho_reest) {
cell_t* serv_cell = rrc_ptr->get_serving_cell();
if (serv_cell) {
// Check if the target frequency is configured
uint32_t target_earfcn = serv_cell->get_earfcn();
if (std::find_if(measIdList.begin(), measIdList.end(), [&](const std::pair<uint32_t, meas_id_to_add_mod_s>& c) {
return measObjectsList.count(c.second.meas_obj_id) &&
measObjectsList.at(c.second.meas_obj_id).carrier_freq == target_earfcn;
}) == measIdList.end()) {
// Run HO procedure
ho_reest_finish(src_earfcn, target_earfcn);
}
} else {
log_h->warning("MEAS: Could not get serving cell earfcn\n");
}
}
return true;
}
void rrc::rrc_meas::var_meas_cfg::cell_trigger_state::event_condition(const bool enter, const bool exit)
{
if (enter) {
nof_tti_enter++;
nof_tti_exit = 0;
} else if (exit) {
nof_tti_enter = 0;
nof_tti_exit++;
} else {
nof_tti_enter = 0;
nof_tti_exit = 0;
}
}
bool rrc::rrc_meas::var_meas_cfg::cell_trigger_state::is_enter_equal(const uint32_t nof_tti)
{
return nof_tti < nof_tti_enter;
}
bool rrc::rrc_meas::var_meas_cfg::cell_trigger_state::is_exit_equal(const uint32_t nof_tti)
{
return nof_tti < nof_tti_exit;
}
} // namespace srsue