1139 lines
44 KiB
C++
1139 lines
44 KiB
C++
/*
|
||
* 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
|