From 7839ab09dc5adc2b6e1562e6e4631ea9ee04aa05 Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Wed, 18 Aug 2021 17:23:56 +0200 Subject: [PATCH] Add RLF-Reportv9 to srsUE --- srsue/hdr/stack/rrc/rrc.h | 5 + srsue/hdr/stack/rrc/rrc_cell.h | 2 + srsue/hdr/stack/rrc/rrc_common.h | 7 + srsue/hdr/stack/rrc/rrc_meas.h | 2 - srsue/hdr/stack/rrc/rrc_rlf_report.h | 58 ++++++ srsue/src/stack/rrc/CMakeLists.txt | 2 +- srsue/src/stack/rrc/rrc.cc | 75 +++++++ srsue/src/stack/rrc/rrc_cell.cc | 9 + srsue/src/stack/rrc/rrc_common.cc | 51 +++++ srsue/src/stack/rrc/rrc_meas.cc | 91 +++----- srsue/src/stack/rrc/rrc_procedures.cc | 3 + srsue/src/stack/rrc/rrc_rlf_report.cc | 136 ++++++++++++ srsue/src/stack/rrc/test/CMakeLists.txt | 4 + .../src/stack/rrc/test/rrc_rlf_report_test.cc | 196 ++++++++++++++++++ 14 files changed, 575 insertions(+), 66 deletions(-) create mode 100644 srsue/hdr/stack/rrc/rrc_rlf_report.h create mode 100644 srsue/src/stack/rrc/rrc_common.cc create mode 100644 srsue/src/stack/rrc/rrc_rlf_report.cc create mode 100644 srsue/src/stack/rrc/test/rrc_rlf_report_test.cc diff --git a/srsue/hdr/stack/rrc/rrc.h b/srsue/hdr/stack/rrc/rrc.h index 1c3d406e6..c33c837cf 100644 --- a/srsue/hdr/stack/rrc/rrc.h +++ b/srsue/hdr/stack/rrc/rrc.h @@ -16,6 +16,7 @@ #include "rrc_cell.h" #include "rrc_common.h" #include "rrc_metrics.h" +#include "rrc_rlf_report.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/bcd_helpers.h" #include "srsran/common/block_queue.h" @@ -218,6 +219,9 @@ private: const char* get_rb_name(uint32_t lcid) { return srsran::is_lte_rb(lcid) ? rb_id_str[lcid].c_str() : "invalid RB"; } + // Var-RLF-Report class + rrc_rlf_report var_rlf_report; + // Measurements private subclass class rrc_meas; std::unique_ptr measurements; @@ -375,6 +379,7 @@ private: void handle_con_reest(const asn1::rrc::rrc_conn_reest_s& setup); void handle_rrc_con_reconfig(uint32_t lcid, const asn1::rrc::rrc_conn_recfg_s& reconfig); void handle_ue_capability_enquiry(const asn1::rrc::ue_cap_enquiry_s& enquiry); + void handle_ue_info_request(const ue_info_request_r9_s& request); void add_srb(const asn1::rrc::srb_to_add_mod_s& srb_cnfg); void add_drb(const asn1::rrc::drb_to_add_mod_s& drb_cnfg); void release_drb(uint32_t drb_id); diff --git a/srsue/hdr/stack/rrc/rrc_cell.h b/srsue/hdr/stack/rrc/rrc_cell.h index dfed544b5..9df95297e 100644 --- a/srsue/hdr/stack/rrc/rrc_cell.h +++ b/srsue/hdr/stack/rrc/rrc_cell.h @@ -147,6 +147,7 @@ public: bool has_plmn_id(asn1::rrc::plmn_id_s plmn_id) const; uint32_t nof_plmns() const { return has_sib1() ? sib1.cell_access_related_info.plmn_id_list.size() : 0; } srsran::plmn_id_t get_plmn(uint32_t idx) const; + asn1::rrc::plmn_id_s get_plmn_asn1(uint32_t idx) const; uint16_t get_tac() const { return has_sib1() ? (uint16_t)sib1.cell_access_related_info.tac.to_number() : 0; } @@ -161,6 +162,7 @@ public: const asn1::rrc::sib_type13_r9_s* sib13ptr() const { return has_sib13() ? &sib13 : nullptr; } uint32_t get_cell_id() const { return (uint32_t)sib1.cell_access_related_info.cell_id.to_number(); } + asn1::fixed_bitstring<28> get_cell_id_bit() const { return sib1.cell_access_related_info.cell_id; } bool has_sib13() const { return has_valid_sib13; } diff --git a/srsue/hdr/stack/rrc/rrc_common.h b/srsue/hdr/stack/rrc/rrc_common.h index 63bf41540..db11de7bf 100644 --- a/srsue/hdr/stack/rrc/rrc_common.h +++ b/srsue/hdr/stack/rrc/rrc_common.h @@ -15,6 +15,8 @@ namespace srsue { +#include + // RRC states (3GPP 36.331 v10.0.0) typedef enum { RRC_STATE_IDLE = 0, @@ -23,6 +25,11 @@ typedef enum { } rrc_state_t; static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE", "CONNECTED"}; +enum quant_s { quant_rsrp, quant_rsrq }; + +uint8_t rrc_value_to_range(quant_s quant, const float value); +float rrc_range_to_value(quant_s quant, const uint8_t range); + } // namespace srsue #endif // SRSUE_RRC_COMMON_H diff --git a/srsue/hdr/stack/rrc/rrc_meas.h b/srsue/hdr/stack/rrc/rrc_meas.h index 44230de8e..43209feab 100644 --- a/srsue/hdr/stack/rrc/rrc_meas.h +++ b/srsue/hdr/stack/rrc/rrc_meas.h @@ -192,8 +192,6 @@ private: rrc* rrc_ptr = nullptr; // Static functions - static uint8_t value_to_range(const report_cfg_eutra_s::trigger_quant_opts::options q, float value); - static float range_to_value(const report_cfg_eutra_s::trigger_quant_opts::options q, const uint8_t range); static uint8_t value_to_range_nr(const asn1::rrc::thres_nr_r15_c::types_opts::options type, const float value); static float range_to_value_nr(const asn1::rrc::thres_nr_r15_c::types_opts::options type, const uint8_t range); static uint8_t offset_val(const meas_obj_eutra_s& meas_obj); diff --git a/srsue/hdr/stack/rrc/rrc_rlf_report.h b/srsue/hdr/stack/rrc/rrc_rlf_report.h new file mode 100644 index 000000000..85948c931 --- /dev/null +++ b/srsue/hdr/stack/rrc/rrc_rlf_report.h @@ -0,0 +1,58 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#ifndef SRSRAN_RRC_RLF_REPORT_H_ +#define SRSRAN_RRC_RLF_REPORT_H_ + +#include "rrc_cell.h" +#include "srsran/asn1/rrc.h" +#include "srsran/common/common.h" + +namespace srsue { + +using namespace asn1::rrc; + +// RRC RLF-Report class +class rrc_rlf_report +{ +public: + enum failure_type_t { rlf, hof }; + + void init(srsran::task_sched_handle task_sched); + + // Returns true if VarRLF-Report structure has info available + bool has_info(); + + // Called upon T304 expiry (type == hof) or Detection of radio link failure (type == rlf) + void set_failure(meas_cell_list& meas_cells, failure_type_t type); + + // Called upon transmission of ReestablishmentRequest message + void set_reest_gci(const asn1::fixed_bitstring<28>& gci, const asn1::rrc::plmn_id_s& plmn_id); + + // Called upon initiation of RadioReconfiguration message including MobilityInfo IE + void received_ho_command(const asn1::fixed_bitstring<28>& current_gci); + + // Returns a copy of the rlf_report_r9 ASN1 struct + rlf_report_r9_s get_report(); + + // Clears VarRLF-Report contents + void clear(); + +private: + asn1::fixed_bitstring<28> ho_gci; + + bool has_event = false; + rlf_report_r9_s rlf_report = {}; + srsran::timer_handler::unique_timer timer_conn_failure = {}; +}; +} // namespace srsue + +#endif // SRSRAN_RRC_RLF_REPORT_H_ diff --git a/srsue/src/stack/rrc/CMakeLists.txt b/srsue/src/stack/rrc/CMakeLists.txt index c73e0f1e3..aa31cd9a6 100644 --- a/srsue/src/stack/rrc/CMakeLists.txt +++ b/srsue/src/stack/rrc/CMakeLists.txt @@ -8,7 +8,7 @@ add_subdirectory(test) -set(SOURCES rrc.cc rrc_procedures.cc rrc_meas.cc rrc_cell.cc phy_controller.cc) +set(SOURCES rrc.cc rrc_procedures.cc rrc_meas.cc rrc_cell.cc rrc_common.cc rrc_rlf_report.cc phy_controller.cc) add_library(srsue_rrc STATIC ${SOURCES}) set(SOURCES rrc_nr.cc) diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index 28935ef8d..6ddf107eb 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -139,6 +139,8 @@ void rrc::init(phy_interface_rrc_lte* phy_, t311 = task_sched.get_unique_timer(); t304 = task_sched.get_unique_timer(); + var_rlf_report.init(task_sched); + transaction_id = 0; cell_clean_cnt = 0; @@ -674,6 +676,9 @@ void rrc::radio_link_failure_process() // TODO: Generate and store failure report srsran::console("Warning: Detected Radio-Link Failure\n"); + // Store the information in VarRLF-Report + var_rlf_report.set_failure(meas_cells, rrc_rlf_report::rlf); + if (state == RRC_STATE_CONNECTED) { if (security_is_activated) { logger.info("Detected Radio-Link Failure with active AS security. Starting ConnectionReestablishment..."); @@ -857,6 +862,9 @@ void rrc::send_con_restablish_request(reest_cause_e cause, uint16_t crnti, uint1 // Clean reestablishment type reestablishment_successful = false; + // set the reestablishmentCellId in the VarRLF-Report to the global cell identity of the selected cell; + var_rlf_report.set_reest_gci(meas_cells.serving_cell().get_cell_id_bit(), meas_cells.serving_cell().get_plmn_asn1(0)); + if (cause.value != reest_cause_opts::ho_fail) { if (cause.value != reest_cause_opts::other_fail) { pci = meas_cells.serving_cell().get_pci(); @@ -945,6 +953,15 @@ void rrc::send_con_restablish_complete() ul_dcch_msg.msg.set_c1().set_rrc_conn_reest_complete().crit_exts.set_rrc_conn_reest_complete_r8(); ul_dcch_msg.msg.c1().rrc_conn_reest_complete().rrc_transaction_id = transaction_id; + // Include rlf-InfoAvailable + if (var_rlf_report.has_info()) { + ul_dcch_msg.msg.c1().rrc_conn_reest_complete().crit_exts.rrc_conn_reest_complete_r8().non_crit_ext_present = true; + ul_dcch_msg.msg.c1() + .rrc_conn_reest_complete() + .crit_exts.rrc_conn_reest_complete_r8() + .non_crit_ext.rlf_info_available_r9_present = true; + } + send_ul_dcch_msg(srb_to_lcid(lte_srb::srb1), ul_dcch_msg); reestablishment_successful = true; @@ -961,6 +978,13 @@ void rrc::send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg) ul_dcch_msg.msg.c1().rrc_conn_setup_complete().rrc_transaction_id = transaction_id; + // Include rlf-InfoAvailable + if (var_rlf_report.has_info()) { + rrc_conn_setup_complete->non_crit_ext_present = true; + rrc_conn_setup_complete->non_crit_ext.non_crit_ext_present = true; + rrc_conn_setup_complete->non_crit_ext.non_crit_ext.rlf_info_available_r10_present = true; + } + rrc_conn_setup_complete->sel_plmn_id = 1; rrc_conn_setup_complete->ded_info_nas.resize(nas_msg->N_bytes); memcpy(rrc_conn_setup_complete->ded_info_nas.data(), nas_msg->msg, nas_msg->N_bytes); // TODO Check! @@ -1005,6 +1029,13 @@ void rrc::send_rrc_con_reconfig_complete(bool contains_nr_complete) &ul_dcch_msg.msg.set_c1().set_rrc_conn_recfg_complete().crit_exts.set_rrc_conn_recfg_complete_r8(); ul_dcch_msg.msg.c1().rrc_conn_recfg_complete().rrc_transaction_id = transaction_id; + // Include rlf-InfoAvailable + if (var_rlf_report.has_info()) { + rrc_conn_recfg_complete_r8->non_crit_ext_present = true; + rrc_conn_recfg_complete_r8->non_crit_ext.non_crit_ext_present = true; + rrc_conn_recfg_complete_r8->non_crit_ext.non_crit_ext.rlf_info_available_r10_present = true; + } + if (contains_nr_complete == true) { logger.debug("Preparing RRC Connection Reconfig Complete with NR Complete"); @@ -1055,6 +1086,10 @@ void rrc::start_go_idle() void rrc::ho_failed() { ho_handler.trigger(ho_proc::t304_expiry{}); + + // Store the information in VarRLF-Report + var_rlf_report.set_failure(meas_cells, rrc_rlf_report::hof); + start_con_restablishment(reest_cause_e::ho_fail); } @@ -1757,6 +1792,10 @@ void rrc::parse_dl_dcch(uint32_t lcid, unique_byte_buffer_t pdu) case dl_dcch_msg_type_c::c1_c_::types::rrc_conn_release: rrc_connection_release(c1->rrc_conn_release().crit_exts.c1().rrc_conn_release_r8().release_cause.to_string()); break; + case dl_dcch_msg_type_c::c1_c_::types::ue_info_request_r9: + transaction_id = c1->ue_info_request_r9().rrc_transaction_id; + handle_ue_info_request(c1->ue_info_request_r9()); + break; default: logger.error("The provided DL-CCCH message type is not recognized or supported"); break; @@ -2136,6 +2175,42 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry) send_ul_dcch_msg(srb_to_lcid(lte_srb::srb1), ul_dcch_msg); } +/******************************************************************************* + * + * + * + * UEInformationRequest message + * + * + * + *******************************************************************************/ +void rrc::handle_ue_info_request(const ue_info_request_r9_s& request) +{ + logger.debug("Preparing UEInformationResponse message"); + + ul_dcch_msg_s ul_dcch_msg; + ue_info_resp_r9_ies_s* resp = + &ul_dcch_msg.msg.set_c1().set_ue_info_resp_r9().crit_exts.set_c1().set_ue_info_resp_r9(); + ul_dcch_msg.msg.c1().ue_info_resp_r9().rrc_transaction_id = transaction_id; + + // if rach-ReportReq is set to true, set the contents of the rach-Report in the UEInformationResponse message as + // follows + if (request.crit_exts.c1().ue_info_request_r9().rach_report_req_r9) { + // todo... + } + + // Include rlf-Report if rlf-ReportReq is set to true + if (request.crit_exts.c1().ue_info_request_r9().rlf_report_req_r9 && var_rlf_report.has_info()) { + resp->rlf_report_r9_present = true; + resp->rlf_report_r9 = var_rlf_report.get_report(); + + // fixme: should be cleared upon successful delivery + var_rlf_report.clear(); + } + + send_ul_dcch_msg(srb_to_lcid(lte_srb::srb1), ul_dcch_msg); +} + /******************************************************************************* * * diff --git a/srsue/src/stack/rrc/rrc_cell.cc b/srsue/src/stack/rrc/rrc_cell.cc index ef6363eea..577aa7060 100644 --- a/srsue/src/stack/rrc/rrc_cell.cc +++ b/srsue/src/stack/rrc/rrc_cell.cc @@ -33,6 +33,15 @@ srsran::plmn_id_t meas_cell_eutra::get_plmn(uint32_t idx) const } } +asn1::rrc::plmn_id_s meas_cell_eutra::get_plmn_asn1(uint32_t idx) const +{ + if (idx < sib1.cell_access_related_info.plmn_id_list.size() && has_valid_sib1) { + return sib1.cell_access_related_info.plmn_id_list[idx].plmn_id; + } else { + return {}; + } +} + void meas_cell_eutra::set_sib1(const asn1::rrc::sib_type1_s& sib1_) { sib1 = sib1_; diff --git a/srsue/src/stack/rrc/rrc_common.cc b/srsue/src/stack/rrc/rrc_common.cc new file mode 100644 index 000000000..9c1f5a8bb --- /dev/null +++ b/srsue/src/stack/rrc/rrc_common.cc @@ -0,0 +1,51 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsue/hdr/stack/rrc/rrc_common.h" + +namespace srsue { + +uint8_t rrc_value_to_range(quant_s quant, const float value) +{ + uint8_t range = 0; + if (quant == quant_rsrp) { + if (value < -140) { + range = 0; + } else if (value < -44) { + range = 1u + (uint8_t)(value + 140); + } else { + range = 97; + } + } else { + if (value < -19.5) { + range = 0; + } else if (value < -3) { + range = 1u + (uint8_t)(2 * (value + 19.5)); + } else { + range = 34; + } + } + return range; +} + +float rrc_range_to_value(quant_s quant, const uint8_t range) +{ + float val = 0; + if (quant == quant_rsrp) { + val = -140 + (float)range; + } else { + val = -19.5f + (float)range / 2; + } + return val; +} + +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/stack/rrc/rrc_meas.cc b/srsue/src/stack/rrc/rrc_meas.cc index 3e0804ff7..0914a06ea 100644 --- a/srsue/src/stack/rrc/rrc_meas.cc +++ b/srsue/src/stack/rrc/rrc_meas.cc @@ -130,50 +130,6 @@ void rrc::rrc_meas::run_tti() 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; -} - // For thresholds, the actual value is (field value – 156) dBm, except for field value 127, in which case the actual // value is infinity. float rrc::rrc_meas::range_to_value_nr(const asn1::rrc::thres_nr_r15_c::types_opts::options type, const uint8_t range) @@ -303,8 +259,8 @@ void rrc::rrc_meas::var_meas_report_list::generate_report_eutra(meas_results_s* 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); + rc.meas_result.rsrp_result = rrc_value_to_range(quant_rsrp, rsrp_value); + rc.meas_result.rsrq_result = rrc_value_to_range(quant_rsrq, rsrq_value); logger.info("MEAS: Adding to report neighbour=%d, pci=%d, earfcn=%d, rsrp=%+.1f, rsrq=%+.1f", neigh_list.size(), @@ -448,10 +404,8 @@ void rrc::rrc_meas::var_meas_report_list::generate_report(const uint32_t measId) 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()); + report->meas_result_pcell.rsrp_result = rrc_value_to_range(quant_rsrp, serv_cell->get_rsrp()); + report->meas_result_pcell.rsrq_result = rrc_value_to_range(quant_rsrq, serv_cell->get_rsrq()); logger.info("MEAS: Generate report MeasId=%d, Pcell rsrp=%f rsrq=%f", report->meas_id, @@ -805,6 +759,14 @@ void rrc::rrc_meas::var_meas_cfg::eval_triggers_eutra(uint32_t meas_i float Ofs, float Ocs) { + auto asn1_quant_convert = [](report_cfg_eutra_s::trigger_quant_e_ q) { + if (q == report_cfg_eutra_s::trigger_quant_opts::rsrp) { + return quant_rsrp; + } else { + return quant_rsrq; + } + }; + 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(); @@ -823,17 +785,21 @@ void rrc::rrc_meas::var_meas_cfg::eval_triggers_eutra(uint32_t meas_i 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()); + thresh = rrc_range_to_value(asn1_quant_convert(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()); + thresh = rrc_range_to_value(asn1_quant_convert(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()); + thresh = rrc_range_to_value(asn1_quant_convert(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()); + thresh = rrc_range_to_value(asn1_quant_convert(report_cfg.trigger_quant), + event_id.event_a2().a2_thres.thres_rsrq()); } enter_condition = Ms + hyst < thresh; exit_condition = Ms - hyst > thresh; @@ -884,7 +850,7 @@ void rrc::rrc_meas::var_meas_cfg::eval_triggers_eutra(uint32_t meas_i } else { range = event_id.event_a4().a4_thres.thres_rsrq(); } - thresh = range_to_value(report_cfg.trigger_quant.value, range); + thresh = rrc_range_to_value(asn1_quant_convert(report_cfg.trigger_quant.value), range); enter_condition = Mn + Ofn + Ocn - hyst > thresh; exit_condition = Mn + Ofn + Ocn + hyst < thresh; break; @@ -899,8 +865,8 @@ void rrc::rrc_meas::var_meas_cfg::eval_triggers_eutra(uint32_t meas_i } 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); + th1 = rrc_range_to_value(asn1_quant_convert(report_cfg.trigger_quant.value), range); + th2 = rrc_range_to_value(asn1_quant_convert(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; @@ -1470,19 +1436,18 @@ void rrc::rrc_meas::var_meas_cfg::log_debug_trigger_value_eutra(const eutra_even switch (e.type()) { case eutra_event_s::event_id_c_::types_opts::event_a1: logger.debug("MEAS: A1-threshold=%.1f dBm", - range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a1().a1_thres.thres_rsrp())); + rrc_range_to_value(quant_rsrp, e.event_a1().a1_thres.thres_rsrp())); break; case eutra_event_s::event_id_c_::types_opts::event_a2: logger.debug("MEAS: A2-threshold=%.1f dBm", - range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a2().a2_thres.thres_rsrp())); + rrc_range_to_value(quant_rsrp, e.event_a2().a2_thres.thres_rsrp())); break; case eutra_event_s::event_id_c_::types_opts::event_a3: - logger.debug("MEAS: A3-offset=%.1f dB", - range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a3().a3_offset)); + logger.debug("MEAS: A3-offset=%.1f dB", rrc_range_to_value(quant_rsrp, e.event_a3().a3_offset)); break; case eutra_event_s::event_id_c_::types_opts::event_a4: logger.debug("MEAS: A4-threshold=%.1f dBm", - range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a4().a4_thres.thres_rsrp())); + rrc_range_to_value(quant_rsrp, e.event_a4().a4_thres.thres_rsrp())); break; default: logger.debug("MEAS: Unsupported"); @@ -1597,7 +1562,7 @@ bool rrc::rrc_meas::var_meas_cfg::parse_meas_config(const meas_cfg_s* cfg, bool // 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); + s_measure_value = rrc_range_to_value(quant_rsrp, cfg->s_measure); } } diff --git a/srsue/src/stack/rrc/rrc_procedures.cc b/srsue/src/stack/rrc/rrc_procedures.cc index 1dc96cf76..d98262a75 100644 --- a/srsue/src/stack/rrc/rrc_procedures.cc +++ b/srsue/src/stack/rrc/rrc_procedures.cc @@ -1656,6 +1656,9 @@ srsran::proc_outcome_t rrc::ho_proc::init(const asn1::rrc::rrc_conn_recfg_s& rrc rrc_ptr->t304.set(mob_ctrl_info->t304.to_number(), [this](uint32_t tid) { rrc_ptr->timer_expired(tid); }); rrc_ptr->t304.run(); + // Indicate RLF-Report that a new HO has been received + rrc_ptr->var_rlf_report.received_ho_command(rrc_ptr->meas_cells.serving_cell().get_cell_id_bit()); + // starting at start synchronising to the DL of the target PCell rrc_ptr->set_serving_cell(target_cell, false); Info("Starting cell selection of target cell PCI=%d EARFCN=%d", target_cell.pci, target_cell.earfcn); diff --git a/srsue/src/stack/rrc/rrc_rlf_report.cc b/srsue/src/stack/rrc/rrc_rlf_report.cc new file mode 100644 index 000000000..c4930af9c --- /dev/null +++ b/srsue/src/stack/rrc/rrc_rlf_report.cc @@ -0,0 +1,136 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsue/hdr/stack/rrc/rrc_rlf_report.h" +#include "srsue/hdr/stack/rrc/rrc_common.h" + +namespace srsue { + +void rrc_rlf_report::init(srsran::task_sched_handle task_sched) +{ + timer_conn_failure = task_sched.get_unique_timer(); +} + +// Returns true if VarRLF-Report structure has info available +bool rrc_rlf_report::has_info() +{ + return has_event; +} + +// Called upon T304 expiry (type == hof) or Detection of radio link failure (type == rlf) +void rrc_rlf_report::set_failure(meas_cell_list& meas_cells, failure_type_t type) +{ + has_event = true; + + // clear the information included in VarRLF-Report, if any + rlf_report = {}; + + // set the plmn-Identity to the RPLMN + + // set the measResultLastServCell to include the RSRP and RSRQ, if available, of the PCell based on + // measurements collected up to the moment the UE detected radio link failure + rlf_report.meas_result_last_serv_cell_r9.rsrp_result_r9 = + rrc_value_to_range(quant_rsrp, meas_cells.serving_cell().get_rsrp()); + rlf_report.meas_result_last_serv_cell_r9.rsrq_result_r9 = + rrc_value_to_range(quant_rsrq, meas_cells.serving_cell().get_rsrq()); + rlf_report.meas_result_last_serv_cell_r9.rsrq_result_r9_present = true; + + // set the measResultNeighCells to include the best measured cells, other than the PCell, ordered such that + // the best cell is listed first, and based on measurements collected up to the moment the UE detected radio + // link failure + if (meas_cells.nof_neighbours() > 0) { + rlf_report.meas_result_neigh_cells_r9_present = true; + rlf_report.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present = true; + rlf_report.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.clear(); + meas_cells.sort_neighbour_cells(); + // It is not clear how the sorting and grouping of cells per frequency must be done. + // We use a separate MeasResultList2EUTRA-r9 struct for each pci/frequency pair + for (const auto& f : meas_cells) { + meas_result2_eutra_r9_s meas2 = {}; + meas2.carrier_freq_r9 = f->get_earfcn(); + meas_result_eutra_s meas = {}; + meas.pci = f->get_pci(); + meas.meas_result.rsrp_result_present = true; + meas.meas_result.rsrq_result_present = true; + meas.meas_result.rsrp_result = rrc_value_to_range(quant_rsrp, f->get_rsrp()); + meas.meas_result.rsrq_result = rrc_value_to_range(quant_rsrq, f->get_rsrq()); + meas2.meas_result_list_r9.push_back(meas); + rlf_report.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.push_back(meas2); + } + } + + // set the failedPCellId to the global cell identity, if available, and otherwise to the physical cell identity and + // carrier frequency of the PCell where radio link failure is detected; + rlf_report.failed_pcell_id_r10.set_present(true); + if (meas_cells.serving_cell().has_sib1()) { + rlf_report.failed_pcell_id_r10->set_cell_global_id_r10().cell_id = meas_cells.serving_cell().get_cell_id_bit(); + rlf_report.failed_pcell_id_r10->cell_global_id_r10().plmn_id = meas_cells.serving_cell().get_plmn_asn1(0); + } else { + rlf_report.failed_pcell_id_r10->set_pci_arfcn_r10(); + rlf_report.failed_pcell_id_r10->pci_arfcn_r10().pci_r10 = meas_cells.serving_cell().get_pci(); + rlf_report.failed_pcell_id_r10->pci_arfcn_r10().carrier_freq_r10 = meas_cells.serving_cell().get_earfcn(); + } + + // if an RRCConnectionReconfiguration message including the mobilityControlInfo was received before the + // connection failure + if (timer_conn_failure.is_running()) { + timer_conn_failure.stop(); + + // include previousPCellId and set it to the global cell identity of the PCell where the last + // RRCConnectionReconfiguration including the mobilityControlInfo message was received; + rlf_report.prev_pcell_id_r10.set_present(true); + rlf_report.prev_pcell_id_r10->cell_id = ho_gci; + rlf_report.prev_pcell_id_r10->plmn_id = meas_cells.serving_cell().get_plmn_asn1(0); + + // set the timeConnFailure to the elapsed time since reception of the last + // RRCConnectionReconfiguration message including the mobilityControlInfo; + rlf_report.time_conn_fail_r10_present = true; + rlf_report.time_conn_fail_r10 = timer_conn_failure.time_elapsed() / 100; // 1 unit = 100 ms + } + + // set the connectionFailureType + rlf_report.conn_fail_type_r10_present = true; + rlf_report.conn_fail_type_r10 = + type == rlf ? rlf_report_r9_s::conn_fail_type_r10_opts::rlf : rlf_report_r9_s::conn_fail_type_r10_opts::hof; + + rlf_report.ext = true; +} + +void rrc_rlf_report::set_reest_gci(const asn1::fixed_bitstring<28>& gci, const asn1::rrc::plmn_id_s& plmn_id) +{ + rlf_report.reest_cell_id_r10.set_present(true); + rlf_report.reest_cell_id_r10->cell_id = gci; + rlf_report.reest_cell_id_r10->plmn_id = plmn_id; +} + +void rrc_rlf_report::received_ho_command(const asn1::fixed_bitstring<28>& current_gci) +{ + if (timer_conn_failure.is_valid()) { + timer_conn_failure.stop(); + timer_conn_failure.run(); + ho_gci = current_gci; + } +} + +rlf_report_r9_s rrc_rlf_report::get_report() +{ + return rlf_report; +} + +// Clears VarRLF-Report contents +void rrc_rlf_report::clear() +{ + has_event = false; + rlf_report = {}; +} + +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/stack/rrc/test/CMakeLists.txt b/srsue/src/stack/rrc/test/CMakeLists.txt index ef592dc2d..55c77ef15 100644 --- a/srsue/src/stack/rrc/test/CMakeLists.txt +++ b/srsue/src/stack/rrc/test/CMakeLists.txt @@ -22,6 +22,10 @@ add_executable(rrc_cell_test rrc_cell_test.cc) target_link_libraries(rrc_cell_test srsue_rrc srsue_upper srsran_pdcp srsran_phy rrc_asn1 rrc_nr_asn1) add_test(rrc_cell_test rrc_cell_test) +add_executable(rrc_rlf_report_test rrc_rlf_report_test.cc) +target_link_libraries(rrc_rlf_report_test srsue_rrc srsue_upper srsran_pdcp srsran_phy rrc_asn1 rrc_nr_asn1) +add_test(rrc_rlf_report_test rrc_rlf_report_test) + add_executable(ue_rrc_nr_test ue_rrc_nr_test.cc) target_link_libraries(ue_rrc_nr_test srsue_rrc_nr srsue_upper srsran_common srsran_pdcp srsran_phy rrc_asn1 rrc_nr_asn1) diff --git a/srsue/src/stack/rrc/test/rrc_rlf_report_test.cc b/srsue/src/stack/rrc/test/rrc_rlf_report_test.cc new file mode 100644 index 000000000..c68b61357 --- /dev/null +++ b/srsue/src/stack/rrc/test/rrc_rlf_report_test.cc @@ -0,0 +1,196 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/common/test_common.h" +#include "srsue/hdr/stack/rrc/rrc_cell.h" +#include "srsue/hdr/stack/rrc/rrc_rlf_report.h" + +using namespace srsue; + +int test_single() +{ + srsran::task_scheduler task_sched; + rrc_rlf_report rlf_report; + meas_cell_list list{&task_sched}; + + phy_meas_t pmeas; + pmeas.rsrp = -20; + pmeas.pci = 1; + pmeas.earfcn = 3400; + pmeas.rsrq = -10; + + list.add_meas_cell(pmeas); + list.set_serving_cell(phy_cell_t{1, 3400}, false); + + rlf_report.set_failure(list, rrc_rlf_report::rlf); + + asn1::json_writer jw; + asn1::rrc::rlf_report_r9_s out = rlf_report.get_report(); + out.to_json(jw); + printf("test_single: %s\n", jw.to_string().c_str()); + + TESTASSERT(!out.meas_result_neigh_cells_r9_present); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.size() == 0); + TESTASSERT(out.failed_pcell_id_r10.is_present()); + TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().pci_r10 == 1); + TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().carrier_freq_r10 = 3400); + TESTASSERT(out.conn_fail_type_r10_present); + TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::rlf); + + return SRSRAN_SUCCESS; +} + +int test_neighbours() +{ + srsran::task_scheduler task_sched; + rrc_rlf_report rlf_report; + meas_cell_list list{&task_sched}; + + phy_meas_t pmeas; + pmeas.rsrp = -20; + pmeas.pci = 1; + pmeas.earfcn = 3400; + pmeas.rsrq = -10; + + list.add_meas_cell(pmeas); + pmeas.pci = 4; + list.add_meas_cell(pmeas); + pmeas.pci = 6; + list.add_meas_cell(pmeas); + list.set_serving_cell(phy_cell_t{4, 3400}, false); + + TESTASSERT(!rlf_report.has_info()); + + rlf_report.set_failure(list, rrc_rlf_report::hof); + + asn1::json_writer jw; + asn1::rrc::rlf_report_r9_s out = rlf_report.get_report(); + out.to_json(jw); + printf("test_neighbours: %s\n", jw.to_string().c_str()); + + TESTASSERT(out.meas_result_neigh_cells_r9_present); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.size() == 2); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[0].carrier_freq_r9 = 3400); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[0].meas_result_list_r9[0].pci == 1); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[1].carrier_freq_r9 = 3400); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[1].meas_result_list_r9[0].pci == 6); + TESTASSERT(out.failed_pcell_id_r10.is_present()); + TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().pci_r10 == 4); + TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().carrier_freq_r10 = 3400); + TESTASSERT(out.conn_fail_type_r10_present); + TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::hof); + + TESTASSERT(rlf_report.has_info()); + + return SRSRAN_SUCCESS; +} + +int test_reest() +{ + srsran::task_scheduler task_sched; + rrc_rlf_report rlf_report; + meas_cell_list list{&task_sched}; + + phy_meas_t pmeas; + pmeas.rsrp = -20; + pmeas.pci = 1; + pmeas.earfcn = 3400; + pmeas.rsrq = -10; + + list.add_meas_cell(pmeas); + pmeas.pci = 4; + list.add_meas_cell(pmeas); + pmeas.pci = 6; + list.add_meas_cell(pmeas); + list.set_serving_cell(phy_cell_t{4, 3400}, false); + + TESTASSERT(!rlf_report.has_info()); + + rlf_report.set_failure(list, rrc_rlf_report::hof); + rlf_report.set_reest_gci(list.serving_cell().get_cell_id_bit(), list.serving_cell().get_plmn_asn1(0)); + + asn1::json_writer jw; + asn1::rrc::rlf_report_r9_s out = rlf_report.get_report(); + out.to_json(jw); + printf("test_reest: %s\n", jw.to_string().c_str()); + + TESTASSERT(out.meas_result_neigh_cells_r9_present); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present); + TESTASSERT(out.failed_pcell_id_r10.is_present()); + TESTASSERT(out.conn_fail_type_r10_present); + TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::hof); + TESTASSERT(out.reest_cell_id_r10.is_present()); + + TESTASSERT(rlf_report.has_info()); + + return SRSRAN_SUCCESS; +} + +int test_ho() +{ + srsran::task_scheduler task_sched; + rrc_rlf_report rlf_report; + meas_cell_list list{&task_sched}; + + rlf_report.init(&task_sched); + + phy_meas_t pmeas; + pmeas.rsrp = -20; + pmeas.pci = 1; + pmeas.earfcn = 3400; + pmeas.rsrq = -10; + + list.add_meas_cell(pmeas); + pmeas.pci = 4; + list.add_meas_cell(pmeas); + pmeas.pci = 6; + list.add_meas_cell(pmeas); + list.set_serving_cell(phy_cell_t{4, 3400}, false); + + TESTASSERT(!rlf_report.has_info()); + + rlf_report.received_ho_command(list.serving_cell().get_cell_id_bit()); + for (int i = 0; i < 1000; i++) { + task_sched.tic(); + task_sched.run_pending_tasks(); + } + rlf_report.set_failure(list, rrc_rlf_report::hof); + rlf_report.set_reest_gci(list.serving_cell().get_cell_id_bit(), list.serving_cell().get_plmn_asn1(0)); + + asn1::json_writer jw; + asn1::rrc::rlf_report_r9_s out = rlf_report.get_report(); + out.to_json(jw); + printf("test_ho: %s\n", jw.to_string().c_str()); + + TESTASSERT(out.meas_result_neigh_cells_r9_present); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present); + TESTASSERT(out.failed_pcell_id_r10.is_present()); + TESTASSERT(out.conn_fail_type_r10_present); + TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::hof); + TESTASSERT(out.reest_cell_id_r10.is_present()); + + TESTASSERT(rlf_report.has_info()); + + return SRSRAN_SUCCESS; +} + +int main() +{ + TESTASSERT(test_single() == SRSRAN_SUCCESS); + TESTASSERT(test_neighbours() == SRSRAN_SUCCESS); + TESTASSERT(test_reest() == SRSRAN_SUCCESS); + TESTASSERT(test_ho() == SRSRAN_SUCCESS); + printf("Success\n"); + + return SRSRAN_SUCCESS; +}