From 52716f871653cb7664ecaff96451c882be639f2e Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Fri, 13 Dec 2019 13:04:47 +0100 Subject: [PATCH] Process HO complete in the background to avoid heap-after-use bug when PHY measurements are reported during a HO rrc_meas refactor. Need to split commit Fix typo Temporal commit Apply rx_gain_offset to neighbour cell measurements srsLTE: modify TESTASSERT Macro to follow codeline SRSUE: prevent RRC from having serving cell in neighbour list SRSUE: DL HARQ does not need Time Aligment Timer. UL is disabled using PUCCH resources release SRSUE: extend intra-frequency to CA SCell SRSUE: fix confusing/ambiguous code in the RRC measurements and fix concurrency issue SRSUE: remove RRC measurement report triggers when measurements are modified or HO succesful SRSUE: fix compilation issues and Reest SIB indexes Fixes sync using incorrect cell configuration when search cell does not find a correct cell Small refactor to remove measurement report triggers always after removing measurement SRSUE: Removed SIC PSS from UE SRSUE: fix inter-frequency reestablishment and added more traces SRSUE: Fix compilation issue --- CMakeLists.txt | 6 + lib/include/srslte/common/timers.h | 2 + lib/include/srslte/interfaces/ue_interfaces.h | 30 +- lib/include/srslte/upper/rlc.h | 1 + lib/include/srslte/upper/rlc_common.h | 13 +- lib/src/upper/rlc.cc | 12 + lib/test/asn1/srslte_asn1_rrc_mcch_test.cc | 4 +- lib/test/asn1/srslte_asn1_rrc_meas_test.cc | 4 +- lib/test/upper/rlc_am_test.cc | 4 +- lib/test/upper/rlc_common_test.cc | 4 +- lib/test/upper/rlc_um_test.cc | 4 +- srsue/hdr/phy/phy.h | 9 +- srsue/hdr/phy/phy_common.h | 4 +- srsue/hdr/phy/scell/intra_measure.h | 27 +- srsue/hdr/phy/scell/measure.h | 75 -- srsue/hdr/phy/scell/scell_recv.h | 21 +- srsue/hdr/phy/sync.h | 8 +- srsue/hdr/stack/mac/dl_harq.h | 2 - srsue/hdr/stack/rrc/rrc.h | 198 +-- srsue/hdr/stack/rrc/rrc_meas.h | 168 +++ srsue/hdr/stack/rrc/rrc_procedures.h | 4 + srsue/hdr/stack/ue_stack_lte.h | 5 +- srsue/src/main.cc | 4 - srsue/src/phy/cc_worker.cc | 11 +- srsue/src/phy/phy.cc | 39 +- srsue/src/phy/phy_common.cc | 7 +- srsue/src/phy/scell/async_scell_recv.cc | 2 - srsue/src/phy/scell/intra_measure.cc | 151 +-- srsue/src/phy/scell/measure.cc | 233 ---- srsue/src/phy/scell/scell_recv.cc | 212 +-- srsue/src/phy/sf_worker.cc | 33 +- srsue/src/phy/sync.cc | 247 ++-- srsue/src/stack/mac/dl_harq.cc | 27 +- srsue/src/stack/mac/mac.cc | 5 +- srsue/src/stack/rrc/CMakeLists.txt | 2 +- srsue/src/stack/rrc/rrc.cc | 1096 ++++------------ srsue/src/stack/rrc/rrc_meas.cc | 1138 +++++++++++++++++ srsue/src/stack/rrc/rrc_procedures.cc | 73 +- srsue/test/phy/scell_search_test.cc | 51 +- srsue/test/phy/ue_phy_test.cc | 11 +- srsue/test/ttcn3/hdr/lte_ttcn3_phy.h | 2 +- srsue/test/ttcn3/src/lte_ttcn3_phy.cc | 2 +- srsue/test/upper/CMakeLists.txt | 4 + srsue/test/upper/rrc_meas_test.cc | 983 ++++++++++++++ srsue/test/upper/rrc_reconfig_test.cc | 9 +- srsue/ue.conf.example | 5 - 46 files changed, 3051 insertions(+), 1901 deletions(-) delete mode 100644 srsue/hdr/phy/scell/measure.h create mode 100644 srsue/hdr/stack/rrc/rrc_meas.h delete mode 100644 srsue/src/phy/scell/measure.cc create mode 100644 srsue/src/stack/rrc/rrc_meas.cc create mode 100644 srsue/test/upper/rrc_meas_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index c2d075544..4672271ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ option(ENABLE_TTCN3 "Enable TTCN3 test binaries" OFF) option(BUILD_STATIC "Attempt to statically link external deps" OFF) option(RPATH "Enable RPATH" OFF) option(ENABLE_ASAN "Enable gcc/clang address sanitizer" OFF) +option(ENABLE_GCOV "Enable gcc/clang address sanitizer" OFF) option(ENABLE_MSAN "Enable clang memory sanitizer" OFF) option(ENABLE_TIDY "Enable clang tidy" OFF) @@ -435,6 +436,11 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fPIE -pie") endif (ENABLE_MSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang") + if (ENABLE_GCOV) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + endif (ENABLE_GCOV) + endif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") diff --git a/lib/include/srslte/common/timers.h b/lib/include/srslte/common/timers.h index 40f68cf51..83080f755 100644 --- a/lib/include/srslte/common/timers.h +++ b/lib/include/srslte/common/timers.h @@ -188,6 +188,8 @@ public: void stop() { impl()->stop(); } + void clear() { impl()->clear(); } + void release() { impl()->clear(); diff --git a/lib/include/srslte/interfaces/ue_interfaces.h b/lib/include/srslte/interfaces/ue_interfaces.h index d3d7f9638..65ec9f9c2 100644 --- a/lib/include/srslte/interfaces/ue_interfaces.h +++ b/lib/include/srslte/interfaces/ue_interfaces.h @@ -28,6 +28,7 @@ #ifndef SRSLTE_UE_INTERFACES_H #define SRSLTE_UE_INTERFACES_H +#include #include #include "rrc_interface_types.h" @@ -133,9 +134,17 @@ public: class rrc_interface_phy_lte { public: - virtual void in_sync() = 0; - virtual void out_of_sync() = 0; - virtual void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn = -1, int pci = -1) = 0; + // Measurement object from phy + typedef struct { + float rsrp; + float rsrq; + uint32_t earfcn; + uint32_t pci; + } phy_meas_t; + + virtual void in_sync() = 0; + virtual void out_of_sync() = 0; + virtual void new_cell_meas(std::vector& meas) = 0; }; // RRC interface for NAS @@ -273,6 +282,7 @@ public: virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0; virtual bool has_bearer(uint32_t lcid) = 0; virtual bool has_data(const uint32_t lcid) = 0; + virtual bool is_suspended(const uint32_t lcid) = 0; virtual void write_sdu(uint32_t lcid, srslte::unique_byte_buffer_t sdu, bool blocking = true) = 0; }; @@ -514,7 +524,6 @@ typedef struct { uint32_t estimator_fil_order = 4; float snr_to_cqi_offset = 0.0f; std::string sss_algorithm = "full"; - bool sic_pss_enabled = false; float rx_gain_offset = 62; bool pdsch_csi_enabled = true; bool pdsch_8bit_decoder = false; @@ -582,10 +591,6 @@ public: class phy_interface_rrc_lte { public: - virtual void get_current_cell(srslte_cell_t* cell, uint32_t* current_earfcn = NULL) = 0; - virtual uint32_t get_current_earfcn() = 0; - virtual uint32_t get_current_pci() = 0; - virtual void set_config(srslte::phy_cfg_t& config, uint32_t cc_idx = 0, uint32_t earfcn = 0, @@ -596,9 +601,8 @@ public: virtual void set_config_mbsfn_mcch(const srslte::mcch_msg_t& mcch) = 0; /* Measurements interface */ - virtual void meas_reset() = 0; - virtual int meas_start(uint32_t earfcn, int pci = -1) = 0; - virtual int meas_stop(uint32_t earfcn, int pci = -1) = 0; + virtual void set_cells_to_meas(uint32_t earfcn, std::set& pci) = 0; + virtual void meas_stop() = 0; typedef struct { enum { CELL_FOUND = 0, CELL_NOT_FOUND, ERROR } found; @@ -606,8 +610,8 @@ public: } cell_search_ret_t; typedef struct { - srslte_cell_t cell; - uint32_t earfcn; + uint32_t pci; + uint32_t earfcn; } phy_cell_t; /* Cell search and selection procedures */ diff --git a/lib/include/srslte/upper/rlc.h b/lib/include/srslte/upper/rlc.h index 777fb1427..d8e538806 100644 --- a/lib/include/srslte/upper/rlc.h +++ b/lib/include/srslte/upper/rlc.h @@ -58,6 +58,7 @@ public: // MAC interface bool has_data(const uint32_t lcid); + bool is_suspended(const uint32_t lcid); uint32_t get_buffer_state(const uint32_t lcid); uint32_t get_total_mch_buffer_state(uint32_t lcid); int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); diff --git a/lib/include/srslte/upper/rlc_common.h b/lib/include/srslte/upper/rlc_common.h index 05b2a06bb..065ec251c 100644 --- a/lib/include/srslte/upper/rlc_common.h +++ b/lib/include/srslte/upper/rlc_common.h @@ -221,17 +221,17 @@ public: bool suspend() { - if (is_suspended) { + if (suspended) { return false; } - is_suspended = true; + suspended = true; return true; } // Pops all PDUs from queue and calls write_pdu() method for the bearer type bool resume() { - if (!is_suspended) { + if (!suspended) { return false; } pdu_t p; @@ -240,13 +240,13 @@ public: write_pdu(p.payload, p.nof_bytes); free(p.payload); } - is_suspended = false; + suspended = false; return true; } void write_pdu_s(uint8_t* payload, uint32_t nof_bytes) { - if (is_suspended) { + if (suspended) { queue_pdu(payload, nof_bytes); } else { write_pdu(payload, nof_bytes); @@ -265,12 +265,13 @@ public: // MAC interface virtual bool has_data() = 0; + bool is_suspended() { return suspended; }; virtual uint32_t get_buffer_state() = 0; virtual int read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; virtual void write_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; private: - bool is_suspended = false; + bool suspended = false; // Enqueues the PDU in the resume queue void queue_pdu(uint8_t* payload, uint32_t nof_bytes) diff --git a/lib/src/upper/rlc.cc b/lib/src/upper/rlc.cc index 5eb16e9ee..336a74913 100644 --- a/lib/src/upper/rlc.cc +++ b/lib/src/upper/rlc.cc @@ -260,6 +260,18 @@ bool rlc::has_data(uint32_t lcid) return has_data; } +bool rlc::is_suspended(const uint32_t lcid) +{ + bool ret = false; + + pthread_rwlock_rdlock(&rwlock); + if (valid_lcid(lcid)) { + ret = rlc_array.at(lcid)->is_suspended(); + } + pthread_rwlock_unlock(&rwlock); + + return ret; +} uint32_t rlc::get_buffer_state(uint32_t lcid) { diff --git a/lib/test/asn1/srslte_asn1_rrc_mcch_test.cc b/lib/test/asn1/srslte_asn1_rrc_mcch_test.cc index a7882627f..ddf591f88 100644 --- a/lib/test/asn1/srslte_asn1_rrc_mcch_test.cc +++ b/lib/test/asn1/srslte_asn1_rrc_mcch_test.cc @@ -36,7 +36,7 @@ using namespace asn1::rrc; } \ } -int basic_test() +int meas_obj_test() { srslte::log_filter log1("RRC"); log1.set_level(srslte::LOG_LEVEL_DEBUG); @@ -130,6 +130,6 @@ int basic_test() int main(int argc, char** argv) { - TESTASSERT(basic_test() == 0); + TESTASSERT(meas_obj_test() == 0); return 0; } diff --git a/lib/test/asn1/srslte_asn1_rrc_meas_test.cc b/lib/test/asn1/srslte_asn1_rrc_meas_test.cc index 267503598..f5a836eb8 100644 --- a/lib/test/asn1/srslte_asn1_rrc_meas_test.cc +++ b/lib/test/asn1/srslte_asn1_rrc_meas_test.cc @@ -38,7 +38,7 @@ using namespace asn1; using namespace asn1::rrc; -int basic_test() +int meas_obj_test() { srslte::log_filter log1("RRC"); log1.set_level(srslte::LOG_LEVEL_DEBUG); @@ -100,6 +100,6 @@ int basic_test() int main(int argc, char** argv) { - TESTASSERT(basic_test() == 0); + TESTASSERT(meas_obj_test() == 0); return 0; } diff --git a/lib/test/upper/rlc_am_test.cc b/lib/test/upper/rlc_am_test.cc index 4e4c6ca1a..2ff69ebc7 100644 --- a/lib/test/upper/rlc_am_test.cc +++ b/lib/test/upper/rlc_am_test.cc @@ -131,7 +131,7 @@ void basic_test_tx(rlc_am_lte* rlc, byte_buffer_t pdu_bufs[NBUFS]) assert(0 == rlc->get_buffer_state()); } -bool basic_test() +bool meas_obj_test() { srslte::log_filter log1("RLC_AM_1"); srslte::log_filter log2("RLC_AM_2"); @@ -1647,7 +1647,7 @@ bool status_pdu_test() int main(int argc, char** argv) { - if (basic_test()) { + if (meas_obj_test()) { printf("basic_test failed\n"); exit(-1); }; diff --git a/lib/test/upper/rlc_common_test.cc b/lib/test/upper/rlc_common_test.cc index e96935bba..939325788 100644 --- a/lib/test/upper/rlc_common_test.cc +++ b/lib/test/upper/rlc_common_test.cc @@ -71,7 +71,7 @@ public: uint32_t expected_sdu_len; }; -int basic_test() +int meas_obj_test() { srslte::log_filter log1("RLC_1"); srslte::log_filter log2("RLC_2"); @@ -204,7 +204,7 @@ int basic_test() int main(int argc, char** argv) { - if (basic_test()) { + if (meas_obj_test()) { return -1; } byte_buffer_pool::get_instance()->cleanup(); diff --git a/lib/test/upper/rlc_um_test.cc b/lib/test/upper/rlc_um_test.cc index fdcea3cf8..3f2584a89 100644 --- a/lib/test/upper/rlc_um_test.cc +++ b/lib/test/upper/rlc_um_test.cc @@ -74,7 +74,7 @@ public: rlc_um_lte rlc1, rlc2; }; -int basic_test() +int meas_obj_test() { rlc_um_lte_test_context1 ctxt; @@ -392,7 +392,7 @@ int reassmble_test2() int main(int argc, char** argv) { - if (basic_test()) { + if (meas_obj_test()) { return -1; } byte_buffer_pool::get_instance()->cleanup(); diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index 4f2a69135..4f7f53b9c 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -72,9 +72,8 @@ public: cell_search_ret_t cell_search(phy_cell_t* cell) final; bool cell_select(phy_cell_t* cell) final; - void meas_reset() final; - int meas_start(uint32_t earfcn, int pci) final; - int meas_stop(uint32_t earfcn, int pci) final; + void set_cells_to_meas(uint32_t earfcn, std::set& pci) final; + void meas_stop() final; // also MAC interface bool cell_is_camping() final; @@ -120,10 +119,6 @@ public: uint32_t get_current_tti() final; - void get_current_cell(srslte_cell_t* cell, uint32_t* current_earfcn) final; - uint32_t get_current_earfcn() final; - uint32_t get_current_pci() final; - void start_plot() final; const static int MAX_WORKERS = 4; diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index 37c35cd2a..09f97402e 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -63,8 +63,8 @@ public: float cur_pusch_power = 0.0f; float avg_rsrp[SRSLTE_MAX_CARRIERS] = {}; float avg_rsrp_dbm[SRSLTE_MAX_CARRIERS] = {}; - float avg_rsrq_db = 0.0f; - float avg_rssi_dbm = 0.0f; + float avg_rsrq_db[SRSLTE_MAX_CARRIERS] = {}; + float avg_rssi_dbm[SRSLTE_MAX_CARRIERS] = {}; float rx_gain_offset = 0.0f; float avg_snr_db_cqi[SRSLTE_MAX_CARRIERS] = {}; float avg_noise[SRSLTE_MAX_CARRIERS] = {}; diff --git a/srsue/hdr/phy/scell/intra_measure.h b/srsue/hdr/phy/scell/intra_measure.h index df17795c4..d02fc254e 100644 --- a/srsue/hdr/phy/scell/intra_measure.h +++ b/srsue/hdr/phy/scell/intra_measure.h @@ -39,36 +39,33 @@ public: ~intra_measure(); void init(phy_common* common, rrc_interface_phy_lte* rrc, srslte::log* log_h); void stop(); - void add_cell(int pci); - void rem_cell(int pci); void set_primary_cell(uint32_t earfcn, srslte_cell_t cell); - void clear_cells(); - int get_offset(uint32_t pci); + void set_cells_to_meas(std::set& pci); + void meas_stop(); void write(uint32_t tti, cf_t* data, uint32_t nsamples); + uint32_t get_earfcn() { return current_earfcn; }; private: void run_thread(); const static int INTRA_FREQ_MEAS_PRIO = DEFAULT_PRIORITY + 5; - scell_recv scell = {}; - rrc_interface_phy_lte* rrc = nullptr; - srslte::log* log_h = nullptr; - phy_common* common = nullptr; - uint32_t current_earfcn = 0; - uint32_t current_sflen = 0; - srslte_cell_t primary_cell = {}; - std::vector active_pci; + scell_recv scell = {}; + rrc_interface_phy_lte* rrc = nullptr; + srslte::log* log_h = nullptr; + phy_common* common = nullptr; + uint32_t current_earfcn = 0; + uint32_t current_sflen = 0; + srslte_cell_t serving_cell = {}; + std::set active_pci = {}; + std::mutex active_pci_mutex = {}; srslte::tti_sync_cv tti_sync; cf_t* search_buffer = nullptr; - scell_recv::cell_info_t info[scell_recv::MAX_CELLS] = {}; - bool running = false; bool receive_enabled = false; bool receiving = false; - uint32_t measure_tti = 0; uint32_t receive_cnt = 0; srslte_ringbuffer_t ring_buffer = {}; diff --git a/srsue/hdr/phy/scell/measure.h b/srsue/hdr/phy/scell/measure.h deleted file mode 100644 index 287306d08..000000000 --- a/srsue/hdr/phy/scell/measure.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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/. - * - */ - -#ifndef SRSUE_MEASURE_RECV_H -#define SRSUE_MEASURE_RECV_H - -#include - -#include "srsue/hdr/phy/phy_common.h" - -namespace srsue { -namespace scell { -// Class to perform cell measurements -class measure -{ - - // TODO: This class could early stop once the variance between the last N measurements is below 3GPP requirements - -public: - typedef enum { IDLE, MEASURE_OK, ERROR } ret_code; - - ~measure(); - void init(cf_t* buffer[SRSLTE_MAX_PORTS], - srslte::log* log_h, - uint32_t nof_rx_antennas, - phy_common* worker_com, - uint32_t nof_subframes = RSRP_MEASURE_NOF_FRAMES); - void reset(); - void set_cell(srslte_cell_t cell); - ret_code run_subframe(uint32_t sf_idx); - ret_code run_multiple_subframes(cf_t* buffer, uint32_t offset, uint32_t sf_idx, uint32_t nof_sf); - float rssi(); - float rsrp(); - float rsrq(); - float snr(); - float cfo(); - uint32_t frame_st_idx(); - void set_rx_gain_offset(float rx_gain_offset); - -private: - srslte::log* log_h; - srslte_ue_dl_t ue_dl; - srslte_ue_dl_cfg_t ue_dl_cfg; - cf_t* buffer[SRSLTE_MAX_PORTS]; - uint32_t cnt; - uint32_t nof_subframes; - uint32_t current_prb; - float rx_gain_offset; - float mean_rsrp, mean_rsrq, mean_snr, mean_rssi, mean_cfo; - uint32_t final_offset; - const static int RSRP_MEASURE_NOF_FRAMES = 5; -}; - -} // namespace scell -} // namespace srsue - -#endif // SRSUE_MEASURE_RECV_H diff --git a/srsue/hdr/phy/scell/scell_recv.h b/srsue/hdr/phy/scell/scell_recv.h index bc244c49a..cfc7ae585 100644 --- a/srsue/hdr/phy/scell/scell_recv.h +++ b/srsue/hdr/phy/scell/scell_recv.h @@ -22,10 +22,10 @@ #ifndef SRSUE_SCELL_RECV_H #define SRSUE_SCELL_RECV_H +#include "srsue/hdr/phy/phy_common.h" +#include #include -#include "measure.h" - namespace srsue { namespace scell { @@ -33,21 +33,10 @@ namespace scell { class scell_recv { public: - const static int MAX_CELLS = 8; - typedef struct { - uint32_t pci; - float rsrp; - float rsrq; - uint32_t offset; - } cell_info_t; - void init(srslte::log* log_h, bool sic_pss_enabled, uint32_t max_sf_window, phy_common* worker_com); + void init(srslte::log* log_h, uint32_t max_sf_window); void deinit(); void reset(); - int find_cells(cf_t* input_buffer, - float rx_gain_offset, - srslte_cell_t current_cell, - uint32_t nof_sf, - cell_info_t found_cells[MAX_CELLS]); + std::set find_cells(const cf_t* input_buffer, const srslte_cell_t serving_cell, const uint32_t nof_sf); private: // 36.133 9.1.2.1 for band 7 @@ -57,9 +46,7 @@ private: srslte::log* log_h; srslte_sync_t sync_find; - bool sic_pss_enabled; uint32_t current_fft_sz; - measure measure_p; }; } // namespace scell diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index e9370e29e..ad90fb526 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -70,9 +70,9 @@ public: bool cell_is_camping(); // RRC interface for controlling the neighbour cell measurement - void meas_reset(); - int meas_start(uint32_t earfcn, int pci); - int meas_stop(uint32_t earfcn, int pci); + void set_cells_to_meas(uint32_t earfcn, std::set& pci); + void set_inter_frequency_measurement(uint32_t cc_idx, uint32_t earfcn_, srslte_cell_t cell_); + void meas_stop(); // from chest_feedback_itf void in_sync() final; @@ -172,7 +172,7 @@ private: // Objects for internal use search search_p; sfn_sync sfn_p; - scell::intra_measure intra_freq_meas; + std::vector > intra_freq_meas; uint32_t current_sflen = 0; int next_offset = 0; // Sample offset triggered by Time aligment commands diff --git a/srsue/hdr/stack/mac/dl_harq.h b/srsue/hdr/stack/mac/dl_harq.h index d141e6907..df770c531 100644 --- a/srsue/hdr/stack/mac/dl_harq.h +++ b/srsue/hdr/stack/mac/dl_harq.h @@ -41,7 +41,6 @@ public: bool init(srslte::log* log_h, mac_interface_rrc::ue_rnti_t* rntis, - srslte::timer_handler::unique_timer* timer_aligment_timer, demux* demux_unit); void reset(); void start_pcap(srslte::mac_pcap* pcap_); @@ -121,7 +120,6 @@ private: std::vector proc; dl_harq_process bcch_proc; - srslte::timer_handler::unique_timer* timer_aligment_timer = nullptr; demux* demux_unit; srslte::log* log_h; srslte::mac_pcap* pcap; diff --git a/srsue/hdr/stack/rrc/rrc.h b/srsue/hdr/stack/rrc/rrc.h index d1f2514fd..0409d9ce9 100644 --- a/srsue/hdr/stack/rrc/rrc.h +++ b/srsue/hdr/stack/rrc/rrc.h @@ -68,9 +68,9 @@ namespace srsue { class cell_t { public: - bool is_valid() { return phy_cell.earfcn != 0 && srslte_cell_isvalid(&phy_cell.cell); } - bool equals(cell_t* x) { return equals(x->phy_cell.earfcn, x->phy_cell.cell.id); } - bool equals(uint32_t earfcn, uint32_t pci) { return earfcn == phy_cell.earfcn && pci == phy_cell.cell.id; } + bool is_valid() { return phy_cell.earfcn != 0 && srslte_cellid_isvalid(phy_cell.pci); } + bool equals(cell_t* x) { return equals(x->phy_cell.earfcn, x->phy_cell.pci); } + bool equals(uint32_t earfcn, uint32_t pci) { return earfcn == phy_cell.earfcn && pci == phy_cell.pci; } // NaN means an RSRP value has not yet been obtained. Keep then in the list and clean them if never updated bool greater(cell_t* x) { return rsrp > x->rsrp || std::isnan(rsrp); } bool plmn_equals(asn1::rrc::plmn_id_s plmn_id) @@ -113,29 +113,27 @@ public: } } - cell_t() - { - phy_interface_rrc_lte::phy_cell_t tmp = {}; - cell_t(tmp, 0); - } - cell_t(phy_interface_rrc_lte::phy_cell_t phy_cell, float rsrp_) + cell_t() { cell_t({0, 0}); } + + cell_t(phy_interface_rrc_lte::phy_cell_t phy_cell_) { gettimeofday(&last_update, nullptr); - this->has_valid_sib1 = false; - this->has_valid_sib2 = false; - this->has_valid_sib3 = false; - this->has_valid_sib13 = false; - this->phy_cell = phy_cell; - rsrp = rsrp_; - bzero(&sib1, sizeof(sib1)); - bzero(&sib2, sizeof(sib2)); - bzero(&sib3, sizeof(sib3)); - bzero(&sib13, sizeof(sib13)); + has_valid_sib1 = false; + has_valid_sib2 = false; + has_valid_sib3 = false; + has_valid_sib13 = false; + phy_cell = phy_cell_; + rsrp = NAN; + rsrq = NAN; + sib1 = {}; + sib2 = {}; + sib3 = {}; + sib13 = {}; } uint32_t get_earfcn() { return phy_cell.earfcn; } - uint32_t get_pci() { return phy_cell.cell.id; } + uint32_t get_pci() { return phy_cell.pci; } void set_rsrp(float rsrp_) { @@ -144,8 +142,15 @@ public: } gettimeofday(&last_update, nullptr); } + void set_rsrq(float rsrq_) + { + if (!std::isnan(rsrq_)) { + rsrq = rsrq_; + } + } float get_rsrp() { return rsrp; } + float get_rsrq() { return rsrq; } void set_sib1(asn1::rrc::sib_type1_s* sib1_) { @@ -244,7 +249,13 @@ public: std::string print() { char buf[256]; - snprintf(buf, 256, "{cell_id: 0x%x, pci: %d, dl_earfcn: %d}\n", get_cell_id(), get_pci(), get_earfcn()); + snprintf(buf, + 256, + "{cell_id: 0x%x, pci: %d, dl_earfcn: %d, rsrp=%+.1f}", + get_cell_id(), + get_pci(), + get_earfcn(), + get_rsrp()); return std::string{buf}; } @@ -258,6 +269,7 @@ public: private: float rsrp = NAN; + float rsrq = NAN; struct timeval last_update = {}; @@ -322,7 +334,7 @@ public: // PHY interface void in_sync() final; void out_of_sync() final; - void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn, int pci) final; + void new_cell_meas(std::vector& meas); // MAC interface void ho_ra_completed(bool ra_successful); @@ -345,9 +357,14 @@ public: void cell_search_completed(const phy_interface_rrc_lte::cell_search_ret_t& cs_ret, const phy_interface_rrc_lte::phy_cell_t& found_cell); +protected: + // Moved to protected to be accessible by unit tests + void set_serving_cell(phy_interface_rrc_lte::phy_cell_t phy_cell, bool discard_serving); + bool has_neighbour_cell(const uint32_t earfcn, const uint32_t pci); + private: typedef struct { - enum { PDU, PCCH, PDU_MCH, RLF, PDU_BCCH_DLSCH, STOP } command; + enum { PDU, PCCH, PDU_MCH, RLF, PDU_BCCH_DLSCH, HO_COMPLETE, STOP } command; srslte::unique_byte_buffer_t pdu; uint16_t lcid; } cmd_msg_t; @@ -448,19 +465,18 @@ private: std::vector neighbour_cells; unique_cell_t serving_cell = nullptr; void set_serving_cell(uint32_t cell_idx); - void set_serving_cell(phy_interface_rrc_lte::phy_cell_t phy_cell); unique_cell_t remove_neighbour_cell(const uint32_t earfcn, const uint32_t pci); cell_t* get_neighbour_cell_handle(const uint32_t earfcn, const uint32_t pci); - bool has_neighbour_cell(const uint32_t earfcn, const uint32_t pci); int find_neighbour_cell(uint32_t earfcn, uint32_t pci); - bool add_neighbour_cell(uint32_t earfcn, uint32_t pci, float rsrp); - bool add_neighbour_cell(phy_interface_rrc_lte::phy_cell_t phy_cell, float rsrp); + bool add_neighbour_cell(phy_meas_t meas); bool add_neighbour_cell(unique_cell_t new_cell); + void log_neighbour_cells(); void sort_neighbour_cells(); void clean_neighbours(); void delete_last_neighbour(); std::string print_neighbour_cells(); + std::set get_neighbour_pcis(uint32_t earfcn); bool initiated = false; asn1::rrc::reest_cause_e m_reest_cause = asn1::rrc::reest_cause_e::nulltype; @@ -469,117 +485,23 @@ private: bool reestablishment_started = false; bool reestablishment_successful = false; - // Measurements sub-class - class rrc_meas - { - public: - void init(rrc* parent); - void reset(); - bool parse_meas_config(asn1::rrc::meas_cfg_s* meas_config); - void new_phy_meas(uint32_t earfcn, uint32_t pci, float rsrp, float rsrq, uint32_t tti); - void run_tti(uint32_t tti); - bool timer_expired(uint32_t timer_id); - void ho_finish(); - void delete_report(uint32_t earfcn, uint32_t pci); - - private: - const static int NOF_MEASUREMENTS = 3; - - typedef enum { RSRP = 0, RSRQ = 1, BOTH = 2 } quantity_t; - - typedef struct { - uint32_t pci; - float q_offset; - } meas_cell_t; - - typedef struct { - uint32_t earfcn; - float q_offset; - std::map meas_cells; - std::map found_cells; - } meas_obj_t; - - typedef struct { - uint32_t interval; - uint32_t max_cell; - int32_t amount; - quantity_t trigger_quantity; - quantity_t report_quantity; - asn1::rrc::eutra_event_s event; - enum { EVENT, PERIODIC } trigger_type; - } report_cfg_t; - - typedef struct { - float ms[NOF_MEASUREMENTS]; - bool triggered; - bool timer_enter_triggered; - bool timer_exit_triggered; - uint32_t enter_tti; - uint32_t exit_tti; - } meas_value_t; - - typedef struct { - uint32_t nof_reports_sent; - uint32_t report_id; - uint32_t object_id; - bool triggered; - srslte::timer_handler::unique_timer periodic_timer; - std::map cell_values; // Value for each PCI in this object - } meas_t; - - std::map objects; - std::map reports_cfg; - std::map active; - - rrc* parent = nullptr; - srslte::log* log_h = nullptr; - phy_interface_rrc_lte* phy = nullptr; - srslte::timer_handler* timers = nullptr; - - uint32_t filter_k_rsrp, filter_k_rsrq = 0; - float filter_a[NOF_MEASUREMENTS] = {}; - - meas_value_t pcell_measurement = {}; - - bool s_measure_enabled = false; - float s_measure_value = 0.0; - - void stop_reports(meas_t* m); - void stop_reports_object(uint32_t object_id); - void remove_meas_object(uint32_t object_id); - void remove_meas_report(uint32_t report_id); - void remove_meas_id(uint32_t measId); - void remove_meas_id(std::map::iterator it); - void calculate_triggers(uint32_t tti); - void update_phy(); - void L3_filter(meas_value_t* value, float rsrp[NOF_MEASUREMENTS]); - bool find_earfcn_cell(uint32_t earfcn, uint32_t pci, meas_obj_t** object, int* cell_idx); - float range_to_value(quantity_t quant, uint8_t range); - uint8_t value_to_range(quantity_t quant, float value); - bool process_event(asn1::rrc::eutra_event_s* event, - uint32_t tti, - bool enter_condition, - bool exit_condition, - meas_t* m, - meas_value_t* cell); - - void generate_report(uint32_t meas_id); - }; - - rrc_meas measurements; - - // Measurement object from phy - typedef struct { - float rsrp; - float rsrq; - uint32_t tti; - uint32_t earfcn; - uint32_t pci; - } phy_meas_t; - - void process_phy_meas(); - void process_new_phy_meas(phy_meas_t meas); - srslte::block_queue phy_meas_q; + // Process HO completition in the background + void process_ho_ra_completed(bool ra_successful); + + // Measurements private subclass + class rrc_meas; + rrc_meas* measurements; + + // Interface from rrc_meas + void send_srb1_msg(const asn1::rrc::ul_dcch_msg_s& msg); + std::set get_cells(const uint32_t earfcn); + float get_cell_rsrp(const uint32_t earfcn, const uint32_t pci); + float get_cell_rsrq(const uint32_t earfcn, const uint32_t pci); + cell_t* get_serving_cell(); + + void process_cell_meas(); + void process_new_cell_meas(std::vector& meas); + srslte::block_queue > cell_meas_q; // Cell selection/reselection functions/variables typedef struct { diff --git a/srsue/hdr/stack/rrc/rrc_meas.h b/srsue/hdr/stack/rrc/rrc_meas.h new file mode 100644 index 000000000..3bf81de51 --- /dev/null +++ b/srsue/hdr/stack/rrc/rrc_meas.h @@ -0,0 +1,168 @@ +/* + * 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/. + * + */ +#ifndef SRSLTE_RRC_MEAS_H_ +#define SRSLTE_RRC_MEAS_H_ + +#include "srslte/asn1/rrc_asn1.h" +#include "srslte/asn1/rrc_asn1_utils.h" +#include "srslte/common/common.h" +#include "srslte/common/log.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srsue/hdr/stack/rrc/rrc.h" + +#include +#include + +namespace srsue { + +using namespace asn1::rrc; + +typedef std::vector cell_triggered_t; + +// RRC Measurements class +class rrc::rrc_meas +{ +public: + rrc_meas(srslte::log* log_) : meas_cfg(&meas_report_list, log_), meas_report_list(&meas_cfg, log_), log_h(log_) {} + void init(rrc* rrc_ptr); + void reset(); + bool parse_meas_config(const rrc_conn_recfg_r8_ies_s* meas_config, bool is_ho_reest = false, uint32_t src_earfcn = 0); + void run_tti(const uint32_t tti); + void update_phy(); + float rsrp_filter(const float new_value, const float avg_value); + float rsrq_filter(const float new_value, const float avg_value); + +private: + typedef struct { + float rsrp; + float rsrq; + } phy_quant_t; + + class var_meas_cfg; + + class var_meas_report_list + { + public: + var_meas_report_list(var_meas_cfg* meas_cfg_, srslte::log* log_) : meas_cfg(meas_cfg_), log_h(log_) {} + void init(rrc* rrc); + void generate_report(const uint32_t measId); + void remove_all_varmeas_reports(); + void remove_varmeas_report(const uint32_t measId); + bool is_timer_expired(const uint32_t measId); + void 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); + void upd_measId(const uint32_t measId, const cell_triggered_t& cell_triggered_list); + cell_triggered_t get_measId_cells(const uint32_t measId); + + private: + class var_meas_report + { + public: + uint32_t carrier_freq = 0; + uint8_t nof_reports_sent = 0; + cell_triggered_t cell_triggered_list = {}; + report_cfg_eutra_s report_cfg = {}; + srslte::timer_handler::unique_timer periodic_timer = {}; + }; + var_meas_cfg* meas_cfg = nullptr; + srslte::log* log_h = nullptr; + srslte::timer_handler* timers = nullptr; + rrc* rrc_ptr = nullptr; + std::map varMeasReportList; + }; + + // The UE variable VarMeasConfig includes the accumulated configuration of the measurements to be performed by the + // UE, covering intra-frequency, inter-frequency and inter-RAT mobility related measurements. + class var_meas_cfg + { + public: + var_meas_cfg(var_meas_report_list* meas_report_, srslte::log* log_) : meas_report(meas_report_), log_h(log_) {} + void init(rrc* rrc); + void reset(); + phy_quant_t get_filter_a(); + void remove_measId(const uint32_t measId); + std::list get_active_objects(); + void ho_reest_finish(const uint32_t src_earfcn, const uint32_t dst_earfcn); + bool parse_meas_config(const meas_cfg_s* meas_config, bool is_ho_reest, uint32_t src_earfcn); + void eval_triggers(); + void report_triggers(); + + private: + void remove_varmeas_report(const uint32_t meas_id); + + void measObject_removal(const meas_obj_to_rem_list_l& list); + void measObject_addmod(const meas_obj_to_add_mod_list_l& list); + void reportConfig_removal(const report_cfg_to_rem_list_l& list); + void reportConfig_addmod(const report_cfg_to_add_mod_list_l& list); + void measId_removal(const meas_id_to_rem_list_l& list); + void measId_addmod(const meas_id_to_add_mod_list_l& list); + void quantity_config(const quant_cfg_s& cfg); + void log_debug_trigger_value(const eutra_event_s::event_id_c_& e); + + static bool is_rsrp(report_cfg_eutra_s::trigger_quant_opts::options q); + + class cell_trigger_state + { + public: + void event_condition(const bool enter, const bool exit); + bool is_enter_equal(const uint32_t nof_tti); + bool is_exit_equal(const uint32_t nof_tti); + + private: + uint32_t nof_tti_enter = 0; + uint32_t nof_tti_exit = 0; + }; + + // varMeasConfig data + std::map measIdList; // Uses MeasId as key + std::map measObjectsList; // Uses MeasObjectId as key + std::map reportConfigList; // Uses ReportConfigId as key + + phy_quant_t filter_a = {}; + float s_measure_value = 0.0; + + // trigger counters. First key is measId, second key is cell id (pci) + // It is safe to use [] operator in this double-map because all members are uint32_t + std::map > trigger_state; + + var_meas_report_list* meas_report = nullptr; + srslte::log* log_h = nullptr; + rrc* rrc_ptr = nullptr; + }; + + std::mutex meas_cfg_mutex; + var_meas_cfg meas_cfg; + var_meas_report_list meas_report_list; + srslte::log* log_h = nullptr; + 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 offset_val(const meas_obj_eutra_s& meas_obj); + static asn1::dyn_array::iterator find_pci_in_meas_obj(meas_obj_eutra_s& meas_obj, uint32_t pci); +}; + +} // namespace srsue + +#endif // SRSLTE_SRSUE_HDR_STACK_RRC_RRC_MEAS_H_ diff --git a/srsue/hdr/stack/rrc/rrc_procedures.h b/srsue/hdr/stack/rrc/rrc_procedures.h index 863d7f6b0..b6cdeb7cf 100644 --- a/srsue/hdr/stack/rrc/rrc_procedures.h +++ b/srsue/hdr/stack/rrc/rrc_procedures.h @@ -131,6 +131,7 @@ private: uint32_t neigh_index; srslte::proc_future_t cell_search_fut; srslte::proc_future_t serv_cell_cfg_fut; + bool discard_serving = false; }; class rrc::plmn_search_proc @@ -211,6 +212,7 @@ public: static const char* name() { return "Go Idle"; } private: + bool wait = false; rrc* rrc_ptr; static const uint32_t rlc_flush_timeout = 2000; @@ -238,6 +240,7 @@ public: srslte::proc_outcome_t init(asn1::rrc::reest_cause_e cause); srslte::proc_outcome_t step(); static const char* name() { return "Connection re-establishment"; } + uint32_t get_source_earfcn() const { return reest_source_freq; } private: enum class state_t { cell_reselection, cell_configuration } state; @@ -246,6 +249,7 @@ private: asn1::rrc::reest_cause_e reest_cause = asn1::rrc::reest_cause_e::nulltype; uint16_t reest_rnti = 0; uint16_t reest_source_pci = 0; + uint32_t reest_source_freq = 0; srslte::proc_outcome_t step_cell_reselection(); srslte::proc_outcome_t step_cell_configuration(); diff --git a/srsue/hdr/stack/ue_stack_lte.h b/srsue/hdr/stack/ue_stack_lte.h index 7bbf6ad05..273c4f4a8 100644 --- a/srsue/hdr/stack/ue_stack_lte.h +++ b/srsue/hdr/stack/ue_stack_lte.h @@ -77,10 +77,7 @@ public: // RRC interface for PHY void in_sync() final; void out_of_sync() final; - void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn = -1, int pci = -1) - { - rrc.new_phy_meas(rsrp, rsrq, tti, earfcn, pci); - }; + void new_cell_meas(std::vector& meas) { rrc.new_cell_meas(meas); } // MAC Interface for PHY uint16_t get_dl_sched_rnti(uint32_t tti) { return mac.get_dl_sched_rnti(tti); } diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 6cee809ab..2712856fb 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -302,10 +302,6 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) bpo::value(&args->phy.cfo_loop_pss_conv)->default_value(DEFAULT_PSS_STABLE_TIMEOUT), "After the PSS estimation is below cfo_loop_pss_tol for cfo_loop_pss_timeout times consecutively, RS adjustments are allowed.") - ("phy.sic_pss_enabled", - bpo::value(&args->phy.sic_pss_enabled)->default_value(false), - "Applies Successive Interference Cancellation to PSS signals when searching for neighbour cells. Must be disabled if cells have identical channel and timing.") - ("phy.interpolate_subframe_enabled", bpo::value(&args->phy.interpolate_subframe_enabled)->default_value(false), "Interpolates in the time domain the channel estimates within 1 subframe.") diff --git a/srsue/src/phy/cc_worker.cc b/srsue/src/phy/cc_worker.cc index 89318d936..1c2c4270d 100644 --- a/srsue/src/phy/cc_worker.cc +++ b/srsue/src/phy/cc_worker.cc @@ -541,10 +541,11 @@ void cc_worker::update_measurements() // Average RSRQ over DEFAULT_MEAS_PERIOD_MS then sent to RRC float rsrq_db = ue_dl.chest_res.rsrq_db; if (std::isnormal(rsrq_db)) { - if (!(CURRENT_TTI % phy->pcell_report_period) || !std::isnormal(phy->avg_rsrq_db)) { - phy->avg_rsrq_db = rsrq_db; + if (!(CURRENT_TTI % phy->pcell_report_period) || !std::isnormal(phy->avg_rsrq_db[cc_idx])) { + phy->avg_rsrq_db[cc_idx] = rsrq_db; } else { - phy->avg_rsrq_db = SRSLTE_VEC_CMA(rsrq_db, phy->avg_rsrq_db, CURRENT_TTI % phy->pcell_report_period); + phy->avg_rsrq_db[cc_idx] = + SRSLTE_VEC_CMA(rsrq_db, phy->avg_rsrq_db[cc_idx], CURRENT_TTI % phy->pcell_report_period); } } @@ -597,8 +598,8 @@ void cc_worker::update_measurements() // Store metrics dl_metrics.n = phy->avg_noise[cc_idx]; dl_metrics.rsrp = phy->avg_rsrp_dbm[cc_idx]; - dl_metrics.rsrq = phy->avg_rsrq_db; - dl_metrics.rssi = phy->avg_rssi_dbm; + dl_metrics.rsrq = phy->avg_rsrq_db[cc_idx]; + dl_metrics.rssi = phy->avg_rssi_dbm[cc_idx]; dl_metrics.pathloss = phy->pathloss[cc_idx]; dl_metrics.sinr = phy->avg_snr_db_cqi[cc_idx]; dl_metrics.sync_err = ue_dl.chest_res.sync_error; diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index c305ef1e0..1a4e5ac11 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -253,7 +253,7 @@ void phy::get_metrics(phy_metrics_t* m) { uint32_t dl_earfcn = 0; srslte_cell_t cell = {}; - get_current_cell(&cell, &dl_earfcn); + sfsync.get_current_cell(&cell, &dl_earfcn); m->info[0].pci = cell.id; m->info[0].dl_earfcn = dl_earfcn; @@ -315,19 +315,14 @@ void phy::configure_prach_params() } } -void phy::meas_reset() +void phy::set_cells_to_meas(uint32_t earfcn, std::set& pci) { - sfsync.meas_reset(); + sfsync.set_cells_to_meas(earfcn, pci); } -int phy::meas_start(uint32_t earfcn, int pci) +void phy::meas_stop() { - return sfsync.meas_start(earfcn, pci); -} - -int phy::meas_stop(uint32_t earfcn, int pci) -{ - return sfsync.meas_stop(earfcn, pci); + sfsync.meas_stop(); } bool phy::cell_select(phy_cell_t* cell) @@ -356,25 +351,6 @@ float phy::get_pathloss_db() return common.cur_pathloss; } -void phy::get_current_cell(srslte_cell_t* cell, uint32_t* current_earfcn) -{ - sfsync.get_current_cell(cell, current_earfcn); -} - -uint32_t phy::get_current_pci() -{ - srslte_cell_t cell; - sfsync.get_current_cell(&cell); - return cell.id; -} - -uint32_t phy::get_current_earfcn() -{ - uint32_t earfcn; - sfsync.get_current_cell(nullptr, &earfcn); - return earfcn; -} - void phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm) { n_ta = 0; @@ -479,6 +455,11 @@ void phy::set_config(srslte::phy_cfg_t& config_, uint32_t cc_idx, uint32_t earfc workers[i]->set_config(cc_idx, config_); } + // Set inter-frequency measurement primary cell + if (cell_info) { + sfsync.set_inter_frequency_measurement(cc_idx, earfcn, *cell_info); + } + if (cc_idx == 0) { prach_cfg = config_.prach_cfg; } else if (cell_info) { diff --git a/srsue/src/phy/phy_common.cc b/srsue/src/phy/phy_common.cc index 7f2e47707..e37aa4840 100644 --- a/srsue/src/phy/phy_common.cc +++ b/srsue/src/phy/phy_common.cc @@ -667,15 +667,14 @@ void phy_common::reset() cur_pusch_power = 0; sr_last_tx_tti = -1; cur_pusch_power = 0; + pcell_report_period = 20; + ZERO_OBJECT(pathloss); ZERO_OBJECT(avg_snr_db_cqi); ZERO_OBJECT(avg_rsrp); ZERO_OBJECT(avg_rsrp_dbm); + ZERO_OBJECT(avg_rsrq_db); ZERO_OBJECT(scell_cfg); - avg_rsrq_db = 0; - - pcell_report_period = 20; - ZERO_OBJECT(pending_dl_ack); ZERO_OBJECT(pending_dl_dai); ZERO_OBJECT(pending_ul_ack); diff --git a/srsue/src/phy/scell/async_scell_recv.cc b/srsue/src/phy/scell/async_scell_recv.cc index 886d5ed89..8d9f29c14 100644 --- a/srsue/src/phy/scell/async_scell_recv.cc +++ b/srsue/src/phy/scell/async_scell_recv.cc @@ -259,8 +259,6 @@ void async_scell_recv::set_ue_sync_opts(srslte_ue_sync_t* q, float cfo) worker_com->args->cfo_loop_pss_tol, worker_com->args->cfo_loop_pss_conv); - q->strack.pss.chest_on_filter = worker_com->args->sic_pss_enabled; - // Disable CP based CFO estimation during find if (cfo != 0) { q->cfo_current_value = cfo / 15000; diff --git a/srsue/src/phy/scell/intra_measure.cc b/srsue/src/phy/scell/intra_measure.cc index 407bc27f2..8d8e11590 100644 --- a/srsue/src/phy/scell/intra_measure.cc +++ b/srsue/src/phy/scell/intra_measure.cc @@ -45,18 +45,18 @@ intra_measure::~intra_measure() free(search_buffer); } -void intra_measure::init(phy_common* common, rrc_interface_phy_lte* rrc, srslte::log* log_h) +void intra_measure::init(phy_common* common_, rrc_interface_phy_lte* rrc_, srslte::log* log_h_) { - this->rrc = rrc; - this->log_h = log_h; - this->common = common; + this->rrc = rrc_; + this->log_h = log_h_; + this->common = common_; receive_enabled = false; // Initialise Reference signal measurement srslte_refsignal_dl_sync_init(&refsignal_dl_sync); // Start scell - scell.init(log_h, common->args->sic_pss_enabled, common->args->intra_freq_meas_len_ms, common); + scell.init(log_h, common->args->intra_freq_meas_len_ms); search_buffer = (cf_t*)srslte_vec_malloc(common->args->intra_freq_meas_len_ms * SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB) * sizeof(cf_t)); @@ -83,52 +83,27 @@ void intra_measure::set_primary_cell(uint32_t earfcn, srslte_cell_t cell) { this->current_earfcn = earfcn; current_sflen = (uint32_t)SRSLTE_SF_LEN_PRB(cell.nof_prb); - this->primary_cell = cell; + this->serving_cell = cell; } -void intra_measure::clear_cells() +void intra_measure::meas_stop() { - active_pci.clear(); receive_enabled = false; receiving = false; receive_cnt = 0; srslte_ringbuffer_reset(&ring_buffer); -} - -void intra_measure::add_cell(int pci) -{ - if (std::find(active_pci.begin(), active_pci.end(), pci) == active_pci.end()) { - active_pci.push_back(pci); - receive_enabled = true; - Info("INTRA: Starting intra-frequency measurement for pci=%d\n", pci); - } else { - Debug("INTRA: Requested to start already existing intra-frequency measurement for PCI=%d\n", pci); - } -} - -int intra_measure::get_offset(uint32_t pci) -{ - for (auto& i : info) { - if (i.pci == pci) { - return i.offset; - } + if (log_h) { + log_h->info("INTRA: Disabled neighbour cell search for EARFCN %d\n", get_earfcn()); } - return -1; } -void intra_measure::rem_cell(int pci) +void intra_measure::set_cells_to_meas(std::set& pci) { - auto newEnd = std::remove(active_pci.begin(), active_pci.end(), pci); - - if (newEnd != active_pci.end()) { - active_pci.erase(newEnd, active_pci.end()); - if (active_pci.empty()) { - receive_enabled = false; - } - Info("INTRA: Stopping intra-frequency measurement for pci=%d. Number of cells: %zu\n", pci, active_pci.size()); - } else { - Warning("INTRA: Requested to stop non-existing intra-frequency measurement for PCI=%d\n", pci); - } + active_pci_mutex.lock(); + active_pci = pci; + active_pci_mutex.unlock(); + receive_enabled = true; + log_h->info("INTRA: Received list of %lu neighbour cells to measure in EARFCN %d.\n", pci.size(), current_earfcn); } void intra_measure::write(uint32_t tti, cf_t* data, uint32_t nsamples) @@ -137,7 +112,6 @@ void intra_measure::write(uint32_t tti, cf_t* data, uint32_t nsamples) if ((tti % common->args->intra_freq_meas_period_ms) == 0) { receiving = true; receive_cnt = 0; - measure_tti = tti; srslte_ringbuffer_reset(&ring_buffer); } if (receiving) { @@ -157,6 +131,8 @@ void intra_measure::write(uint32_t tti, cf_t* data, uint32_t nsamples) void intra_measure::run_thread() { + std::set cells_to_measure = {}; + while (running) { if (running) { tti_sync.wait(); @@ -164,77 +140,62 @@ void intra_measure::run_thread() if (running) { + active_pci_mutex.lock(); + cells_to_measure = active_pci; + active_pci_mutex.unlock(); + // Read data from buffer and find cells in it srslte_ringbuffer_read( &ring_buffer, search_buffer, common->args->intra_freq_meas_len_ms * current_sflen * sizeof(cf_t)); - int found_cells = scell.find_cells( - search_buffer, common->rx_gain_offset, primary_cell, common->args->intra_freq_meas_len_ms, info); + + // Detect new cells using PSS/SSS + std::set detected_cells = + scell.find_cells(search_buffer, serving_cell, common->args->intra_freq_meas_len_ms); + + // Add detected cells to the list of cells to measure + for (auto& c : detected_cells) { + cells_to_measure.insert(c); + } + receiving = false; - // Look for other cells not found automatically - // Using Cell Reference signal synchronization for all known active PCI - for (auto q : active_pci) { - srslte_cell_t cell = primary_cell; - cell.id = q; + std::vector neigbhour_cells = {}; + + // Use Cell Reference signal to measure cells in the time domain for all known active PCI + for (auto id : cells_to_measure) { + // Do not measure serving cell here since it's measured by workers + if (id == serving_cell.id) { + continue; + } + srslte_cell_t cell = serving_cell; + cell.id = id; srslte_refsignal_dl_sync_set_cell(&refsignal_dl_sync, cell); srslte_refsignal_dl_sync_run( &refsignal_dl_sync, search_buffer, common->args->intra_freq_meas_len_ms * current_sflen); if (refsignal_dl_sync.found) { - Info("INTRA: Found neighbour cell: PCI=%03d, RSRP=%5.1f dBm, RSRQ=%5.1f, peak_idx=%5d, CFO=%+.1fHz\n", - cell.id, - refsignal_dl_sync.rsrp_dBfs, - refsignal_dl_sync.rsrq_dB, + rrc_interface_phy_lte::phy_meas_t m = {}; + m.pci = cell.id; + m.earfcn = current_earfcn; + m.rsrp = refsignal_dl_sync.rsrp_dBfs - common->rx_gain_offset; + m.rsrq = refsignal_dl_sync.rsrq_dB; + neigbhour_cells.push_back(m); + + Info("INTRA: Found neighbour cell: EARFCN=%d, PCI=%03d, RSRP=%5.1f dBm, RSRQ=%5.1f, peak_idx=%5d, " + "CFO=%+.1fHz\n", + m.earfcn, + m.pci, + m.rsrp, + m.rsrq, refsignal_dl_sync.peak_index, refsignal_dl_sync.cfo_Hz); - - bool found = false; - float weakest_rsrp_value = +INFINITY; - uint32_t weakest_rsrp_index = 0; - - // Try to find PCI in info list - for (int i = 0; i < found_cells && !found; i++) { - // Finds cell, update - if (info[i].pci == cell.id) { - info[i].rsrp = refsignal_dl_sync.rsrp_dBfs; - info[i].rsrq = refsignal_dl_sync.rsrq_dB; - info[i].offset = refsignal_dl_sync.peak_index; - found = true; - } else if (weakest_rsrp_value > info[i].rsrp) { - // Update weakest - weakest_rsrp_value = info[i].rsrp; - weakest_rsrp_index = i; - } - } - - if (!found) { - // If number of cells exceeds - if (found_cells >= scell_recv::MAX_CELLS) { - // overwrite weakest cell if stronger - if (refsignal_dl_sync.rsrp_dBfs > weakest_rsrp_value) { - info[weakest_rsrp_index].pci = cell.id; - info[weakest_rsrp_index].rsrp = refsignal_dl_sync.rsrp_dBfs; - info[weakest_rsrp_index].rsrq = refsignal_dl_sync.rsrq_dB; - info[weakest_rsrp_index].offset = refsignal_dl_sync.peak_index; - } else { - // Ignore measurement - } - } else { - // Otherwise append cell - info[found_cells].pci = cell.id; - info[found_cells].rsrp = refsignal_dl_sync.rsrp_dBfs; - info[found_cells].rsrq = refsignal_dl_sync.rsrq_dB; - info[found_cells].offset = refsignal_dl_sync.peak_index; - found_cells++; - } - } } } // Send measurements to RRC - for (int i = 0; i < found_cells; i++) { - rrc->new_phy_meas(info[i].rsrp, info[i].rsrq, measure_tti, current_earfcn, info[i].pci); + if (neigbhour_cells.size() > 0) { + rrc->new_cell_meas(neigbhour_cells); } } } diff --git a/srsue/src/phy/scell/measure.cc b/srsue/src/phy/scell/measure.cc deleted file mode 100644 index 7360f3453..000000000 --- a/srsue/src/phy/scell/measure.cc +++ /dev/null @@ -1,233 +0,0 @@ -/* - * 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/phy/scell/measure.h" - -#define Error(fmt, ...) \ - if (SRSLTE_DEBUG_ENABLED) \ - log_h->error(fmt, ##__VA_ARGS__) -#define Warning(fmt, ...) \ - if (SRSLTE_DEBUG_ENABLED) \ - log_h->warning(fmt, ##__VA_ARGS__) -#define Info(fmt, ...) \ - if (SRSLTE_DEBUG_ENABLED) \ - log_h->info(fmt, ##__VA_ARGS__) -#define Debug(fmt, ...) \ - if (SRSLTE_DEBUG_ENABLED) \ - log_h->debug(fmt, ##__VA_ARGS__) - -namespace srsue { -namespace scell { -void measure::init(cf_t* buffer[SRSLTE_MAX_PORTS], - srslte::log* log_h, - uint32_t nof_rx_antennas, - phy_common* worker_com, - uint32_t nof_subframes) - -{ - this->log_h = log_h; - this->nof_subframes = nof_subframes; - for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { - this->buffer[i] = buffer[i]; - } - - if (srslte_ue_dl_init(&ue_dl, this->buffer, SRSLTE_MAX_PRB, nof_rx_antennas)) { - Error("SYNC: Initiating ue_dl_measure\n"); - return; - } - worker_com->set_ue_dl_cfg(&ue_dl_cfg); - ue_dl_cfg.chest_cfg.rsrp_neighbour = true; - reset(); -} - -measure::~measure() -{ - srslte_ue_dl_free(&ue_dl); -} - -void measure::reset() -{ - cnt = 0; - mean_rsrp = 0; - mean_rsrq = 0; - mean_snr = 0; - mean_rssi = 0; - mean_cfo = 0; -} - -void measure::set_cell(srslte_cell_t cell) -{ - current_prb = cell.nof_prb; - if (srslte_ue_dl_set_cell(&ue_dl, cell)) { - Error("SYNC: Setting cell: initiating ue_dl_measure\n"); - } - reset(); -} - -float measure::rssi() -{ - return srslte_convert_power_to_dB(mean_rssi); -} - -float measure::rsrp() -{ - return srslte_convert_power_to_dBm(mean_rsrp) - rx_gain_offset; -} - -float measure::rsrq() -{ - return srslte_convert_power_to_dB(mean_rsrq); -} - -float measure::snr() -{ - return mean_snr; -} - -float measure::cfo() -{ - return mean_cfo; -} - -uint32_t measure::frame_st_idx() -{ - return final_offset; -} - -void measure::set_rx_gain_offset(float rx_gain_offset_) -{ - rx_gain_offset = rx_gain_offset_; -} - -measure::ret_code measure::run_multiple_subframes(cf_t* input_buffer, uint32_t offset, uint32_t sf_idx, uint32_t max_sf) -{ - uint32_t sf_len = (uint32_t)SRSLTE_SF_LEN_PRB(current_prb); - - ret_code ret = IDLE; - - int sf_start = offset - sf_len / 2; - while (sf_start < 0 && sf_idx < max_sf) { - Info("INTRA: sf_start=%d, sf_idx=%d\n", sf_start, sf_idx); - sf_start += sf_len; - sf_idx++; - } - -#ifdef FINE_TUNE_OFFSET_WITH_RS - float max_rsrp = -200; - int best_test_sf_start = 0; - int test_sf_start = 0; - bool found_best = false; - - // Fine-tune sf_start using RS - for (uint32_t n = 0; n < 5; n++) { - - test_sf_start = sf_start - 2 + n; - if (test_sf_start >= 0) { - - cf_t* buf_m[SRSLTE_MAX_PORTS]; - buf_m[0] = &input_buffer[test_sf_start]; - - uint32_t cfi; - if (srslte_ue_dl_decode_fft_estimate_noguru(&ue_dl, buf_m, sf_idx, &cfi)) { - Error("MEAS: Measuring RSRP: Estimating channel\n"); - return ERROR; - } - - float rsrp = srslte_chest_dl_get_rsrp(&ue_dl.chest); - if (rsrp > max_rsrp) { - max_rsrp = rsrp; - best_test_sf_start = test_sf_start; - found_best = true; - } - } - } - - Debug("INTRA: fine-tuning sf_start: %d, found_best=%d, rem_sf=%d\n", sf_start, found_best, nof_sf); - - sf_start = found_best ? best_test_sf_start : sf_start; -#endif - - if (sf_start >= 0 && sf_start < (int)(sf_len * max_sf)) { - - uint32_t nof_sf = (sf_len * max_sf - sf_start) / sf_len; - - final_offset = (uint32_t)sf_start; - - for (uint32_t i = 0; i < nof_sf; i++) { - memcpy(buffer[0], &input_buffer[sf_start + i * sf_len], sizeof(cf_t) * sf_len); - ret = run_subframe((sf_idx + i) % 10); - if (ret != IDLE) { - return ret; - } - } - if (ret != ERROR) { - return MEASURE_OK; - } - } else { - Error("INTRA: not running because sf_start=%d, offset=%d, sf_len*max_sf=%d*%d\n", sf_start, offset, sf_len, max_sf); - ret = ERROR; - } - return ret; -} - -measure::ret_code measure::run_subframe(uint32_t sf_idx) -{ - srslte_dl_sf_cfg_t sf_cfg; - ZERO_OBJECT(sf_cfg); - sf_cfg.tti = sf_idx; - - if (srslte_ue_dl_decode_fft_estimate(&ue_dl, &sf_cfg, &ue_dl_cfg)) { - log_h->error("SYNC: Measuring RSRP: Estimating channel\n"); - return ERROR; - } - - float rsrp = ue_dl.chest_res.rsrp_neigh; - float rsrq = ue_dl.chest_res.rsrq; - float snr = ue_dl.chest_res.snr_db; - float cfo = ue_dl.chest_res.cfo; - float rssi = srslte_vec_avg_power_cf(buffer[0], (uint32_t)SRSLTE_SF_LEN_PRB(current_prb)); - - if (cnt == 0) { - mean_rsrp = rsrp; - mean_rsrq = rsrq; - mean_snr = snr; - mean_rssi = rssi; - mean_cfo = cfo; - } else { - mean_rsrp = SRSLTE_VEC_CMA(rsrp, mean_rsrp, cnt); - mean_rsrq = SRSLTE_VEC_CMA(rsrq, mean_rsrq, cnt); - mean_snr = SRSLTE_VEC_CMA(snr, mean_snr, cnt); - mean_rssi = SRSLTE_VEC_CMA(rssi, mean_rssi, cnt); - mean_cfo = SRSLTE_VEC_CMA(cfo, mean_cfo, cnt); - } - cnt++; - - log_h->debug( - "SYNC: Measuring RSRP %d/%d, sf_idx=%d, RSRP=%.1f dBm, SNR=%.1f dB\n", cnt, nof_subframes, sf_idx, rsrp, snr); - - if (cnt >= nof_subframes) { - return MEASURE_OK; - } else { - return IDLE; - } -} -} // namespace scell -} // namespace srsue diff --git a/srsue/src/phy/scell/scell_recv.cc b/srsue/src/phy/scell/scell_recv.cc index bcedc6a4d..5dc17626b 100644 --- a/srsue/src/phy/scell/scell_recv.cc +++ b/srsue/src/phy/scell/scell_recv.cc @@ -21,19 +21,6 @@ #include "srsue/hdr/phy/scell/scell_recv.h" -#define Error(fmt, ...) \ - if (SRSLTE_DEBUG_ENABLED) \ - log_h->error(fmt, ##__VA_ARGS__) -#define Warning(fmt, ...) \ - if (SRSLTE_DEBUG_ENABLED) \ - log_h->warning(fmt, ##__VA_ARGS__) -#define Info(fmt, ...) \ - if (SRSLTE_DEBUG_ENABLED) \ - log_h->info(fmt, ##__VA_ARGS__) -#define Debug(fmt, ...) \ - if (SRSLTE_DEBUG_ENABLED) \ - log_h->debug(fmt, ##__VA_ARGS__) - namespace srsue { namespace scell { @@ -41,10 +28,9 @@ namespace scell { * Secondary cell receiver */ -void scell_recv::init(srslte::log* _log_h, bool _sic_pss_enabled, uint32_t max_sf_window, phy_common* worker_com) +void scell_recv::init(srslte::log* _log_h, uint32_t max_sf_window) { log_h = _log_h; - sic_pss_enabled = _sic_pss_enabled; // and a separate ue_sync instance @@ -53,14 +39,13 @@ void scell_recv::init(srslte::log* _log_h, bool _sic_pss_enabled, uint32_t max_s sf_buffer[0] = (cf_t*)srslte_vec_malloc(sizeof(cf_t) * max_sf_size); if (!sf_buffer[0]) { - ERROR("Error allocating %d samples for scell\n", max_sf_size); + log_h->error("Error allocating %d samples for scell\n", max_sf_size); return; } - measure_p.init(sf_buffer, log_h, 1, worker_com, max_sf_window); // do this different we don't need all this search window. if (srslte_sync_init(&sync_find, max_sf_window * max_sf_size, 5 * max_sf_size, max_fft_sz)) { - ERROR("Error initiating sync_find\n"); + log_h->error("Error initiating sync_find\n"); return; } srslte_sync_set_sss_algorithm(&sync_find, SSS_FULL); @@ -92,179 +77,86 @@ void scell_recv::deinit() void scell_recv::reset() { current_fft_sz = 0; - measure_p.reset(); } -int scell_recv::find_cells(cf_t* input_buffer, - float rx_gain_offset, - srslte_cell_t cell, - uint32_t nof_sf, - cell_info_t cells[MAX_CELLS]) +std::set +scell_recv::find_cells(const cf_t* input_buffer, const srslte_cell_t serving_cell, const uint32_t nof_sf) { - uint32_t fft_sz = srslte_symbol_sz(cell.nof_prb); + std::set found_cell_ids = {}; + + uint32_t fft_sz = srslte_symbol_sz(serving_cell.nof_prb); uint32_t sf_len = SRSLTE_SF_LEN(fft_sz); if (fft_sz != current_fft_sz) { if (srslte_sync_resize(&sync_find, nof_sf * sf_len, 5 * sf_len, fft_sz)) { - ERROR("Error resizing sync nof_sf=%d, sf_len=%d, fft_sz=%d\n", nof_sf, sf_len, fft_sz); - return SRSLTE_ERROR; + log_h->error("Error resizing sync nof_sf=%d, sf_len=%d, fft_sz=%d\n", nof_sf, sf_len, fft_sz); + return found_cell_ids; } current_fft_sz = fft_sz; } - int nof_cells = 0; uint32_t peak_idx = 0; - uint32_t sf_idx = 0; int cell_id = 0; - srslte_cell_t found_cell; - found_cell = cell; - - measure_p.set_rx_gain_offset(rx_gain_offset); - for (uint32_t n_id_2 = 0; n_id_2 < 3; n_id_2++) { - found_cell.id = 10000; - - if (n_id_2 != (cell.id % 3) || sic_pss_enabled) { + if (n_id_2 != (serving_cell.id % 3)) { srslte_sync_set_N_id_2(&sync_find, n_id_2); srslte_sync_find_ret_t sync_res; - do { - srslte_sync_reset(&sync_find); - srslte_sync_cfo_reset(&sync_find); + srslte_sync_reset(&sync_find); + srslte_sync_cfo_reset(&sync_find); - sync_res = SRSLTE_SYNC_NOFOUND; - bool sss_detected = false; - cell_id = 0; - float max_peak = -1; - uint32_t max_sf5 = 0; - uint32_t max_sf_idx = 0; + sync_res = SRSLTE_SYNC_NOFOUND; + bool sss_detected = false; + cell_id = 0; + float max_peak = -1; - float sss_correlation_peak_max = 0.0f; + float sss_correlation_peak_max = 0.0f; - for (uint32_t sf5_cnt = 0; sf5_cnt < nof_sf / 5; sf5_cnt++) { - sync_res = srslte_sync_find(&sync_find, input_buffer, sf5_cnt * 5 * sf_len, &peak_idx); - Debug("INTRA: n_id_2=%d, cnt=%d/%d, sync_res=%d, sf_idx=%d, peak_idx=%d, peak_value=%f, sss_detected=%d\n", - n_id_2, - sf5_cnt, - nof_sf / 5, - sync_res, - srslte_sync_get_sf_idx(&sync_find), - peak_idx, - sync_find.peak_value, - srslte_sync_sss_detected(&sync_find)); + for (uint32_t sf5_cnt = 0; sf5_cnt < nof_sf / 5; sf5_cnt++) { + sync_res = srslte_sync_find(&sync_find, input_buffer, sf5_cnt * 5 * sf_len, &peak_idx); + if (sync_res == SRSLTE_SYNC_ERROR) { + log_h->error("INTRA: Error calling sync_find()\n"); + return found_cell_ids; + } - if (sync_find.peak_value > max_peak && sync_res == SRSLTE_SYNC_FOUND && - srslte_sync_sss_detected(&sync_find)) { - max_sf5 = sf5_cnt; - max_sf_idx = srslte_sync_get_sf_idx(&sync_find); + if (sync_find.peak_value > max_peak && sync_res == SRSLTE_SYNC_FOUND && srslte_sync_sss_detected(&sync_find)) { - // Uses the cell ID from the highest SSS correlation peak - if (sss_correlation_peak_max < srslte_sync_sss_correlation_peak(&sync_find)) { - // Set the cell ID - cell_id = srslte_sync_get_cell_id(&sync_find); + // Uses the cell ID from the highest SSS correlation peak + if (sss_correlation_peak_max < srslte_sync_sss_correlation_peak(&sync_find)) { + // Set the cell ID + cell_id = srslte_sync_get_cell_id(&sync_find); - // Update the maximum value - sss_correlation_peak_max = srslte_sync_sss_correlation_peak(&sync_find); - } - sss_detected = true; + // Update the maximum value + sss_correlation_peak_max = srslte_sync_sss_correlation_peak(&sync_find); } + sss_detected = true; } - // If the SSS was not detected, the cell id is not reliable. So, consider no sync found - if (!sss_detected) { - sync_res = SRSLTE_SYNC_NOFOUND; - } - - switch (sync_res) { - case SRSLTE_SYNC_ERROR: - return SRSLTE_ERROR; - ERROR("Error finding correlation peak\n"); - return SRSLTE_ERROR; - case SRSLTE_SYNC_FOUND: - - sf_idx = (10 - max_sf_idx - 5 * (max_sf5 % 2)) % 10; - - if (cell_id >= 0) { - // We found the same cell as before, look another N_id_2 - if ((uint32_t)cell_id == found_cell.id || (uint32_t)cell_id == cell.id) { - Debug("INTRA: n_id_2=%d, PCI=%d, found_cell.id=%d, cell.id=%d\n", - n_id_2, - cell_id, - found_cell.id, - cell.id); - sync_res = SRSLTE_SYNC_NOFOUND; - } else { - // We found a new cell ID - found_cell.id = (uint32_t)cell_id; - found_cell.nof_ports = 1; // Use port 0 only for measurement - measure_p.set_cell(found_cell); - - // Correct CFO - /* - srslte_cfo_correct(&sync_find.cfo_corr_frame, - input_buffer, - input_cfo_corrected, - -srslte_sync_get_cfo(&sync_find)/sync_find.fft_size); - */ - - switch (measure_p.run_multiple_subframes(input_buffer, peak_idx, sf_idx, nof_sf)) { - default: - // Consider a cell to be detectable 8.1.2.2.1.1 from 36.133. Currently only using first condition - if (measure_p.rsrp() > ABSOLUTE_RSRP_THRESHOLD_DBM) { - cells[nof_cells].pci = found_cell.id; - cells[nof_cells].rsrp = measure_p.rsrp(); - cells[nof_cells].rsrq = measure_p.rsrq(); - cells[nof_cells].offset = measure_p.frame_st_idx(); - - Info("INTRA: Found neighbour cell %d: PCI=%03d, RSRP=%5.1f dBm, SNR=%5.1f dB, peak_idx=%5d, " - "peak_value=%3.2f, " - "sf=%d, max_sf=%d, n_id_2=%d, CFO=%6.1f/%6.1fHz\n", - nof_cells, - cell_id, - measure_p.rsrp(), - measure_p.snr(), - measure_p.frame_st_idx(), - sync_find.peak_value, - sf_idx, - max_sf5, - n_id_2, - 15000 * srslte_sync_get_cfo(&sync_find), - 15000 * measure_p.cfo()); - - nof_cells++; - - /* - if (sic_pss_enabled) { - srslte_pss_sic(&sync_find.pss, &input_buffer[sf5_cnt * 5 * sf_len + sf_len / 2 - fft_sz]); - }*/ - } else { - Info("INTRA: Found neighbour cell but RSRP=%.1f dBm is below threshold (%.1f dBm)\n", - measure_p.rsrp(), - ABSOLUTE_RSRP_THRESHOLD_DBM); - } - break; - case measure::ERROR: - Error("INTRA: Measuring neighbour cell\n"); - return SRSLTE_ERROR; - } - } - } else { - sync_res = SRSLTE_SYNC_NOFOUND; - } - break; - case SRSLTE_SYNC_FOUND_NOSPACE: - /* If a peak was found but there is not enough space for SSS/CP detection, discard a few samples */ - break; - default: - break; - } - } while (sync_res == SRSLTE_SYNC_FOUND && sic_pss_enabled && nof_cells < MAX_CELLS); + log_h->debug("INTRA: n_id_2=%d, cnt=%d/%d, sync_res=%d, cell_id=%d, sf_idx=%d, peak_idx=%d, peak_value=%f, " + "sss_detected=%d\n", + n_id_2, + sf5_cnt, + nof_sf / 5, + sync_res, + cell_id, + srslte_sync_get_sf_idx(&sync_find), + peak_idx, + sync_find.peak_value, + srslte_sync_sss_detected(&sync_find)); + } + + // If the SSS was not detected, the serving_cell id is not reliable. So, consider no sync found + if (sync_res == SRSLTE_SYNC_FOUND && sss_detected && cell_id >= 0) { + // We have found a new cell, add to the list + found_cell_ids.insert((uint32_t)cell_id); + log_h->debug("INTRA: Detected new cell_id=%d using PSS/SSS\n", cell_id); + } } } - return nof_cells; + return found_cell_ids; } } // namespace scell diff --git a/srsue/src/phy/sf_worker.cc b/srsue/src/phy/sf_worker.cc index e41937717..27a6f5838 100644 --- a/srsue/src/phy/sf_worker.cc +++ b/srsue/src/phy/sf_worker.cc @@ -297,7 +297,7 @@ void sf_worker::update_measurements() 30) : 0; if (std::isnormal(rssi_dbm)) { - phy->avg_rssi_dbm = SRSLTE_VEC_EMA(rssi_dbm, phy->avg_rssi_dbm, phy->args->snr_ema_coeff); + phy->avg_rssi_dbm[0] = SRSLTE_VEC_EMA(rssi_dbm, phy->avg_rssi_dbm[0], phy->args->snr_ema_coeff); } if (!rssi_read_cnt) { @@ -310,27 +310,30 @@ void sf_worker::update_measurements() } // Run measurements in all carriers + std::vector serving_cells = {}; for (uint32_t cc_idx = 0; cc_idx < cc_workers.size(); cc_idx++) { + bool active = (cc_idx == 0 || phy->scell_cfg[cc_idx].configured); + // Update measurement of the Component Carrier cc_workers[cc_idx]->update_measurements(); - // Send measurements - if ((tti % phy->pcell_report_period) == phy->pcell_report_period - 1) { - if (cc_idx == 0) { - // Send report for PCell - phy->stack->new_phy_meas(phy->avg_rsrp_dbm[0], phy->avg_rsrq_db, tti); - } else { - // Send report for SCell (if enabled) - if (phy->scell_cfg[cc_idx].enabled) { - phy->stack->new_phy_meas(phy->avg_rsrp_dbm[cc_idx], - phy->avg_rsrq_db, - tti, - phy->scell_cfg[cc_idx].earfcn, - phy->scell_cfg[cc_idx].pci); - } + // Send measurements for serving cells + if (active && ((tti % phy->pcell_report_period) == phy->pcell_report_period - 1)) { + rrc_interface_phy_lte::phy_meas_t meas = {}; + meas.rsrp = phy->avg_rsrp_dbm[cc_idx]; + meas.rsrq = phy->avg_rsrq_db[cc_idx]; + // Save EARFCN and PCI for secondary cells, primary cell has earfcn=0 + if (cc_idx > 0) { + meas.earfcn = phy->scell_cfg[cc_idx].earfcn; + meas.pci = phy->scell_cfg[cc_idx].pci; } + serving_cells.push_back(meas); } } + // Send report to stack + if (not serving_cells.empty()) { + phy->stack->new_cell_meas(serving_cells); + } // Check in-sync / out-sync conditions if (phy->avg_rsrp_dbm[0] > phy->args->in_sync_rsrp_dbm_th && phy->avg_snr_db_cqi[0] > phy->args->in_sync_snr_db_th) { diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index aa2e8941a..77cbbc403 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -98,7 +98,11 @@ void sync::init(srslte::radio_interface_phy* _radio, sfn_p.init(&ue_sync, sf_buffer[0], log_h); // Start intra-frequency measurement - intra_freq_meas.init(worker_com, stack, log_h); + for (uint32_t i = 0; i < worker_com->args->nof_carriers; i++) { + auto q = new scell::intra_measure; + q->init(worker_com, stack, log_h); + intra_freq_meas.push_back(std::unique_ptr(q)); + } reset(); running = true; @@ -136,7 +140,9 @@ sync::~sync() void sync::stop() { worker_com->semaphore.wait_all(); - intra_freq_meas.stop(); + for (auto& q : intra_freq_meas) { + q->stop(); + } running = false; wait_thread_finish(); } @@ -204,6 +210,9 @@ phy_interface_rrc_lte::cell_search_ret_t sync::cell_search(phy_interface_rrc_lte phy_state.go_idle(); worker_com->reset(); + // Stop all intra-frequency measurement before changing frequency + meas_stop(); + try { if (current_earfcn != (int)earfcn.at(cellsearch_earfcn_index)) { current_earfcn = (int)earfcn[cellsearch_earfcn_index]; @@ -224,7 +233,9 @@ phy_interface_rrc_lte::cell_search_ret_t sync::cell_search(phy_interface_rrc_lte case search::CELL_FOUND: // If a cell is found, configure it, synchronize and measure it if (set_cell()) { - intra_freq_meas.set_primary_cell(current_earfcn, cell); + + // Reconfigure first intra-frequency measurement + intra_freq_meas[0]->set_primary_cell(current_earfcn, cell); Info("Cell Search: Setting sampling rate and synchronizing SFN...\n"); set_sampling_rate(); @@ -234,7 +245,7 @@ phy_interface_rrc_lte::cell_search_ret_t sync::cell_search(phy_interface_rrc_lte log_h->info("Cell Search: Sync OK. Camping on cell PCI=%d\n", cell.id); if (found_cell) { found_cell->earfcn = current_earfcn; - found_cell->cell = cell; + found_cell->pci = cell.id; } ret.found = phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND; } else { @@ -282,14 +293,11 @@ bool sync::cell_select(phy_interface_rrc_lte::phy_cell_t* new_cell) if (!new_cell) { Info("Cell Select: Starting cell resynchronization\n"); } else { - if (!srslte_cell_isvalid(&new_cell->cell)) { - log_h->error("Cell Select: Invalid cell. ID=%d, PRB=%d, ports=%d\n", cell.id, cell.nof_prb, cell.nof_ports); + if (!srslte_cellid_isvalid(new_cell->pci)) { + log_h->error("Cell Select: Invalid cell_id=%d\n", cell.id); return ret; } - Info("Cell Select: Starting cell selection for PCI=%d, n_prb=%d, EARFCN=%d\n", - new_cell->cell.id, - new_cell->cell.nof_prb, - new_cell->earfcn); + Info("Cell Select: Starting cell selection for PCI=%d, EARFCN=%d\n", new_cell->pci, new_cell->earfcn); } // Wait for any pending PHICH @@ -309,9 +317,9 @@ bool sync::cell_select(phy_interface_rrc_lte::phy_cell_t* new_cell) /* Reconfigure cell if necessary */ if (new_cell) { - if (new_cell->cell.id != cell.id) { - Info("Cell Select: Reconfiguring cell\n"); - cell = new_cell->cell; + if (new_cell->pci != cell.id) { + Info("Cell Select: Reconfiguring cell ID\n"); + cell.id = new_cell->pci; if (!set_cell()) { Error("Cell Select: Reconfiguring cell\n"); return ret; @@ -321,6 +329,10 @@ bool sync::cell_select(phy_interface_rrc_lte::phy_cell_t* new_cell) /* Select new frequency if necessary */ if ((int)new_cell->earfcn != current_earfcn) { current_earfcn = new_cell->earfcn; + + // Stop all intra-frequency measurement before changing frequency + meas_stop(); + Info("Cell Select: Setting new frequency EARFCN=%d\n", new_cell->earfcn); if (!set_frequency()) { Error("Cell Select: Setting new frequency EARFCN=%d\n", new_cell->earfcn); @@ -328,14 +340,14 @@ bool sync::cell_select(phy_interface_rrc_lte::phy_cell_t* new_cell) } } - /* Reconfigure intra-frequency measurement */ - intra_freq_meas.set_primary_cell(current_earfcn, cell); + // Reconfigure first intra-frequency measurement + intra_freq_meas[0]->set_primary_cell(current_earfcn, cell); } /* Change sampling rate if necessary */ if (srate_mode != SRATE_CAMP) { - set_sampling_rate(); log_h->info("Cell Select: Setting CAMPING sampling rate\n"); + set_sampling_rate(); } /* SFN synchronization */ @@ -375,7 +387,8 @@ bool sync::cell_is_camping() void sync::run_thread() { sf_worker* worker = nullptr; - cf_t* buffer[SRSLTE_MAX_RADIOS][SRSLTE_MAX_PORTS] = {NULL}; + cf_t* buffer[SRSLTE_MAX_RADIOS][SRSLTE_MAX_PORTS] = {}; + srslte_cell_t temp_cell = {}; bool is_end_of_burst = false; bool force_camping_sfn_sync = false; @@ -419,8 +432,18 @@ void sync::run_thread() /* SFN synchronization using MIB. run_subframe() receives and processes 1 subframe * and returns */ - switch (sfn_p.run_subframe(&cell, &tti, mib)) { + temp_cell = cell; + switch (sfn_p.run_subframe(&temp_cell, &tti, mib)) { case sfn_sync::SFN_FOUND: + if (memcmp(&cell, &temp_cell, sizeof(srslte_cell_t))) { + srslte_cell_fprint(stdout, &cell, 0); + srslte_cell_fprint(stdout, &temp_cell, 0); + log_h->error("Detected cell during SFN synchronization differs from configured cell. Cell reselection to " + "cells with different MIB is not supported\n"); + log_h->console("Detected cell during SFN synchronization differs from configured cell. Cell reselection " + "to cells with different MIB is not supported\n"); + phy_state.state_exit(false); + } stack->in_sync(); phy_state.state_exit(); break; @@ -459,16 +482,22 @@ void sync::run_thread() // Force decode MIB if required if (force_camping_sfn_sync) { - uint32_t _tti = 0; - srslte_cell_t temp_cell = {}; + uint32_t _tti = 0; + temp_cell = cell; sync::sfn_sync::ret_code ret = sfn_p.decode_mib(&temp_cell, &_tti, buffer[0], mib); - if (ret == sfn_sync::SFN_FOUND) { // Force tti tti = _tti; // Disable force_camping_sfn_sync = false; + + if (memcmp(&cell, &temp_cell, sizeof(srslte_cell_t))) { + log_h->error("Detected cell during SFN synchronization differs from configured cell. Cell " + "reselection to cells with different MIB is not supported\n"); + log_h->console("Detected cell during SFN synchronization differs from configured cell. Cell " + "reselection to cells with different MIB is not supported\n"); + } } } @@ -561,12 +590,10 @@ void sync::run_thread() workers_pool->start_worker(worker); // Save signal for Intra-frequency measurement - if ((tti % 5) == 0 && worker_com->args->sic_pss_enabled) { - srslte_pss_sic(&ue_sync.strack.pss, - &buffer[0][0][SRSLTE_SF_LEN_PRB(cell.nof_prb) / 2 - ue_sync.strack.fft_size]); - } if (srslte_cell_isvalid(&cell)) { - intra_freq_meas.write(tti, buffer[0][0], SRSLTE_SF_LEN_PRB(cell.nof_prb)); + for (size_t i = 0; i < intra_freq_meas.size(); i++) { + intra_freq_meas[i]->write(tti, worker->get_buffer(i, 0), SRSLTE_SF_LEN_PRB(cell.nof_prb)); + } } break; case 0: @@ -723,12 +750,12 @@ void sync::set_agc_enable(bool enable) } } -void sync::set_time_adv_sec(float time_adv_sec) +void sync::set_time_adv_sec(float time_adv_sec_) { // If transmitting earlier, transmit less samples to align time advance. If transmit later just delay next TX - next_offset = (int)round((this->time_adv_sec - time_adv_sec) * srslte_sampling_freq_hz(cell.nof_prb)); - this->next_time_adv_sec = time_adv_sec; - Info("Applying time_adv_sec=%.1f us, next_offset=%d\n", time_adv_sec * 1e6, next_offset); + next_offset = (int)round((time_adv_sec - time_adv_sec_) * srslte_sampling_freq_hz(cell.nof_prb)); + next_time_adv_sec = time_adv_sec_; + Info("Applying time_adv_sec=%.1f us, next_offset=%d\n", time_adv_sec_ * 1e6, next_offset); } float sync::get_tx_cfo() @@ -766,8 +793,6 @@ void sync::set_ue_sync_opts(srslte_ue_sync_t* q, float cfo) worker_com->args->cfo_loop_pss_tol, worker_com->args->cfo_loop_pss_conv); - q->strack.pss.chest_on_filter = worker_com->args->sic_pss_enabled; - // Disable CP based CFO estimation during find if (cfo != 0) { q->cfo_current_value = cfo / 15000; @@ -802,6 +827,11 @@ bool sync::set_cell() return false; } + if (!srslte_cell_isvalid(&cell)) { + Error("SYNC: Setting cell: invalid cell (nof_prb=%d, pci=%d, ports=%d)\n", cell.nof_prb, cell.id, cell.nof_ports); + return false; + } + // Set cell in all objects if (srslte_ue_sync_set_cell(&ue_sync, cell)) { Error("SYNC: Setting cell: initiating ue_sync\n"); @@ -826,15 +856,15 @@ bool sync::set_cell() return true; } -void sync::set_earfcn(std::vector earfcn) +void sync::set_earfcn(std::vector earfcn_) { - this->earfcn = earfcn; + earfcn = earfcn_; } -void sync::force_freq(float dl_freq, float ul_freq) +void sync::force_freq(float dl_freq_, float ul_freq_) { - this->dl_freq = dl_freq; - this->ul_freq = ul_freq; + dl_freq = dl_freq_; + ul_freq = ul_freq_; } bool sync::set_frequency() @@ -842,9 +872,9 @@ bool sync::set_frequency() double set_dl_freq = 0; double set_ul_freq = 0; - if (this->dl_freq > 0 && this->ul_freq > 0) { - set_dl_freq = this->dl_freq; - set_ul_freq = this->ul_freq; + if (dl_freq > 0 && ul_freq > 0) { + set_dl_freq = dl_freq; + set_ul_freq = ul_freq; } else { set_dl_freq = 1e6 * srslte_band_fd(current_earfcn); if (srslte_band_is_tdd(srslte_band_get_band(current_earfcn))) { @@ -907,13 +937,13 @@ uint32_t sync::get_current_tti() return tti; } -void sync::get_current_cell(srslte_cell_t* cell, uint32_t* earfcn) +void sync::get_current_cell(srslte_cell_t* cell_, uint32_t* earfcn_) { - if (cell) { - *cell = this->cell; + if (cell_) { + *cell_ = cell; } - if (earfcn) { - *earfcn = current_earfcn; + if (earfcn_) { + *earfcn_ = current_earfcn; } } @@ -977,13 +1007,13 @@ sync::search::~search() srslte_ue_cellsearch_free(&cs); } -void sync::search::init(cf_t* buffer[SRSLTE_MAX_PORTS], srslte::log* log_h, uint32_t nof_rx_antennas, sync* parent) +void sync::search::init(cf_t* buffer_[SRSLTE_MAX_PORTS], srslte::log* log_h_, uint32_t nof_rx_antennas, sync* parent) { - this->log_h = log_h; - this->p = parent; + log_h = log_h_; + p = parent; for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { - this->buffer[i] = buffer[i]; + buffer[i] = buffer_[i]; } if (srslte_ue_cellsearch_init_multi(&cs, 8, radio_recv_callback, nof_rx_antennas, parent)) { @@ -1025,15 +1055,12 @@ void sync::search::set_agc_enable(bool enable) } } -sync::search::ret_code sync::search::run(srslte_cell_t* cell, std::array& bch_payload) +sync::search::ret_code sync::search::run(srslte_cell_t* cell_, std::array& bch_payload) { - if (!cell) { - return ERROR; - } + srslte_cell_t new_cell = {}; srslte_ue_cellsearch_result_t found_cells[3]; - bzero(cell, sizeof(srslte_cell_t)); bzero(found_cells, 3 * sizeof(srslte_ue_cellsearch_result_t)); if (p->srate_mode != SRATE_FIND) { @@ -1065,19 +1092,19 @@ sync::search::ret_code sync::search::run(srslte_cell_t* cell, std::arrayid = found_cells[max_peak_cell].cell_id; - cell->cp = found_cells[max_peak_cell].cp; - cell->frame_type = found_cells[max_peak_cell].frame_type; - float cfo = found_cells[max_peak_cell].cfo; + new_cell.id = found_cells[max_peak_cell].cell_id; + new_cell.cp = found_cells[max_peak_cell].cp; + new_cell.frame_type = found_cells[max_peak_cell].frame_type; + float cfo = found_cells[max_peak_cell].cfo; log_h->console("\n"); Info("SYNC: PSS/SSS detected: Mode=%s, PCI=%d, CFO=%.1f KHz, CP=%s\n", - cell->frame_type ? "TDD" : "FDD", - cell->id, + new_cell.frame_type ? "TDD" : "FDD", + new_cell.id, cfo / 1000, - srslte_cp_string(cell->cp)); + srslte_cp_string(new_cell.cp)); - if (srslte_ue_mib_sync_set_cell(&ue_mib_sync, *cell)) { + if (srslte_ue_mib_sync_set_cell(&ue_mib_sync, new_cell)) { Error("SYNC: Setting UE MIB cell\n"); return ERROR; } @@ -1089,9 +1116,9 @@ sync::search::ret_code sync::search::run(srslte_cell_t* cell, std::arraynof_ports, &sfn_offset); + ret = srslte_ue_mib_sync_decode(&ue_mib_sync, 40, bch_payload.data(), &new_cell.nof_ports, &sfn_offset); if (ret == 1) { - srslte_pbch_mib_unpack(bch_payload.data(), cell, NULL); + srslte_pbch_mib_unpack(bch_payload.data(), &new_cell, NULL); // pack MIB and store inplace for PCAP dump std::array mib_packed; srslte_bit_pack_vector(bch_payload.data(), mib_packed.data(), SRSLTE_BCH_PAYLOAD_LEN); @@ -1099,23 +1126,29 @@ sync::search::ret_code sync::search::run(srslte_cell_t* cell, std::arrayframe_type ? "TDD" : "FDD", - cell->id, - cell->nof_prb, - cell->nof_ports, + new_cell.frame_type ? "TDD" : "FDD", + new_cell.id, + new_cell.nof_prb, + new_cell.nof_ports, cfo / 1000); Info("SYNC: MIB Decoded: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n", - cell->frame_type ? "TDD" : "FDD", - cell->id, - cell->nof_prb, - cell->nof_ports, + new_cell.frame_type ? "TDD" : "FDD", + new_cell.id, + new_cell.nof_prb, + new_cell.nof_ports, cfo / 1000); - if (!srslte_cell_isvalid(cell)) { + if (!srslte_cell_isvalid(&new_cell)) { Error("SYNC: Detected invalid cell.\n"); return CELL_NOT_FOUND; } + + // Save cell pointer + if (cell_) { + *cell_ = new_cell; + } + return CELL_FOUND; } else if (ret == 0) { Warning("SYNC: Found PSS but could not decode PBCH\n"); @@ -1135,27 +1168,27 @@ sync::sfn_sync::~sfn_sync() srslte_ue_mib_free(&ue_mib); } -void sync::sfn_sync::init(srslte_ue_sync_t* ue_sync, - cf_t* buffer[SRSLTE_MAX_PORTS], - srslte::log* log_h, +void sync::sfn_sync::init(srslte_ue_sync_t* ue_sync_, + cf_t* buffer_[SRSLTE_MAX_PORTS], + srslte::log* log_h_, uint32_t nof_subframes) { - this->log_h = log_h; - this->ue_sync = ue_sync; - this->timeout = nof_subframes; + log_h = log_h_; + ue_sync = ue_sync_; + timeout = nof_subframes; for (int p = 0; p < SRSLTE_MAX_PORTS; p++) { - this->buffer[p] = buffer[p]; + buffer[p] = buffer_[p]; } - if (srslte_ue_mib_init(&ue_mib, this->buffer, SRSLTE_MAX_PRB)) { + if (srslte_ue_mib_init(&ue_mib, buffer, SRSLTE_MAX_PRB)) { Error("SYNC: Initiating UE MIB decoder\n"); } } -bool sync::sfn_sync::set_cell(srslte_cell_t cell) +bool sync::sfn_sync::set_cell(srslte_cell_t cell_) { - if (srslte_ue_mib_set_cell(&ue_mib, cell)) { + if (srslte_ue_mib_set_cell(&ue_mib, cell_)) { Error("SYNC: Setting cell: initiating ue_mib\n"); return false; } @@ -1169,7 +1202,7 @@ void sync::sfn_sync::reset() srslte_ue_mib_reset(&ue_mib); } -sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t* cell, +sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t* cell_, uint32_t* tti_cnt, std::array& bch_payload, bool sfidx_only) @@ -1182,7 +1215,7 @@ sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t* } if (ret == 1) { - sync::sfn_sync::ret_code ret2 = decode_mib(cell, tti_cnt, NULL, bch_payload, sfidx_only); + sync::sfn_sync::ret_code ret2 = decode_mib(cell_, tti_cnt, NULL, bch_payload, sfidx_only); if (ret2 != SFN_NOFOUND) { return ret2; } @@ -1199,7 +1232,7 @@ sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t* return IDLE; } -sync::sfn_sync::ret_code sync::sfn_sync::decode_mib(srslte_cell_t* cell, +sync::sfn_sync::ret_code sync::sfn_sync::decode_mib(srslte_cell_t* cell_, uint32_t* tti_cnt, cf_t* ext_buffer[SRSLTE_MAX_PORTS], std::array& bch_payload, @@ -1228,7 +1261,7 @@ sync::sfn_sync::ret_code sync::sfn_sync::decode_mib(srslte_cell_t* cell, return ERROR; case SRSLTE_UE_MIB_FOUND: uint32_t sfn; - srslte_pbch_mib_unpack(bch_payload.data(), cell, &sfn); + srslte_pbch_mib_unpack(bch_payload.data(), cell_, &sfn); sfn = (sfn + sfn_offset) % 1024; if (tti_cnt) { @@ -1252,39 +1285,31 @@ sync::sfn_sync::ret_code sync::sfn_sync::decode_mib(srslte_cell_t* cell, * */ -void sync::meas_reset() +void sync::set_inter_frequency_measurement(uint32_t cc_idx, uint32_t earfcn_, srslte_cell_t cell_) { - // Stop all measurements - intra_freq_meas.clear_cells(); + if (cc_idx < intra_freq_meas.size()) { + intra_freq_meas[cc_idx]->set_primary_cell(earfcn_, cell_); + } } - -int sync::meas_start(uint32_t earfcn, int pci) +void sync::set_cells_to_meas(uint32_t earfcn_, std::set& pci) { - if ((int)earfcn == current_earfcn) { - if (pci != (int)cell.id) { - intra_freq_meas.add_cell(pci); + bool found = false; + for (size_t i = 0; i < intra_freq_meas.size() and not found; i++) { + if (earfcn_ == intra_freq_meas[i]->get_earfcn()) { + intra_freq_meas[i]->set_cells_to_meas(pci); + found = true; } - return 0; - } else { - Warning("INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested measurement for %d)\n", - current_earfcn, - earfcn); - return -1; + } + if (!found) { + log_h->error("Neighbour cell measurement not supported in secondary carrier. EARFCN=%d\n", earfcn_); } } -int sync::meas_stop(uint32_t earfcn, int pci) +void sync::meas_stop() { - if ((int)earfcn == current_earfcn) { - intra_freq_meas.rem_cell(pci); - return 0; - } else { - Warning( - "INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested stop measurement for %d)\n", - current_earfcn, - earfcn); + for (auto& q : intra_freq_meas) { + q->meas_stop(); } - return -1; } } // namespace srsue diff --git a/srsue/src/stack/mac/dl_harq.cc b/srsue/src/stack/mac/dl_harq.cc index 2456db465..ffc30f1e3 100644 --- a/srsue/src/stack/mac/dl_harq.cc +++ b/srsue/src/stack/mac/dl_harq.cc @@ -34,25 +34,20 @@ namespace srsue { dl_harq_entity::dl_harq_entity() : proc(SRSLTE_MAX_HARQ_PROC) { - pcap = NULL; - demux_unit = NULL; - log_h = NULL; - timer_aligment_timer = NULL; + pcap = nullptr; + demux_unit = nullptr; + log_h = nullptr; si_window_start = 0; last_temporal_crnti = 0; average_retx = 0; nof_pkts = 0; } -bool dl_harq_entity::init(srslte::log* log_h, - mac_interface_rrc::ue_rnti_t* rntis, - srslte::timer_handler::unique_timer* timer_aligment_timer_, - demux* demux_unit) +bool dl_harq_entity::init(srslte::log* log_h_, mac_interface_rrc::ue_rnti_t* rntis_, demux* demux_unit_) { - timer_aligment_timer = timer_aligment_timer_; - this->demux_unit = demux_unit; - this->log_h = log_h; - this->rntis = rntis; + demux_unit = demux_unit_; + log_h = log_h_; + rntis = rntis_; for (uint32_t i = 0; i < SRSLTE_MAX_HARQ_PROC; i++) { if (!proc[i].init(i, this)) { @@ -329,12 +324,8 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::new_grant_dl(mac_interface_ } } - if (is_bcch || harq_entity->timer_aligment_timer->is_expired()) { - // Do not generate ACK - action->generate_ack = false; - } else { - action->generate_ack = true; - } + // Do NOT generate ACK if Broadcast Control Channel + action->generate_ack = not is_bcch; } void dl_harq_entity::dl_harq_process::dl_tb_process::tb_decoded(mac_interface_phy_lte::mac_grant_dl_t grant, diff --git a/srsue/src/stack/mac/mac.cc b/srsue/src/stack/mac/mac.cc index 19bd3e604..5485571f0 100644 --- a/srsue/src/stack/mac/mac.cc +++ b/srsue/src/stack/mac/mac.cc @@ -90,7 +90,7 @@ bool mac::init(phy_interface_mac_lte* phy, // Create UL/DL unique HARQ pointers ul_harq.at(0)->init(log_h, &uernti, &ra_procedure, &mux_unit); - dl_harq.at(0)->init(log_h, &uernti, &timer_alignment, &demux_unit); + dl_harq.at(0)->init(log_h, &uernti, &demux_unit); reset(); @@ -137,7 +137,7 @@ void mac::reconfiguration(const uint32_t& cc_idx, const bool& enable) } while (dl_harq.size() < cc_idx + 1) { auto dl = dl_harq_entity_ptr(new dl_harq_entity()); - dl->init(log_h, &uernti, &timer_alignment, &demux_unit); + dl->init(log_h, &uernti, &demux_unit); if (pcap) { dl->start_pcap(pcap); @@ -538,6 +538,7 @@ void mac::setup_timers(int time_alignment_timer) void mac::timer_expired(uint32_t timer_id) { if (timer_id == timer_alignment.id()) { + Info("Time Alignment Timer expired\n"); timer_alignment_expire(); } else { Warning("Received callback from unknown timer_id=%d\n", timer_id); diff --git a/srsue/src/stack/rrc/CMakeLists.txt b/srsue/src/stack/rrc/CMakeLists.txt index e8d85f2db..103b04720 100644 --- a/srsue/src/stack/rrc/CMakeLists.txt +++ b/srsue/src/stack/rrc/CMakeLists.txt @@ -18,7 +18,7 @@ # and at http://www.gnu.org/licenses/. # -set(SOURCES rrc.cc rrc_procedures.cc) +set(SOURCES rrc.cc rrc_procedures.cc rrc_meas.cc) add_library(srsue_rrc STATIC ${SOURCES}) diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index ee26a995b..bdb5c8b25 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -23,6 +23,7 @@ #include "srslte/asn1/rrc_asn1.h" #include "srslte/common/bcd_helpers.h" #include "srslte/common/security.h" +#include "srsue/hdr/stack/rrc/rrc_meas.h" #include "srsue/hdr/stack/rrc/rrc_procedures.h" #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -63,11 +65,15 @@ rrc::rrc(srslte::log* rrc_log_) : plmn_searcher(this), cell_reselector(this), connection_reest(this), - serving_cell(unique_cell_t(new cell_t({}, 0.0))) + serving_cell(unique_cell_t(new cell_t())) { + measurements = new rrc_meas(rrc_log); } -rrc::~rrc() = default; +rrc::~rrc() +{ + delete measurements; +} template void rrc::log_rrc_message(const std::string source, @@ -140,7 +146,7 @@ void rrc::init(phy_interface_rrc_lte* phy_, // Set default values for RRC. MAC and PHY are set to default themselves set_rrc_default(); - measurements.init(this); + measurements->init(this); struct timeval tv; gettimeofday(&tv, NULL); @@ -199,7 +205,7 @@ void rrc::run_tti(uint32_t tti) } // Process pending PHY measurements in IDLE/CONNECTED - process_phy_meas(); + process_cell_meas(); // Process on-going callbacks, and clear finished callbacks callback_list.run(); @@ -225,7 +231,7 @@ void rrc::run_tti(uint32_t tti) } break; case RRC_STATE_CONNECTED: - measurements.run_tti(tti); + measurements->run_tti(tti); break; default: break; @@ -251,6 +257,9 @@ void rrc::run_tti(uint32_t tti) case cmd_msg_t::PDU_BCCH_DLSCH: parse_pdu_bcch_dlsch(std::move(msg.pdu)); break; + case cmd_msg_t::HO_COMPLETE: + process_ho_ra_completed(msg.lcid > 0); + break; case cmd_msg_t::STOP: return; } @@ -352,59 +361,61 @@ void rrc::set_ue_identity(srslte::s_tmsi_t s_tmsi) /* This function is called from a PHY worker thus must return very quickly. * Queue the values of the measurements and process them from the RRC thread */ -void rrc::new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn_i, int pci_i) +void rrc::new_cell_meas(std::vector& meas) { - uint32_t pci = 0; - uint32_t earfcn = 0; - if (earfcn_i < 0) { - earfcn = (uint32_t)serving_cell->get_earfcn(); - } else { - earfcn = (uint32_t)earfcn_i; - } - if (pci_i < 0) { - pci = (uint32_t)serving_cell->get_pci(); - } else { - pci = (uint32_t)pci_i; - } - phy_meas_t new_meas = {rsrp, rsrq, tti, earfcn, pci}; - phy_meas_q.push(new_meas); - rrc_log->info("MEAS: New measurement earfcn=%d, pci=%d (%s), rsrp=%.1f dBm.\n", - earfcn_i, - pci, - pci_i < 0 ? "serving" : "neighbour", - rsrp); + cell_meas_q.push(meas); } /* Processes all pending PHY measurements in queue. Must be called from a mutexed function */ -void rrc::process_phy_meas() +void rrc::process_cell_meas() { - phy_meas_t m; - while (phy_meas_q.try_pop(&m)) { - rrc_log->debug("MEAS: Processing measurement. %zd measurements in queue\n", phy_meas_q.size()); - process_new_phy_meas(m); + std::vector m; + while (cell_meas_q.try_pop(&m)) { + if (cell_meas_q.size() > 0) { + rrc_log->debug("MEAS: Processing measurement. %zd measurements in queue\n", cell_meas_q.size()); + } + process_new_cell_meas(m); } } -void rrc::process_new_phy_meas(phy_meas_t meas) +void rrc::process_new_cell_meas(std::vector& meas) { - float rsrp = meas.rsrp; - float rsrq = meas.rsrq; - uint32_t tti = meas.tti; - uint32_t earfcn = meas.earfcn; - uint32_t pci = meas.pci; + bool neighbour_added = false; + rrc_log->debug("MEAS: Processing measurement of %lu cells\n", meas.size()); + for (auto& m : meas) { + cell_t* c = nullptr; + // Get serving_cell handle if it's the serving cell + if (m.earfcn == 0 or (m.earfcn == serving_cell->get_earfcn() and m.pci == serving_cell->get_pci())) { + c = serving_cell.get(); + if (c == nullptr || !serving_cell->is_valid()) { + rrc_log->error("MEAS: Received serving cell measurement but undefined or invalid\n"); + return; + } + // Or update/add RRC neighbour cell database + } else { + c = get_neighbour_cell_handle(m.earfcn, m.pci); + } + // Filter RSRP/RSRQ measurements if cell exits + if (c != nullptr) { + c->set_rsrp(measurements->rsrp_filter(m.rsrp, c->get_rsrp())); + c->set_rsrq(measurements->rsrq_filter(m.rsrq, c->get_rsrq())); + } else { + // or just set initial value + neighbour_added = add_neighbour_cell(m); + } - if (state == RRC_STATE_IDLE) { - // Update serving cell - if (serving_cell->equals(earfcn, pci)) { - serving_cell->set_rsrp(rsrp); + if (m.earfcn == 0) { + rrc_log->info("MEAS: New measurement serving cell: rsrp=%.2f dBm.\n", m.rsrp); } else { - // Or update/add neighbour cell - add_neighbour_cell(earfcn, pci, rsrp); + rrc_log->info( + "MEAS: New measurement neighbour cell: earfcn=%d, pci=%d, rsrp=%.2f dBm.\n", m.earfcn, m.pci, m.rsrp); } - } else { - // Measurements in RRC_CONNECTED go through measurement class to log reports etc. - measurements.new_phy_meas(earfcn, pci, rsrp, rsrq, tti); + } + + // Instruct measurements subclass to update phy with new cells to measure based on strongest neighbours + if (state == RRC_STATE_CONNECTED && neighbour_added) { + measurements->update_phy(); } } @@ -418,7 +429,10 @@ void rrc::out_of_sync() // upon receiving N310 consecutive "out-of-sync" indications for the PCell from lower layers while neither T300, // T301, T304 nor T311 is running: if (state == RRC_STATE_CONNECTED) { - if (!t300.is_running() && !t301.is_running() && !t304.is_running() && !t310.is_running() && !t311.is_running()) { + // upon receiving N310 consecutive "out-of-sync" indications from lower layers while neither T300, T301, T304 + // nor T311 is running + bool t311_running = t311.is_running() || connection_reest.is_busy(); + if (!t300.is_running() and !t301.is_running() and !t304.is_running() and !t310.is_running() and !t311_running) { rrc_log->info("Received out-of-sync while in state %s. n310=%d, t311=%s, t310=%s\n", rrc_state_text[state], n310_cnt, @@ -485,37 +499,31 @@ void rrc::cell_reselection(float rsrp, float rsrq) // Intra-frequency cell-reselection criteria if (get_srxlev(rsrp) > cell_resel_cfg.s_intrasearchP && rsrp > -95.0) { - // UE may not perform intra-frequency measurements. - phy->meas_reset(); - // keep measuring serving cell - phy->meas_start(phy->get_current_earfcn(), phy->get_current_pci()); + // UE may not perform intra-frequency measurements-> + phy->meas_stop(); } else { // UE must start intra-frequency measurements - phy->meas_start(phy->get_current_earfcn(), -1); + auto pci = get_neighbour_pcis(serving_cell->get_earfcn()); + phy->set_cells_to_meas(serving_cell->get_earfcn(), pci); } // TODO: Inter-frequency cell reselection } // Set new serving cell -void rrc::set_serving_cell(phy_interface_rrc_lte::phy_cell_t phy_cell) +void rrc::set_serving_cell(phy_interface_rrc_lte::phy_cell_t phy_cell, bool discard_serving) { - if (has_neighbour_cell(phy_cell.earfcn, phy_cell.cell.id)) { + if (has_neighbour_cell(phy_cell.earfcn, phy_cell.pci)) { // Remove future serving cell from neighbours to make space for current serving cell - unique_cell_t new_serving_cell = remove_neighbour_cell(phy_cell.earfcn, phy_cell.cell.id); + unique_cell_t new_serving_cell = remove_neighbour_cell(phy_cell.earfcn, phy_cell.pci); + bool same_cell = (phy_cell.earfcn == serving_cell->get_earfcn() and phy_cell.pci == serving_cell->get_pci()); // Move serving cell to neighbours list - if (serving_cell->is_valid()) { - if (has_neighbour_cell(serving_cell->get_earfcn(), serving_cell->get_pci())) { - rrc_log->error("Error serving cell is already in the neighbour list.\n"); - // note, removing it here is not an option - } else { - if (add_neighbour_cell(std::move(serving_cell)) == false) { - rrc_log->info("Serving cell not added to list of neighbours. Worse than current neighbours\n"); - } + if (serving_cell->is_valid() and not same_cell and not discard_serving) { + if (not add_neighbour_cell(std::move(serving_cell))) { + rrc_log->info("Serving cell not added to list of neighbours. Worse than current neighbours\n"); } } - // Set new serving cell serving_cell = std::move(new_serving_cell); rrc_log->info("Setting serving cell earfcn=%d, PCI=%d, rsrp=%.2f, nof_neighbours=%zd\n", @@ -524,7 +532,7 @@ void rrc::set_serving_cell(phy_interface_rrc_lte::phy_cell_t phy_cell) serving_cell->get_rsrp(), neighbour_cells.size()); } else { - rrc_log->error("Setting serving cell: Unknown cell with earfcn=%d, PCI=%d\n", phy_cell.earfcn, phy_cell.cell.id); + rrc_log->error("Setting serving cell: Unknown cell with earfcn=%d, PCI=%d\n", phy_cell.earfcn, phy_cell.pci); } } @@ -533,7 +541,6 @@ void rrc::delete_last_neighbour() if (not neighbour_cells.empty()) { auto& it = neighbour_cells.back(); rrc_log->debug("Delete cell earfcn=%d, pci=%d from neighbor list.\n", (*it).get_earfcn(), (*it).get_pci()); - measurements.delete_report((*it).get_earfcn(), (*it).get_pci()); neighbour_cells.pop_back(); } } @@ -555,21 +562,16 @@ void rrc::clean_neighbours() } } -// Sort neighbour cells by decreasing order of RSRP -void rrc::sort_neighbour_cells() +void rrc::log_neighbour_cells() { - std::sort(std::begin(neighbour_cells), std::end(neighbour_cells), [](const unique_cell_t& a, const unique_cell_t& b) { - return a->greater(b.get()); - }); - - if (neighbour_cells.size() > 0) { + if (not neighbour_cells.empty()) { char ordered[512] = {}; int n = 0; n += snprintf(ordered, 512, "[earfcn=%d, pci=%d, rsrp=%.2f", neighbour_cells[0]->get_earfcn(), - neighbour_cells[0]->phy_cell.cell.id, + neighbour_cells[0]->phy_cell.pci, neighbour_cells[0]->get_rsrp()); for (uint32_t i = 1; i < neighbour_cells.size(); i++) { int m = snprintf(&ordered[n], @@ -586,9 +588,31 @@ void rrc::sort_neighbour_cells() } } +// Sort neighbour cells by decreasing order of RSRP +void rrc::sort_neighbour_cells() +{ + std::sort(std::begin(neighbour_cells), std::end(neighbour_cells), [](const unique_cell_t& a, const unique_cell_t& b) { + return a->greater(b.get()); + }); + + log_neighbour_cells(); +} + bool rrc::add_neighbour_cell(unique_cell_t new_cell) { bool ret = false; + // Make sure cell is valid + if (!new_cell->is_valid()) { + rrc_log->error( + "Trying to add cell EARFCN=%d, PCI=%d but is not valid", new_cell->get_earfcn(), new_cell->get_pci()); + return ret; + } + // First make sure it doesn't exist + if (has_neighbour_cell(new_cell->get_earfcn(), new_cell->get_pci())) { + rrc_log->warning( + "Trying to add cell EARFCN=%d, PCI=%d but already exists", new_cell->get_earfcn(), new_cell->get_pci()); + return ret; + } if (neighbour_cells.size() < NOF_NEIGHBOUR_CELLS) { ret = true; } else if (new_cell->greater(neighbour_cells.back().get())) { @@ -597,10 +621,8 @@ bool rrc::add_neighbour_cell(unique_cell_t new_cell) ret = true; } if (ret) { - rrc_log->info("Adding neighbour cell EARFCN=%d, PCI=%d, nof_neighbours=%zd\n", - new_cell->get_earfcn(), - new_cell->get_pci(), - neighbour_cells.size() + 1); + rrc_log->info( + "Adding neighbour cell %s, nof_neighbours=%zd\n", new_cell->print().c_str(), neighbour_cells.size() + 1); neighbour_cells.push_back(std::move(new_cell)); } @@ -609,30 +631,15 @@ bool rrc::add_neighbour_cell(unique_cell_t new_cell) } // If only neighbour PCI is provided, copy full cell from serving cell -bool rrc::add_neighbour_cell(uint32_t earfcn, uint32_t pci, float rsrp) +bool rrc::add_neighbour_cell(phy_meas_t meas) { - if (serving_cell->is_valid()) { - phy_interface_rrc_lte::phy_cell_t phy_cell; - phy_cell = serving_cell->phy_cell; - phy_cell.earfcn = earfcn; - phy_cell.cell.id = pci; - return add_neighbour_cell(phy_cell, rsrp); - } - return false; -} - -bool rrc::add_neighbour_cell(phy_interface_rrc_lte::phy_cell_t phy_cell, float rsrp) -{ - // if cell exists, update RSRP, if provided, sort again and return - cell_t* cell = get_neighbour_cell_handle(phy_cell.earfcn, phy_cell.cell.id); - if (cell && std::isnormal(rsrp)) { - cell->set_rsrp(rsrp); - sort_neighbour_cells(); - return true; - } - - // if cell doesn't exist, add it - return add_neighbour_cell(unique_cell_t(new cell_t(phy_cell, rsrp))); + phy_interface_rrc_lte::phy_cell_t phy_cell = {}; + phy_cell.earfcn = meas.earfcn; + phy_cell.pci = meas.pci; + unique_cell_t c = unique_cell_t(new cell_t(phy_cell)); + c.get()->set_rsrp(meas.rsrp); + c.get()->set_rsrq(meas.rsrq); + return add_neighbour_cell(std::move(c)); } // This will remove the cell from the current neighbour list @@ -678,6 +685,17 @@ std::string rrc::print_neighbour_cells() return s; } +std::set rrc::get_neighbour_pcis(uint32_t earfcn) +{ + std::set pcis = {}; + for (auto& cell : neighbour_cells) { + if (cell->get_earfcn() == earfcn) { + pcis.insert(cell->get_pci()); + } + } + return pcis; +} + /******************************************************************************* * * @@ -689,7 +707,7 @@ std::string rrc::print_neighbour_cells() *******************************************************************************/ std::string rrc::print_mbms() { - asn1::rrc::mcch_msg_type_c msg = serving_cell->mcch.msg; + mcch_msg_type_c msg = serving_cell->mcch.msg; std::stringstream ss; for (uint32_t i = 0; i < msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9.size(); i++) { ss << "PMCH: " << i << std::endl; @@ -722,7 +740,7 @@ bool rrc::mbms_service_start(uint32_t serv, uint32_t port) rrc_log->info("%s\n", print_mbms().c_str()); - asn1::rrc::mcch_msg_type_c msg = serving_cell->mcch.msg; + mcch_msg_type_c msg = serving_cell->mcch.msg; for (uint32_t i = 0; i < msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9.size(); i++) { pmch_info_r9_s* pmch = &msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9[i]; for (uint32_t j = 0; j < pmch->mbms_session_info_list_r9.size(); j++) { @@ -756,7 +774,7 @@ void rrc::radio_link_failure() rrc_log->warning("Detected Radio-Link Failure\n"); rrc_log->console("Warning: Detected Radio-Link Failure\n"); if (state == RRC_STATE_CONNECTED) { - start_con_restablishment(asn1::rrc::reest_cause_e::other_fail); + start_con_restablishment(reest_cause_e::other_fail); } } @@ -789,8 +807,12 @@ void rrc::timer_expired(uint32_t timeout_id) rrc_log->info("Timer T310 expired: Radio Link Failure\n"); radio_link_failure(); } else if (timeout_id == t311.id()) { - rrc_log->info("Timer T311 expired: Going to RRC IDLE\n"); - start_go_idle(); + if (connection_reest.is_idle()) { + rrc_log->info("Timer T311 expired: Going to RRC IDLE\n"); + start_go_idle(); + } else { + // Do nothing, this is handled by the procedure + } } else if (timeout_id == t301.id()) { if (state == RRC_STATE_IDLE) { rrc_log->info("Timer T301 expired: Already in IDLE.\n"); @@ -806,8 +828,7 @@ void rrc::timer_expired(uint32_t timeout_id) } else if (timeout_id == t304.id()) { rrc_log->console("Timer T304 expired: Handover failed\n"); ho_failed(); - // fw to measurement - } else if (!measurements.timer_expired(timeout_id)) { + } else { rrc_log->error("Timeout from unknown timer id %d\n", timeout_id); } } @@ -843,24 +864,24 @@ void rrc::send_con_request(srslte::establishment_cause_t cause) } rrc_conn_req->ue_id.random_value().from_number(random_id); } - rrc_conn_req->establishment_cause = (asn1::rrc::establishment_cause_opts::options)cause; + rrc_conn_req->establishment_cause = (establishment_cause_opts::options)cause; send_ul_ccch_msg(ul_ccch_msg); } /* RRC connection re-establishment procedure (5.3.7.4) */ -void rrc::send_con_restablish_request(asn1::rrc::reest_cause_e cause, uint16_t crnti, uint16_t pci) +void rrc::send_con_restablish_request(reest_cause_e cause, uint16_t crnti, uint16_t pci) { uint32_t cellid; // Clean reestablishment type reestablishment_successful = false; - if (cause == asn1::rrc::reest_cause_e::ho_fail) { + if (cause == reest_cause_e::ho_fail) { crnti = ho_src_rnti; pci = ho_src_cell.get_pci(); cellid = ho_src_cell.get_cell_id(); - } else if (cause == asn1::rrc::reest_cause_e::other_fail) { + } else if (cause == reest_cause_e::other_fail) { // use source PCI after RLF cellid = serving_cell->get_cell_id(); } else { @@ -939,7 +960,7 @@ void rrc::send_con_restablish_complete() rrc_log->console("RRC Connected\n"); // Prepare ConnectionSetupComplete packet - asn1::rrc::ul_dcch_msg_s ul_dcch_msg; + ul_dcch_msg_s ul_dcch_msg; 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; @@ -985,7 +1006,7 @@ void rrc::send_security_mode_complete() rrc_log->debug("Preparing Security Mode Complete\n"); // Prepare Security Mode Command Complete - asn1::rrc::ul_dcch_msg_s ul_dcch_msg; + ul_dcch_msg_s ul_dcch_msg; ul_dcch_msg.msg.set_c1().set_security_mode_complete().crit_exts.set_security_mode_complete_r8(); ul_dcch_msg.msg.c1().security_mode_complete().rrc_transaction_id = transaction_id; @@ -996,7 +1017,7 @@ void rrc::send_rrc_con_reconfig_complete() { rrc_log->debug("Preparing RRC Connection Reconfig Complete\n"); - asn1::rrc::ul_dcch_msg_s ul_dcch_msg; + ul_dcch_msg_s ul_dcch_msg; 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; @@ -1006,8 +1027,8 @@ void rrc::send_rrc_con_reconfig_complete() bool rrc::ho_prepare() { if (pending_mob_reconf) { - asn1::rrc::rrc_conn_recfg_r8_ies_s* mob_reconf_r8 = &mob_reconf.crit_exts.c1().rrc_conn_recfg_r8(); - asn1::rrc::mob_ctrl_info_s* mob_ctrl_info = &mob_reconf_r8->mob_ctrl_info; + rrc_conn_recfg_r8_ies_s* mob_reconf_r8 = &mob_reconf.crit_exts.c1().rrc_conn_recfg_r8(); + mob_ctrl_info_s* mob_ctrl_info = &mob_reconf_r8->mob_ctrl_info; rrc_log->info("Processing HO command to target PCell=%d\n", mob_ctrl_info->target_pci); uint32_t target_earfcn = (mob_ctrl_info->carrier_freq_present) ? mob_ctrl_info->carrier_freq.dl_carrier_freq @@ -1035,7 +1056,6 @@ bool rrc::ho_prepare() rlc->reestablish(); mac->wait_uplink(); mac->clear_rntis(); - phy->meas_reset(); mac->reset(); phy->reset(); @@ -1050,12 +1070,17 @@ bool rrc::ho_prepare() cell_t* target_cell = get_neighbour_cell_handle(target_earfcn, mob_ctrl_info->target_pci); if (!phy->cell_select(&target_cell->phy_cell)) { - rrc_log->error("Could not synchronize with target cell pci=%d. Trying to return to source PCI\n", - target_cell->get_pci()); + rrc_log->error("Could not synchronize with target cell %s. Removing cell and trying to return to source %s\n", + target_cell->print().c_str(), + serving_cell->print().c_str()); + + // Remove cell from list to avoid cell re-selection, picking the same cell + target_cell->set_rsrp(-INFINITY); + return false; } - set_serving_cell(target_cell->phy_cell); + set_serving_cell(target_cell->phy_cell, false); // Extract and apply scell config if any apply_scell_config(mob_reconf_r8); @@ -1089,7 +1114,7 @@ bool rrc::ho_prepare() } usim->generate_as_keys_ho(mob_ctrl_info->target_pci, - phy->get_current_earfcn(), + serving_cell->get_earfcn(), ncc, k_rrc_enc, k_rrc_int, @@ -1105,16 +1130,21 @@ bool rrc::ho_prepare() } void rrc::ho_ra_completed(bool ra_successful) +{ + cmd_msg_t msg; + msg.command = cmd_msg_t::HO_COMPLETE; + msg.lcid = ra_successful ? 1 : 0; + cmd_q.push(std::move(msg)); +} + +void rrc::process_ho_ra_completed(bool ra_successful) { if (pending_mob_reconf) { - asn1::rrc::rrc_conn_recfg_r8_ies_s* mob_reconf_r8 = &mob_reconf.crit_exts.c1().rrc_conn_recfg_r8(); if (ra_successful) { - measurements.ho_finish(); - - if (mob_reconf_r8->meas_cfg_present) { - measurements.parse_meas_config(&mob_reconf_r8->meas_cfg); + if (!measurements->parse_meas_config( + &mob_reconf.crit_exts.c1().rrc_conn_recfg_r8(), true, ho_src_cell.get_earfcn())) { + rrc_log->error("Parsing measurementConfig. TODO: Send ReconfigurationReject\n"); } - t304.stop(); } // T304 will expiry and send ho_failure @@ -1128,10 +1158,10 @@ void rrc::ho_ra_completed(bool ra_successful) } } -bool rrc::con_reconfig_ho(asn1::rrc::rrc_conn_recfg_s* reconfig) +bool rrc::con_reconfig_ho(rrc_conn_recfg_s* reconfig) { - asn1::rrc::rrc_conn_recfg_r8_ies_s* mob_reconf_r8 = &reconfig->crit_exts.c1().rrc_conn_recfg_r8(); - if (mob_reconf_r8->mob_ctrl_info.target_pci == phy->get_current_pci()) { + rrc_conn_recfg_r8_ies_s* mob_reconf_r8 = &reconfig->crit_exts.c1().rrc_conn_recfg_r8(); + if (mob_reconf_r8->mob_ctrl_info.target_pci == serving_cell->get_pci()) { rrc_log->console("Warning: Received HO command to own cell\n"); rrc_log->warning("Received HO command to own cell\n"); return false; @@ -1175,9 +1205,9 @@ void rrc::start_go_idle() } // Handle RRC Reconfiguration without MobilityInformation Section 5.3.5.3 -bool rrc::con_reconfig(asn1::rrc::rrc_conn_recfg_s* reconfig) +bool rrc::con_reconfig(rrc_conn_recfg_s* reconfig) { - asn1::rrc::rrc_conn_recfg_r8_ies_s* reconfig_r8 = &reconfig->crit_exts.c1().rrc_conn_recfg_r8(); + rrc_conn_recfg_r8_ies_s* reconfig_r8 = &reconfig->crit_exts.c1().rrc_conn_recfg_r8(); // If this is the first con_reconfig after a reestablishment if (reestablishment_successful) { @@ -1200,6 +1230,11 @@ bool rrc::con_reconfig(asn1::rrc::rrc_conn_recfg_s* reconfig) apply_scell_config(reconfig_r8); + if (!measurements->parse_meas_config( + reconfig_r8, reestablishment_successful, connection_reest.get()->get_source_earfcn())) { + return false; + } + // If first message after reestablishment, resume SRB2 and all DRB if (reestablishment_successful) { reestablishment_successful = false; @@ -1210,12 +1245,6 @@ bool rrc::con_reconfig(asn1::rrc::rrc_conn_recfg_s* reconfig) } } - if (reconfig_r8->meas_cfg_present) { - if (!measurements.parse_meas_config(&reconfig_r8->meas_cfg)) { - return false; - } - } - send_rrc_con_reconfig_complete(); unique_byte_buffer_t nas_sdu; @@ -1236,7 +1265,7 @@ bool rrc::con_reconfig(asn1::rrc::rrc_conn_recfg_s* reconfig) // HO failure from T304 expiry 5.3.5.6 void rrc::ho_failed() { - start_con_restablishment(asn1::rrc::reest_cause_e::ho_fail); + start_con_restablishment(reest_cause_e::ho_fail); } // Reconfiguration failure or Section 5.3.5.5 @@ -1252,18 +1281,18 @@ void rrc::con_reconfig_failed() if (security_is_activated) { // Start the Reestablishment Procedure - start_con_restablishment(asn1::rrc::reest_cause_e::recfg_fail); + start_con_restablishment(reest_cause_e::recfg_fail); } else { start_go_idle(); } } -void rrc::handle_rrc_con_reconfig(uint32_t lcid, asn1::rrc::rrc_conn_recfg_s* reconfig) +void rrc::handle_rrc_con_reconfig(uint32_t lcid, rrc_conn_recfg_s* reconfig) { previous_phy_cfg = current_phy_cfg; previous_mac_cfg = current_mac_cfg; - asn1::rrc::rrc_conn_recfg_r8_ies_s* reconfig_r8 = &reconfig->crit_exts.c1().rrc_conn_recfg_r8(); + rrc_conn_recfg_r8_ies_s* reconfig_r8 = &reconfig->crit_exts.c1().rrc_conn_recfg_r8(); if (reconfig_r8->mob_ctrl_info_present) { if (!con_reconfig_ho(reconfig)) { con_reconfig_failed(); @@ -1291,7 +1320,7 @@ void rrc::leave_connected() state = RRC_STATE_IDLE; drb_up = false; security_is_activated = false; - measurements.reset(); + measurements->reset(); nas->left_rrc_connected(); pdcp->reset(); rlc->reset(); @@ -1304,8 +1333,6 @@ void rrc::leave_connected() if (phy->cell_is_camping()) { // Receive paging mac->pcch_start_rx(); - // Instruct PHY to measure serving cell for cell reselection - phy->meas_start(phy->get_current_earfcn(), phy->get_current_pci()); } } @@ -1329,7 +1356,7 @@ void rrc::stop_timers() * * The parameter cause shall indicate the cause of the reestablishment according to the sections mentioned adobe. */ -void rrc::start_con_restablishment(asn1::rrc::reest_cause_e cause) +void rrc::start_con_restablishment(reest_cause_e cause) { if (not connection_reest.launch(cause)) { rrc_log->info("Failed to launch connection re-establishment procedure\n"); @@ -1362,6 +1389,46 @@ void rrc::cell_search_completed(const phy_interface_rrc_lte::cell_search_ret_t& cell_searcher.trigger(cell_search_proc::cell_search_event_t{cs_ret, found_cell}); } +/******************************************************************************* + * + * Interface from RRC measurements class + * + *******************************************************************************/ +void rrc::send_srb1_msg(const ul_dcch_msg_s& msg) +{ + send_ul_dcch_msg(RB_ID_SRB1, msg); +} + +std::set rrc::get_cells(const uint32_t earfcn) +{ + return get_neighbour_pcis(earfcn); +} + +float rrc::get_cell_rsrp(const uint32_t earfcn, const uint32_t pci) +{ + cell_t* c = get_neighbour_cell_handle(earfcn, pci); + if (c != nullptr) { + return c->get_rsrp(); + } else { + return NAN; + } +} + +float rrc::get_cell_rsrq(const uint32_t earfcn, const uint32_t pci) +{ + cell_t* c = get_neighbour_cell_handle(earfcn, pci); + if (c != nullptr) { + return c->get_rsrq(); + } else { + return NAN; + } +} + +cell_t* rrc::get_serving_cell() +{ + return serving_cell.get(); +} + /******************************************************************************* * * @@ -1373,7 +1440,7 @@ void rrc::cell_search_completed(const phy_interface_rrc_lte::cell_search_ret_t& *******************************************************************************/ void rrc::write_pdu_bcch_bch(unique_byte_buffer_t pdu) { - asn1::rrc::bcch_bch_msg_s bch_msg; + bcch_bch_msg_s bch_msg; asn1::cbit_ref bch_bref(pdu->msg, pdu->N_bytes); asn1::SRSASN_CODE err = bch_msg.unpack(bch_bref); if (err != asn1::SRSASN_SUCCESS) { @@ -1399,8 +1466,9 @@ void rrc::parse_pdu_bcch_dlsch(unique_byte_buffer_t pdu) // Stop BCCH search after successful reception of 1 BCCH block mac->bcch_stop_rx(); - asn1::rrc::bcch_dl_sch_msg_s dlsch_msg; - asn1::cbit_ref dlsch_bref(pdu->msg, pdu->N_bytes); + bcch_dl_sch_msg_s dlsch_msg; + asn1::cbit_ref dlsch_bref(pdu->msg, pdu->N_bytes); + asn1::SRSASN_CODE err = dlsch_msg.unpack(dlsch_bref); if (err != asn1::SRSASN_SUCCESS or dlsch_msg.msg.type().value != bcch_dl_sch_msg_type_c::types_opts::c1) { rrc_log->error("Could not unpack BCCH DL-SCH message.\n"); @@ -1667,7 +1735,7 @@ void rrc::parse_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu) * *******************************************************************************/ -void rrc::send_ul_ccch_msg(const asn1::rrc::ul_ccch_msg_s& msg) +void rrc::send_ul_ccch_msg(const ul_ccch_msg_s& msg) { // Reset and reuse sdu buffer if provided unique_byte_buffer_t pdcp_buf = srslte::allocate_unique_buffer(*pool, true); @@ -1699,7 +1767,7 @@ void rrc::send_ul_ccch_msg(const asn1::rrc::ul_ccch_msg_s& msg) rlc->write_sdu(lcid, std::move(pdcp_buf)); } -void rrc::send_ul_dcch_msg(uint32_t lcid, const asn1::rrc::ul_dcch_msg_s& msg) +void rrc::send_ul_dcch_msg(uint32_t lcid, const ul_dcch_msg_s& msg) { // Reset and reuse sdu buffer if provided unique_byte_buffer_t pdcp_buf = srslte::allocate_unique_buffer(*pool, true); @@ -1898,11 +1966,11 @@ void rrc::enable_capabilities() rrc_log->info("%s 64QAM PUSCH\n", enable_ul_64 ? "Enabling" : "Disabling"); } -void rrc::handle_ue_capability_enquiry(const asn1::rrc::ue_cap_enquiry_s& enquiry) +void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry) { rrc_log->debug("Preparing UE Capability Info\n"); - asn1::rrc::ul_dcch_msg_s ul_dcch_msg; + ul_dcch_msg_s ul_dcch_msg; ue_cap_info_r8_ies_s* info = &ul_dcch_msg.msg.set_c1().set_ue_cap_info().crit_exts.set_c1().set_ue_cap_info_r8(); ul_dcch_msg.msg.c1().ue_cap_info().rrc_transaction_id = transaction_id; @@ -2299,7 +2367,7 @@ void rrc::apply_phy_config_dedicated(const phys_cfg_ded_s& phy_cnfg) } } -void rrc::apply_phy_scell_config(const asn1::rrc::scell_to_add_mod_r10_s& scell_config) +void rrc::apply_phy_scell_config(const scell_to_add_mod_r10_s& scell_config) { srslte_cell_t scell = {}; uint32_t earfcn = 0; @@ -2310,7 +2378,7 @@ void rrc::apply_phy_scell_config(const asn1::rrc::scell_to_add_mod_r10_s& scell_ } // Initialise default parameters from primary cell - phy->get_current_cell(&scell, &earfcn); + earfcn = serving_cell->get_earfcn(); // Parse identification if (scell_config.cell_identif_r10_present) { @@ -2436,7 +2504,7 @@ bool rrc::apply_rr_config_dedicated(rr_cfg_ded_s* cnfg) /* * Extracts and applies SCell configuration from an ASN.1 reconfiguration struct */ -void rrc::apply_scell_config(asn1::rrc::rrc_conn_recfg_r8_ies_s* reconfig_r8) +void rrc::apply_scell_config(rrc_conn_recfg_r8_ies_s* reconfig_r8) { if (reconfig_r8->non_crit_ext_present) { auto reconfig_r890 = &reconfig_r8->non_crit_ext; @@ -2525,7 +2593,7 @@ void rrc::handle_con_reest(rrc_conn_reest_s* setup) // Update RRC Integrity keys int ncc = setup->crit_exts.c1().rrc_conn_reest_r8().next_hop_chaining_count; usim->generate_as_keys_ho(serving_cell->get_pci(), - phy->get_current_earfcn(), + serving_cell->get_earfcn(), ncc, k_rrc_enc, k_rrc_int, @@ -2715,720 +2783,6 @@ void rrc::set_rrc_default() t311.set(1000, timer_expire_func); } -/************************************************************************ - * - * - * RRC Measurements - * - * - ************************************************************************/ - -void rrc::rrc_meas::init(rrc* parent_) -{ - parent = parent_; - log_h = parent_->rrc_log; - phy = parent_->phy; - timers = parent_->timers; - s_measure_enabled = false; - reset(); -} - -void rrc::rrc_meas::reset() -{ - filter_k_rsrp = filt_coef_e(filt_coef_e::fc4).to_number(); - filter_k_rsrq = filt_coef_e(filt_coef_e::fc4).to_number(); - - // TODO: Turn struct into a class and use destructor - std::map::iterator iter = active.begin(); - while (iter != active.end()) { - remove_meas_id(iter++); - } - - // These objects do not need destructor - objects.clear(); - reports_cfg.clear(); - phy->meas_reset(); - bzero(&pcell_measurement, sizeof(meas_value_t)); -} - -/* L3 filtering 5.5.3.2 */ -void rrc::rrc_meas::L3_filter(meas_value_t* value, float values[NOF_MEASUREMENTS]) -{ - for (int i = 0; i < NOF_MEASUREMENTS; i++) { - if (value->ms[i]) { - value->ms[i] = SRSLTE_VEC_EMA(values[i], value->ms[i], filter_a[i]); - } else { - value->ms[i] = values[i]; - } - } -} - -void rrc::rrc_meas::new_phy_meas(uint32_t earfcn, uint32_t pci, float rsrp, float rsrq, uint32_t tti) -{ - float values[NOF_MEASUREMENTS] = {rsrp, rsrq}; - - // This indicates serving cell - if (parent->serving_cell->equals(earfcn, pci)) { - - log_h->debug("MEAS: New measurement serving cell, rsrp=%f, rsrq=%f, tti=%d\n", rsrp, rsrq, tti); - - L3_filter(&pcell_measurement, values); - - // Update serving cell measurement - parent->serving_cell->set_rsrp(rsrp); - - } else { - - // Add to list of neighbour cells - bool added = parent->add_neighbour_cell(earfcn, pci, rsrp); - - log_h->debug("MEAS: New measurement %s earfcn=%d, pci=%d, rsrp=%f, rsrq=%f, tti=%d\n", - added ? "added" : "not added", - earfcn, - pci, - rsrp, - rsrq, - tti); - - // Only report measurements of 8th strongest cells - if (added) { - // Save PHY measurement for all active measurements whose earfcn/pci matches - for (std::map::iterator iter = active.begin(); iter != active.end(); ++iter) { - meas_t* m = &iter->second; - if (objects[m->object_id].earfcn == earfcn) { - // If it's a newly discovered cell, add it to objects - if (!m->cell_values.count(pci)) { - uint32_t cell_idx = objects[m->object_id].found_cells.size(); - objects[m->object_id].found_cells[cell_idx].pci = pci; - objects[m->object_id].found_cells[cell_idx].q_offset = 0; - } - // Update or add cell - L3_filter(&m->cell_values[pci], values); - } - } - } - } -} - -// Remove all stored measurements for a given cell -void rrc::rrc_meas::delete_report(uint32_t earfcn, uint32_t pci) -{ - for (std::map::iterator iter = active.begin(); iter != active.end(); ++iter) { - meas_t* m = &iter->second; - if (objects[m->object_id].earfcn == earfcn) { - if (m->cell_values.count(pci)) { - m->cell_values.erase(pci); - log_h->info("Deleting report PCI=%d from cell_values\n", pci); - } - } - } -} - -void rrc::rrc_meas::run_tti(uint32_t tti) -{ - // Measurement Report Triggering Section 5.5.4 - calculate_triggers(tti); -} - -bool rrc::rrc_meas::find_earfcn_cell(uint32_t earfcn, uint32_t pci, meas_obj_t** object, int* cell_idx) -{ - if (object) { - *object = NULL; - } - for (std::map::iterator obj = objects.begin(); obj != objects.end(); ++obj) { - if (obj->second.earfcn == earfcn) { - if (object) { - *object = &obj->second; - } - for (std::map::iterator c = obj->second.found_cells.begin(); - c != obj->second.found_cells.end(); - ++c) { - if (c->second.pci == pci) { - if (cell_idx) { - *cell_idx = c->first; - return true; - } - } - } - // return true if cell idx not found but frequency is found - if (cell_idx) { - *cell_idx = -1; - } - return true; - } - } - return false; -} - -/* Generate report procedure 5.5.5 */ -void rrc::rrc_meas::generate_report(uint32_t meas_id) -{ - asn1::rrc::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; - - meas_t* m = &active[meas_id]; - report_cfg_t* cfg = &reports_cfg[m->report_id]; - - report->meas_id = (uint8_t)meas_id; - report->meas_result_pcell.rsrp_result = value_to_range(RSRP, pcell_measurement.ms[RSRP]); - report->meas_result_pcell.rsrq_result = value_to_range(RSRQ, pcell_measurement.ms[RSRQ]); - - log_h->info("MEAS: Generate report MeasId=%d, nof_reports_send=%d, Pcell rsrp=%f rsrq=%f\n", - report->meas_id, - m->nof_reports_sent, - pcell_measurement.ms[RSRP], - pcell_measurement.ms[RSRQ]); - - meas_result_list_eutra_l& neigh_list = report->meas_result_neigh_cells.set_meas_result_list_eutra(); - // TODO: report up to 8 best cells - for (const auto& cell : m->cell_values) { - if (cell.second.triggered and neigh_list.size() <= 8) { - meas_result_eutra_s rc; - - rc.pci = (uint16_t)cell.first; - rc.meas_result.rsrp_result_present = cfg->report_quantity == RSRP || cfg->report_quantity == BOTH; - rc.meas_result.rsrq_result_present = cfg->report_quantity == RSRQ || cfg->report_quantity == BOTH; - rc.meas_result.rsrp_result = value_to_range(RSRP, cell.second.ms[RSRP]); - rc.meas_result.rsrq_result = value_to_range(RSRQ, cell.second.ms[RSRQ]); - - log_h->info("MEAS: Adding to report neighbour=%d, pci=%d, rsrp=%f, rsrq=%f\n", - neigh_list.size(), - rc.pci, - cell.second.ms[RSRP], - cell.second.ms[RSRQ]); - - neigh_list.push_back(rc); - } - } - report->meas_result_neigh_cells_present = neigh_list.size() > 0; - - m->nof_reports_sent++; - m->periodic_timer.stop(); - - // re-arm timer if needed (also includes case where amount is infinity) - if (m->nof_reports_sent < static_cast(cfg->amount)) { - m->periodic_timer.run(); - } else { - if (cfg->trigger_type == report_cfg_t::PERIODIC) { - m->triggered = false; - } - } - - // Send to lower layers - parent->send_ul_dcch_msg(RB_ID_SRB1, ul_dcch_msg); -} - -/* Handle entering/leaving event conditions 5.5.4.1 */ -bool rrc::rrc_meas::process_event(eutra_event_s* event, - uint32_t tti, - bool enter_condition, - bool exit_condition, - meas_t* m, - meas_value_t* cell) -{ - bool generate_report = false; - if (enter_condition && (!m->triggered || !cell->triggered)) { - if (!cell->timer_enter_triggered) { - cell->timer_enter_triggered = true; - cell->enter_tti = tti; - } else if (srslte_tti_interval(tti, cell->enter_tti) >= event->time_to_trigger.to_number()) { - m->triggered = true; - cell->triggered = true; - m->nof_reports_sent = 0; - generate_report = true; - } - } else if (exit_condition) { - if (!cell->timer_exit_triggered) { - cell->timer_exit_triggered = true; - cell->exit_tti = tti; - } else if (srslte_tti_interval(tti, cell->exit_tti) >= event->time_to_trigger.to_number()) { - m->triggered = false; - cell->triggered = false; - m->periodic_timer.stop(); - if (event) { - if (event->event_id.type() == eutra_event_s::event_id_c_::types::event_a3 && - event->event_id.event_a3().report_on_leave) { - generate_report = true; - } - } - } - } - if (!enter_condition) { - cell->timer_enter_triggered = false; - } - if (!exit_condition) { - cell->timer_exit_triggered = false; - } - return generate_report; -} - -/* Calculate trigger conditions for each cell 5.5.4 */ -void rrc::rrc_meas::calculate_triggers(uint32_t tti) -{ - float Ofp = 0, Ocp = 0; - meas_obj_t* serving_object = NULL; - int serving_cell_idx = 0; - - // Get serving cell - if (active.size()) { - if (find_earfcn_cell(phy->get_current_earfcn(), phy->get_current_pci(), &serving_object, &serving_cell_idx)) { - Ofp = serving_object->q_offset; - if (serving_cell_idx >= 0) { - Ocp = serving_object->found_cells[serving_cell_idx].q_offset; - } - } else { - log_h->warning("Can't find current earfcn=%d, pci=%d in objects list. Using Ofp=0, Ocp=0\n", - phy->get_current_earfcn(), - phy->get_current_pci()); - } - } - - for (std::map::iterator m = active.begin(); m != active.end(); ++m) { - // make sure report config exists - if (reports_cfg.find(m->second.report_id) == reports_cfg.end()) { - log_h->error("Error in measurement id=%d, report id=%d is not configured.\n", m->first, m->second.report_id); - break; - } - report_cfg_t* cfg = &reports_cfg.at(m->second.report_id); - double hyst = 0.5 * cfg->event.hysteresis; - float Mp = pcell_measurement.ms[cfg->trigger_quantity]; - - bool gen_report = false; - - if (cfg->trigger_type == report_cfg_t::EVENT) { - // A1 & A2 are for serving cell only - if (cfg->event.event_id.type().value < eutra_event_s::event_id_c_::types::event_a3) { - bool enter_condition; - bool exit_condition; - if (cfg->event.event_id.type() == eutra_event_s::event_id_c_::types::event_a1) { - uint8_t range; - if (cfg->event.event_id.event_a1().a1_thres.type().value == thres_eutra_c::types::thres_rsrp) { - range = cfg->event.event_id.event_a1().a1_thres.thres_rsrp(); - } else { - range = cfg->event.event_id.event_a1().a1_thres.thres_rsrq(); - } - enter_condition = Mp - hyst > range_to_value(cfg->trigger_quantity, range); - exit_condition = Mp + hyst < range_to_value(cfg->trigger_quantity, range); - } else { - uint8_t range; - if (cfg->event.event_id.event_a2().a2_thres.type() == thres_eutra_c::types::thres_rsrp) { - range = cfg->event.event_id.event_a2().a2_thres.thres_rsrp(); - } else { - range = cfg->event.event_id.event_a2().a2_thres.thres_rsrq(); - } - enter_condition = Mp + hyst < range_to_value(cfg->trigger_quantity, range); - exit_condition = Mp - hyst > range_to_value(cfg->trigger_quantity, range); - } - - // check only if - gen_report |= process_event(&cfg->event, tti, enter_condition, exit_condition, &m->second, &pcell_measurement); - - if (gen_report) { - log_h->info("Triggered by A1/A2 event\n"); - } - // Rest are evaluated for every cell in frequency - } else { - meas_obj_t* obj = &objects[m->second.object_id]; - for (std::map::iterator cell = obj->found_cells.begin(); cell != obj->found_cells.end(); - ++cell) { - if (m->second.cell_values.count(cell->second.pci)) { - float Ofn = obj->q_offset; - float Ocn = cell->second.q_offset; - float Mn = m->second.cell_values[cell->second.pci].ms[cfg->trigger_quantity]; - double Off = 0; - float th = 0, th1 = 0, th2 = 0; - bool enter_condition = false; - bool exit_condition = false; - uint8_t range, range2; - switch (cfg->event.event_id.type().value) { - case eutra_event_s::event_id_c_::types::event_a3: - Off = 0.5 * cfg->event.event_id.event_a3().a3_offset; - enter_condition = Mn + Ofn + Ocn - hyst > Mp + Ofp + Ocp + Off; - exit_condition = Mn + Ofn + Ocn + hyst < Mp + Ofp + Ocp + Off; - break; - case eutra_event_s::event_id_c_::types::event_a4: - if (cfg->event.event_id.event_a4().a4_thres.type() == thres_eutra_c::types::thres_rsrp) { - range = cfg->event.event_id.event_a4().a4_thres.thres_rsrp(); - } else { - range = cfg->event.event_id.event_a4().a4_thres.thres_rsrq(); - } - th = range_to_value(cfg->trigger_quantity, range); - enter_condition = Mn + Ofn + Ocn - hyst > th; - exit_condition = Mn + Ofn + Ocn + hyst < th; - break; - case eutra_event_s::event_id_c_::types::event_a5: - if (cfg->event.event_id.event_a5().a5_thres1.type() == thres_eutra_c::types::thres_rsrp) { - range = cfg->event.event_id.event_a5().a5_thres1.thres_rsrp(); - } else { - range = cfg->event.event_id.event_a5().a5_thres1.thres_rsrq(); - } - if (cfg->event.event_id.event_a5().a5_thres2.type() == thres_eutra_c::types::thres_rsrp) { - range2 = cfg->event.event_id.event_a5().a5_thres2.thres_rsrp(); - } else { - range2 = cfg->event.event_id.event_a5().a5_thres2.thres_rsrq(); - } - th1 = range_to_value(cfg->trigger_quantity, range); - th2 = range_to_value(cfg->trigger_quantity, range2); - enter_condition = (Mp + hyst < th1) && (Mn + Ofn + Ocn - hyst > th2); - exit_condition = (Mp - hyst > th1) && (Mn + Ofn + Ocn + hyst < th2); - break; - default: - log_h->error("Error event %s not implemented\n", cfg->event.event_id.type().to_string().c_str()); - } - gen_report |= process_event(&cfg->event, - tti, - enter_condition, - exit_condition, - &m->second, - &m->second.cell_values[cell->second.pci]); - } - } - } - } - if (gen_report) { - log_h->info("Generate report MeasId=%d, from event\n", m->first); - generate_report(m->first); - } - } -} - -// Procedure upon handover or reestablishment 5.5.6.1 -void rrc::rrc_meas::ho_finish() -{ - // Remove all measId with trigger periodic - std::map::iterator iter = active.begin(); - while (iter != active.end()) { - if (reports_cfg[iter->second.report_id].trigger_type == report_cfg_t::PERIODIC) { - remove_meas_id(iter++); - } else { - ++iter; - } - } - - // TODO: Inter-frequency handover - - // Stop all reports - for (auto& item : active) { - stop_reports(&item.second); - } -} - -// 5.5.4.1 expiry of periodical reporting timer -bool rrc::rrc_meas::timer_expired(uint32_t timer_id) -{ - for (std::map::iterator iter = active.begin(); iter != active.end(); ++iter) { - if (iter->second.periodic_timer.id() == timer_id) { - log_h->info("Periodic report timer expired for MeasId=%d (timer_id=%d)\n", iter->first, timer_id); - generate_report(iter->first); - return true; - } - } - return false; -} - -void rrc::rrc_meas::stop_reports(meas_t* m) -{ - m->periodic_timer.stop(); - m->triggered = false; -} - -void rrc::rrc_meas::stop_reports_object(uint32_t object_id) -{ - for (std::map::iterator iter = active.begin(); iter != active.end(); ++iter) { - if (iter->second.object_id == object_id) { - stop_reports(&iter->second); - } - } -} - -void rrc::rrc_meas::remove_meas_object(uint32_t object_id) -{ - std::map::iterator iter = active.begin(); - while (iter != active.end()) { - if (iter->second.object_id == object_id) { - remove_meas_id(iter++); - } else { - ++iter; - } - } -} - -void rrc::rrc_meas::remove_meas_report(uint32_t report_id) -{ - std::map::iterator iter = active.begin(); - while (iter != active.end()) { - if (iter->second.report_id == report_id) { - remove_meas_id(iter++); - } else { - ++iter; - } - } -} - -void rrc::rrc_meas::remove_meas_id(uint32_t measId) -{ - if (active.count(measId)) { - log_h->info("MEAS: Removed measId=%d\n", measId); - active.erase(measId); - } else { - log_h->warning("MEAS: Removing unexistent measId=%d\n", measId); - } -} - -void rrc::rrc_meas::remove_meas_id(std::map::iterator it) -{ - log_h->info("MEAS: Removed measId=%d\n", it->first); - active.erase(it); -} - -/* Parses MeasConfig object from RRCConnectionReconfiguration message and applies configuration - * as per section 5.5.2 - */ -bool rrc::rrc_meas::parse_meas_config(meas_cfg_s* cfg) -{ - // Measurement object removal 5.5.2.4 - for (uint32_t i = 0; i < cfg->meas_obj_to_rem_list.size(); i++) { - objects.erase(cfg->meas_obj_to_rem_list[i]); - remove_meas_object(cfg->meas_obj_to_rem_list[i]); - log_h->info("MEAS: Removed measObjectId=%d\n", cfg->meas_obj_to_rem_list[i]); - } - - // Measurement object addition/modification Section 5.5.2.5 - if (cfg->meas_obj_to_add_mod_list_present) { - for (uint32_t i = 0; i < cfg->meas_obj_to_add_mod_list.size(); i++) { - if (cfg->meas_obj_to_add_mod_list[i].meas_obj.type() == - meas_obj_to_add_mod_s::meas_obj_c_::types::meas_obj_eutra) { - meas_obj_eutra_s* src_obj = &cfg->meas_obj_to_add_mod_list[i].meas_obj.meas_obj_eutra(); - - // Access the object if exists or create it - meas_obj_t* dst_obj = &objects[cfg->meas_obj_to_add_mod_list[i].meas_obj_id]; - - dst_obj->earfcn = src_obj->carrier_freq; - ; - if (src_obj->offset_freq_present) { - dst_obj->q_offset = src_obj->offset_freq.to_number(); - } else { - dst_obj->q_offset = 0; - } - - if (src_obj->black_cells_to_rem_list_present) { - for (uint32_t j = 0; j < src_obj->black_cells_to_rem_list.size(); j++) { - dst_obj->meas_cells.erase(src_obj->black_cells_to_rem_list[i]); - } - } - - for (uint32_t j = 0; j < src_obj->cells_to_add_mod_list.size(); j++) { - dst_obj->meas_cells[src_obj->cells_to_add_mod_list[j].cell_idx].q_offset = - src_obj->cells_to_add_mod_list[j].cell_individual_offset.to_number(); - dst_obj->meas_cells[src_obj->cells_to_add_mod_list[j].cell_idx].pci = src_obj->cells_to_add_mod_list[j].pci; - - log_h->info("MEAS: Added measObjectId=%d, earfcn=%d, q_offset=%f, pci=%d, offset_cell=%f\n", - cfg->meas_obj_to_add_mod_list[i].meas_obj_id, - dst_obj->earfcn, - dst_obj->q_offset, - dst_obj->meas_cells[src_obj->cells_to_add_mod_list[j].cell_idx].pci, - dst_obj->meas_cells[src_obj->cells_to_add_mod_list[j].cell_idx].q_offset); - } - - // Untrigger reports and stop timers - stop_reports_object(cfg->meas_obj_to_add_mod_list[i].meas_obj_id); - - // TODO: Blackcells - // TODO: meassubframepattern - - } else { - log_h->warning("MEAS: Unsupported MeasObject type %s\n", - cfg->meas_obj_to_add_mod_list[i].meas_obj.type().to_string().c_str()); - } - } - } - - // Reporting configuration removal 5.5.2.6 - for (uint32_t i = 0; i < cfg->report_cfg_to_rem_list.size(); i++) { - reports_cfg.erase(cfg->report_cfg_to_rem_list[i]); - remove_meas_report(cfg->report_cfg_to_rem_list[i]); - log_h->info("MEAS: Removed reportConfigId=%d\n", cfg->report_cfg_to_rem_list[i]); - } - - // Reporting configuration addition/modification 5.5.2.7 - if (cfg->report_cfg_to_add_mod_list_present) { - for (uint32_t i = 0; i < cfg->report_cfg_to_add_mod_list.size(); i++) { - if (cfg->report_cfg_to_add_mod_list[i].report_cfg.type() == - report_cfg_to_add_mod_s::report_cfg_c_::types::report_cfg_eutra) { - report_cfg_eutra_s* src_rep = &cfg->report_cfg_to_add_mod_list[i].report_cfg.report_cfg_eutra(); - // Access the object if exists or create it - report_cfg_t* dst_rep = &reports_cfg[cfg->report_cfg_to_add_mod_list[i].report_cfg_id]; - - dst_rep->trigger_type = src_rep->trigger_type.type() == report_cfg_eutra_s::trigger_type_c_::types::event - ? report_cfg_t::EVENT - : report_cfg_t::PERIODIC; - if (dst_rep->trigger_type == report_cfg_t::EVENT) { - dst_rep->event = src_rep->trigger_type.event(); - } - dst_rep->amount = src_rep->report_amount.to_number(); - dst_rep->interval = src_rep->report_interv.to_number(); - dst_rep->max_cell = src_rep->max_report_cells; - dst_rep->trigger_quantity = (quantity_t)src_rep->trigger_quant.value; - dst_rep->report_quantity = src_rep->report_quant == report_cfg_eutra_s::report_quant_e_::same_as_trigger_quant - ? dst_rep->trigger_quantity - : BOTH; - - if (dst_rep->trigger_type == report_cfg_t::EVENT) { - log_h->info("MEAS: Added reportConfigId=%d, event=%s, amount=%d, interval=%d\n", - cfg->report_cfg_to_add_mod_list[i].report_cfg_id, - dst_rep->event.event_id.type().to_string().c_str(), - dst_rep->amount, - dst_rep->interval); - } else { - log_h->info("MEAS: Added reportConfigId=%d, type=periodical, amount=%d, interval=%d\n", - cfg->report_cfg_to_add_mod_list[i].report_cfg_id, - dst_rep->amount, - dst_rep->interval); - } - - // Reset reports counter - for (std::map::iterator iter = active.begin(); iter != active.end(); ++iter) { - if (iter->second.report_id == cfg->report_cfg_to_add_mod_list[i].report_cfg_id) { - iter->second.nof_reports_sent = 0; - stop_reports(&iter->second); - } - } - } else { - log_h->warning("MEAS: Unsupported reportConfigType %s\n", - cfg->report_cfg_to_add_mod_list[i].report_cfg.type().to_string().c_str()); - } - } - } - - // Quantity configuration 5.5.2.8 - if (cfg->quant_cfg_present && cfg->quant_cfg.quant_cfg_eutra_present) { - if (cfg->quant_cfg.quant_cfg_eutra.filt_coef_rsrp_present) { - filter_k_rsrp = cfg->quant_cfg.quant_cfg_eutra.filt_coef_rsrp.to_number(); - } else { - filter_k_rsrp = filt_coef_e(filt_coef_e::fc4).to_number(); - } - if (cfg->quant_cfg.quant_cfg_eutra.filt_coef_rsrq_present) { - filter_k_rsrq = cfg->quant_cfg.quant_cfg_eutra.filt_coef_rsrq.to_number(); - } else { - filter_k_rsrq = filt_coef_e(filt_coef_e::fc4).to_number(); - } - filter_a[RSRP] = powf(0.5, (float)filter_k_rsrp / 4); - filter_a[RSRQ] = powf(0.5, (float)filter_k_rsrq / 4); - - log_h->info("MEAS: Quantity configuration k_rsrp=%d, k_rsrq=%d\n", filter_k_rsrp, filter_k_rsrq); - } - - // Measurement identity removal 5.5.2.2 - for (uint32_t i = 0; i < cfg->meas_id_to_rem_list.size(); i++) { - remove_meas_id(cfg->meas_id_to_rem_list[i]); - } - - log_h->info("nof active measId=%zd\n", active.size()); - - // Measurement identity addition/modification 5.5.2.3 - if (cfg->meas_id_to_add_mod_list_present) { - for (uint32_t i = 0; i < cfg->meas_id_to_add_mod_list.size(); i++) { - meas_id_to_add_mod_s* meas_id = &cfg->meas_id_to_add_mod_list[i]; - // Stop the timer if the entry exists or create the timer if not - bool is_new = false; - if (active.count(meas_id->meas_id)) { - active[meas_id->meas_id].periodic_timer.stop(); - } else { - is_new = true; - active[meas_id->meas_id].periodic_timer = timers->get_unique_timer(); - } - active[meas_id->meas_id].object_id = meas_id->meas_obj_id; - active[meas_id->meas_id].report_id = meas_id->report_cfg_id; - active[meas_id->meas_id].periodic_timer.set(reports_cfg[meas_id->report_cfg_id].interval, - [this](uint32_t tid) { timer_expired(tid); }); - - log_h->info("MEAS: %s measId=%d, measObjectId=%d, reportConfigId=%d, timer_id=%u, nof_values=%zd\n", - is_new ? "Added" : "Updated", - meas_id->meas_id, - meas_id->meas_obj_id, - meas_id->report_cfg_id, - active[meas_id->meas_id].periodic_timer.id(), - active[meas_id->meas_id].cell_values.size()); - } - } - - // S-Measure - if (cfg->s_measure_present) { - if (cfg->s_measure) { - s_measure_enabled = true; - s_measure_value = range_to_value(RSRP, cfg->s_measure); - } else { - s_measure_enabled = false; - } - } - - update_phy(); - - return true; -} - -/* Instruct PHY to start measurement */ -void rrc::rrc_meas::update_phy() -{ - phy->meas_reset(); - for (const auto& obj : objects) { - meas_obj_t o = obj.second; - // Instruct PHY to look for neighbour cells on this frequency - phy->meas_start(o.earfcn); - for (const auto& cell : o.meas_cells) { - // Instruct PHY to look for cells IDs on this frequency - phy->meas_start(o.earfcn, cell.second.pci); - } - } -} - -uint8_t rrc::rrc_meas::value_to_range(quantity_t quant, float value) -{ - uint8_t range = 0; - switch (quant) { - case RSRP: - if (value < -140) { - range = 0; - } else if (value < -44) { - range = 1u + (uint8_t)(value + 140); - } else { - range = 97; - } - break; - case RSRQ: - if (value < -19.5) { - range = 0; - } else if (value < -3) { - range = 1u + (uint8_t)(2 * (value + 19.5)); - } else { - range = 34; - } - break; - case BOTH: - log_h->error("Error quantity both not supported in value_to_range\n"); - break; - } - return range; -} - -float rrc::rrc_meas::range_to_value(quantity_t quant, uint8_t range) -{ - float val = 0; - switch (quant) { - case RSRP: - val = -140 + (float)range; - break; - case RSRQ: - val = -19.5f + (float)range / 2; - break; - case BOTH: - log_h->error("Error quantity both not supported in range_to_value\n"); - break; - } - return val; -} - const std::string rrc::rb_id_str[] = {"SRB0", "SRB1", "SRB2", "DRB1", "DRB2", "DRB3", "DRB4", "DRB5", "DRB6", "DRB7", "DRB8"}; diff --git a/srsue/src/stack/rrc/rrc_meas.cc b/srsue/src/stack/rrc/rrc_meas.cc new file mode 100644 index 000000000..b526133a5 --- /dev/null +++ b/srsue/src/stack/rrc/rrc_meas.cc @@ -0,0 +1,1138 @@ +/* + * 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 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 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 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 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::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& 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 rrc::rrc_meas::var_meas_cfg::get_active_objects() +{ + std::list 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& c) { + return c.second.carrier_freq == src_earfcn; + }); + auto dst_obj = std::find_if( + measObjectsList.begin(), measObjectsList.end(), [&dst_earfcn](const std::pair& 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& 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 diff --git a/srsue/src/stack/rrc/rrc_procedures.cc b/srsue/src/stack/rrc/rrc_procedures.cc index 47f741461..35cace5a5 100644 --- a/srsue/src/stack/rrc/rrc_procedures.cc +++ b/srsue/src/stack/rrc/rrc_procedures.cc @@ -72,12 +72,12 @@ proc_outcome_t rrc::cell_search_proc::handle_cell_found(const phy_interface_rrc_ Info("Cell found in this frequency. Setting new serving cell...\n"); // Create a cell with NaN RSRP. Will be updated by new_phy_meas() during SIB search. - if (not rrc_ptr->add_neighbour_cell(new_cell, NAN)) { + if (not rrc_ptr->add_neighbour_cell(unique_cell_t(new cell_t(new_cell)))) { Info("No more space for neighbour cells\n"); return proc_outcome_t::success; } - rrc_ptr->set_serving_cell(new_cell); + rrc_ptr->set_serving_cell(new_cell, false); if (not rrc_ptr->phy->cell_is_camping()) { Warning("Could not camp on found cell.\n"); @@ -340,11 +340,14 @@ proc_outcome_t rrc::cell_selection_proc::init() neigh_index = 0; cs_result = cs_result_t::no_cell; state = search_state_t::cell_selection; + discard_serving = false; return step(); } proc_outcome_t rrc::cell_selection_proc::step_cell_selection() { + Info("Current serving cell: %s\n", rrc_ptr->serving_cell->print().c_str()); + // Neighbour cells are sorted in descending order of RSRP for (; neigh_index < rrc_ptr->neighbour_cells.size(); ++neigh_index) { /*TODO: CHECK that PLMN matches. Currently we don't receive SIB1 of neighbour cells @@ -356,23 +359,25 @@ proc_outcome_t rrc::cell_selection_proc::step_cell_selection() (rrc_ptr->cell_selection_criteria(rsrp) and rsrp > rrc_ptr->serving_cell->get_rsrp() + 5)) { // currently connected and verifies cell selection criteria // Try to select Cell - rrc_ptr->set_serving_cell(rrc_ptr->neighbour_cells.at(neigh_index)->phy_cell); + rrc_ptr->set_serving_cell(rrc_ptr->neighbour_cells.at(neigh_index)->phy_cell, discard_serving); + discard_serving = false; Info("Selected cell: %s\n", rrc_ptr->serving_cell->print().c_str()); - rrc_ptr->rrc_log->console("Selected cell: %s\n", rrc_ptr->serving_cell->print().c_str()); /* BLOCKING CALL */ if (rrc_ptr->phy->cell_select(&rrc_ptr->serving_cell->phy_cell)) { Info("Wait PHY to be in-synch\n"); state = search_state_t::wait_in_sync; + rrc_ptr->phy_sync_state = phy_unknown_sync; return step(); } else { rrc_ptr->phy_sync_state = phy_unknown_sync; Error("Could not camp on serving cell.\n"); + discard_serving = true; // Continue to next neighbour cell } } } - if (rrc_ptr->phy_sync_state == phy_in_sync) { + if (rrc_ptr->phy_sync_state == phy_in_sync && rrc_ptr->cell_selection_criteria(rrc_ptr->serving_cell->get_rsrp())) { if (not rrc_ptr->phy->cell_is_camping()) { Info("Serving cell %s is in-sync but not camping. Selecting it...\n", rrc_ptr->serving_cell->print().c_str()); @@ -381,6 +386,7 @@ proc_outcome_t rrc::cell_selection_proc::step_cell_selection() Info("Selected serving cell OK.\n"); } else { rrc_ptr->phy_sync_state = phy_unknown_sync; + rrc_ptr->serving_cell->set_rsrp(-INFINITY); Error("Could not camp on serving cell.\n"); return proc_outcome_t::error; } @@ -401,11 +407,20 @@ proc_outcome_t rrc::cell_selection_proc::step_cell_selection() proc_outcome_t rrc::cell_selection_proc::step_wait_in_sync() { if (rrc_ptr->phy_sync_state == phy_in_sync) { - Info("PHY is in SYNC\n"); - if (not rrc_ptr->serv_cell_cfg.launch(&serv_cell_cfg_fut, rrc_ptr->ue_required_sibs)) { - return proc_outcome_t::error; + if (rrc_ptr->cell_selection_criteria(rrc_ptr->serving_cell->get_rsrp())) { + Info("PHY is in SYNC and cell selection passed\n"); + serv_cell_cfg_fut = rrc_ptr->serv_cell_cfg.get_future(); + if (not rrc_ptr->serv_cell_cfg.launch(&serv_cell_cfg_fut, rrc_ptr->ue_required_sibs)) { + return proc_outcome_t::error; + } + state = search_state_t::cell_config; + } else { + Info("PHY is in SYNC but cell selection did not pass. Go back to select step.\n"); + neigh_index = 0; + cs_result = cs_result_t::no_cell; + state = search_state_t::cell_selection; + discard_serving = true; // Discard this cell } - state = search_state_t::cell_config; } return proc_outcome_t::yield; } @@ -432,6 +447,7 @@ proc_outcome_t rrc::cell_selection_proc::step_cell_config() return proc_outcome_t::yield; } if (serv_cell_cfg_fut.is_success()) { + rrc_ptr->rrc_log->console("Selected cell: %s\n", rrc_ptr->serving_cell->print().c_str()); Info("All SIBs of serving cell obtained successfully\n"); cs_result = cs_result_t::changed_cell; return proc_outcome_t::success; @@ -476,7 +492,8 @@ rrc::plmn_search_proc::plmn_search_proc(rrc* parent_) : rrc_ptr(parent_), log_h( proc_outcome_t rrc::plmn_search_proc::init() { Info("Starting PLMN search\n"); - nof_plmns = 0; + nof_plmns = 0; + cell_search_fut = rrc_ptr->cell_searcher.get_future(); if (not rrc_ptr->cell_searcher.launch(&cell_search_fut)) { Error("Failed due to fail to init cell search...\n"); return proc_outcome_t::error; @@ -804,7 +821,7 @@ proc_outcome_t rrc::go_idle_proc::init() { rlc_flush_counter = 0; Info("Starting...\n"); - return proc_outcome_t::yield; + return step(); } proc_outcome_t rrc::go_idle_proc::step() @@ -814,8 +831,10 @@ proc_outcome_t rrc::go_idle_proc::step() return proc_outcome_t::success; } + // If the RLC SRB1 is not suspended // wait for max. 2s for RLC on SRB1 to be flushed - if (not rrc_ptr->rlc->has_data(RB_ID_SRB1) || ++rlc_flush_counter > rlc_flush_timeout) { + if (rrc_ptr->rlc->is_suspended(RB_ID_SRB1) || not rrc_ptr->rlc->has_data(RB_ID_SRB1) || + ++rlc_flush_counter > rlc_flush_timeout) { rrc_ptr->leave_connected(); return proc_outcome_t::success; } else { @@ -854,8 +873,7 @@ proc_outcome_t rrc::cell_reselection_proc::step() Info("Cell Selection completed. Handling its result...\n"); switch (*cell_selection_fut.value()) { case cs_result_t::changed_cell: - Info("New cell has been selected, start receiving PCCH\n"); - rrc_ptr->mac->pcch_start_rx(); + Info("New cell has been selected\n"); break; case cs_result_t::no_cell: Warning("Could not find any cell to camp on\n"); @@ -879,7 +897,10 @@ rrc::connection_reest_proc::connection_reest_proc(srsue::rrc* rrc_) : rrc_ptr(rr proc_outcome_t rrc::connection_reest_proc::init(asn1::rrc::reest_cause_e cause) { - Info("Starting...\n"); + Info("Starting... Cause: %s\n", + cause == asn1::rrc::reest_cause_opts::recfg_fail + ? "Reconfiguration failure" + : cause == asn1::rrc::reest_cause_opts::ho_fail ? "Handover failure" : "Other failure"); // Save Current RNTI before MAC Reset mac_interface_rrc::ue_rnti_t uernti; @@ -891,6 +912,7 @@ proc_outcome_t rrc::connection_reest_proc::init(asn1::rrc::reest_cause_e cause) reest_rnti = uernti.crnti; reest_cause = cause; reest_source_pci = rrc_ptr->serving_cell->get_pci(); // needed for reestablishment with another cell + reest_source_freq = rrc_ptr->serving_cell->get_earfcn(); // the initiation of reestablishment procedure as indicates in 3GPP 36.331 Section 5.3.7.2 // Cannot be called from here because it has PHY-MAC re-configuration that should be performed in a different thread @@ -947,9 +969,10 @@ srslte::proc_outcome_t rrc::connection_reest_proc::step_cell_reselection() // Run cell reselection if (not rrc_ptr->cell_reselector.run()) { // Check T311 - if (!rrc_ptr->t311.is_running()) { + if (not rrc_ptr->t311.is_running()) { // Abort procedure if T311 expires - Info("T311 expired during cell reselection. Aborting.\n"); + Info("T311 expired during cell reselection. Going to IDLE.\n"); + rrc_ptr->start_go_idle(); return proc_outcome_t::success; } @@ -964,7 +987,7 @@ srslte::proc_outcome_t rrc::connection_reest_proc::step_cell_reselection() rrc_ptr->serving_cell->has_sib1(), rrc_ptr->serving_cell->has_sib2(), rrc_ptr->serving_cell->has_sib3()); - std::vector required_sibs = {1, 2, 3}; + std::vector required_sibs = {0, 1, 2}; if (!rrc_ptr->serv_cell_cfg.launch(required_sibs)) { Error("Failed to initiate configure serving cell\n"); return proc_outcome_t::error; @@ -991,7 +1014,8 @@ proc_outcome_t rrc::connection_reest_proc::step_cell_configuration() // Check T311 if (!rrc_ptr->t311.is_running()) { // Abort procedure if T311 expires - Info("T311 expired during cell configuration. Aborting.\n"); + Info("T311 expired during cell configuration. Going to IDLE.\n"); + rrc_ptr->start_go_idle(); return proc_outcome_t::success; } @@ -1040,10 +1064,17 @@ srslte::proc_outcome_t rrc::connection_reest_proc::cell_criteria() // initiate transmission of the RRCConnectionReestablishmentRequest message in accordance with 5.3.7.4; rrc_ptr->send_con_restablish_request(reest_cause, reest_rnti, reest_source_pci); - } else { + } else if (rrc_ptr->t311.is_running()) { // Upon selecting an inter-RAT cell Info("Reestablishment Cell Selection criteria failed.\n"); - rrc_ptr->leave_connected(); + + // Launch cell reselection + if (not rrc_ptr->cell_reselector.launch()) { + Error("Failed to initiate a Cell re-selection procedure...\n"); + return proc_outcome_t::error; + } + state = state_t::cell_reselection; + return proc_outcome_t::yield; } return proc_outcome_t::success; } diff --git a/srsue/test/phy/scell_search_test.cc b/srsue/test/phy/scell_search_test.cc index 5106d97e8..0963aa212 100644 --- a/srsue/test/phy/scell_search_test.cc +++ b/srsue/test/phy/scell_search_test.cc @@ -240,25 +240,28 @@ public: void in_sync() override {} void out_of_sync() override {} - void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn, int pci) override + void new_cell_meas(std::vector& meas) override { - if (!cells.count(pci)) { - cells[pci].rsrp_min = rsrp; - cells[pci].rsrp_max = rsrp; - cells[pci].rsrp_avg = rsrp; - cells[pci].rsrq_min = rsrq; - cells[pci].rsrq_max = rsrq; - cells[pci].rsrq_avg = rsrq; - cells[pci].count = 1; - } else { - cells[pci].rsrp_min = SRSLTE_MIN(cells[pci].rsrp_min, rsrp); - cells[pci].rsrp_max = SRSLTE_MAX(cells[pci].rsrp_max, rsrp); - cells[pci].rsrp_avg = (rsrp + cells[pci].rsrp_avg * cells[pci].count) / (cells[pci].count + 1); - - cells[pci].rsrq_min = SRSLTE_MIN(cells[pci].rsrq_min, rsrq); - cells[pci].rsrq_max = SRSLTE_MAX(cells[pci].rsrq_max, rsrq); - cells[pci].rsrq_avg = (rsrq + cells[pci].rsrq_avg * cells[pci].count) / (cells[pci].count + 1); - cells[pci].count++; + for (auto& m : meas) { + uint32_t pci = m.pci; + if (!cells.count(pci)) { + cells[pci].rsrp_min = m.rsrp; + cells[pci].rsrp_max = m.rsrp; + cells[pci].rsrp_avg = m.rsrp; + cells[pci].rsrq_min = m.rsrq; + cells[pci].rsrq_max = m.rsrq; + cells[pci].rsrq_avg = m.rsrq; + cells[pci].count = 1; + } else { + cells[pci].rsrp_min = SRSLTE_MIN(cells[pci].rsrp_min, m.rsrp); + cells[pci].rsrp_max = SRSLTE_MAX(cells[pci].rsrp_max, m.rsrp); + cells[pci].rsrp_avg = (m.rsrp + cells[pci].rsrp_avg * cells[pci].count) / (cells[pci].count + 1); + + cells[pci].rsrq_min = SRSLTE_MIN(cells[pci].rsrq_min, m.rsrq); + cells[pci].rsrq_max = SRSLTE_MAX(cells[pci].rsrq_max, m.rsrq); + cells[pci].rsrq_avg = (m.rsrq + cells[pci].rsrq_avg * cells[pci].count) / (cells[pci].count + 1); + cells[pci].count++; + } } } @@ -393,7 +396,6 @@ int main(int argc, char** argv) phy_args.estimator_fil_auto = false; phy_args.estimator_fil_order = 4; phy_args.estimator_fil_stddev = 1.0f; - phy_args.sic_pss_enabled = false; phy_args.interpolate_subframe_enabled = false; phy_args.nof_rx_ant = 1; phy_args.cfo_is_doppler = true; @@ -450,6 +452,8 @@ int main(int argc, char** argv) intra_measure.init(&common, &rrc, &logger); intra_measure.set_primary_cell(serving_cell_id, cell_base); + std::set pcis_to_meas = {}; + if (earfcn_dl >= 0) { // Create radio log radio_log = std::unique_ptr(new srslte::log_filter("Radio")); @@ -490,7 +494,7 @@ int main(int argc, char** argv) // Add cell to known cells if (cell_list.empty()) { - intra_measure.add_cell(cell.id); + pcis_to_meas.insert(cell.id); } } } @@ -499,7 +503,7 @@ int main(int argc, char** argv) if (cell_list == "all") { // Add all possible cells for (int i = 0; i < 504; i++) { - intra_measure.add_cell(i); + pcis_to_meas.insert(i); } } else if (cell_list == "none") { // Do nothing @@ -516,10 +520,13 @@ int main(int argc, char** argv) while (ss.good()) { std::string substr; getline(ss, substr, ','); - intra_measure.add_cell((uint32_t)strtoul(substr.c_str(), nullptr, 10)); + pcis_to_meas.insert((uint32_t)strtoul(substr.c_str(), nullptr, 10)); } } + // pass cells to measure to intra_measure object + intra_measure.set_cells_to_meas(pcis_to_meas); + // Run loop for (uint32_t sf_idx = 0; sf_idx < duration_execution_s * 1000; sf_idx++) { srslte_dl_sf_cfg_t sf_cfg_dl = {}; diff --git a/srsue/test/phy/ue_phy_test.cc b/srsue/test/phy/ue_phy_test.cc index 511491a1c..d5c3d3de1 100644 --- a/srsue/test/phy/ue_phy_test.cc +++ b/srsue/test/phy/ue_phy_test.cc @@ -85,10 +85,13 @@ private: void in_sync() override { notify_in_sync(); } void out_of_sync() override { notify_out_of_sync(); } - void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn, int pci) override + void new_cell_meas(std::vector& meas) override { - notify_new_phy_meas(); - log_h.info("New measurement earfcn=%d; pci=%d; rsrp=%+.1fdBm; rsrq=%+.1fdB;\n", earfcn, pci, rsrp, rsrq); + for (auto& m : meas) { + notify_new_phy_meas(); + log_h.info( + "New measurement earfcn=%d; pci=%d; rsrp=%+.1fdBm; rsrq=%+.1fdB;\n", m.earfcn, m.pci, m.rsrp, m.rsrq); + } } uint16_t get_dl_sched_rnti(uint32_t tti) override { return rnti; } uint16_t get_ul_sched_rnti(uint32_t tti) override { return rnti; } @@ -491,7 +494,7 @@ int main(int argc, char** argv) auto cell_search_res = phy_test->get_phy_interface_rrc()->cell_search(&phy_cell); TESTASSERT(cell_search_res.found == srsue::phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND); TESTASSERT(phy_test->get_stack()->wait_in_sync(default_timeout)); - TESTASSERT(memcmp(&phy_cell.cell, &cell, sizeof(srslte_cell_t)) == 0); + TESTASSERT(phy_cell.pci == cell.id); // 2. Cell select phy_test->get_phy_interface_rrc()->cell_select(&phy_cell); diff --git a/srsue/test/ttcn3/hdr/lte_ttcn3_phy.h b/srsue/test/ttcn3/hdr/lte_ttcn3_phy.h index dcbc57aed..3ea945462 100644 --- a/srsue/test/ttcn3/hdr/lte_ttcn3_phy.h +++ b/srsue/test/ttcn3/hdr/lte_ttcn3_phy.h @@ -78,7 +78,7 @@ public: void set_config_mbsfn_mcch(const srslte::mcch_msg_t& mcch){}; // Measurements interface - void meas_reset(); + void meas_stop(); int meas_start(uint32_t earfcn, int pci = -1); int meas_stop(uint32_t earfcn, int pci = -1); diff --git a/srsue/test/ttcn3/src/lte_ttcn3_phy.cc b/srsue/test/ttcn3/src/lte_ttcn3_phy.cc index 8cb620b6a..e7c2b328b 100644 --- a/srsue/test/ttcn3/src/lte_ttcn3_phy.cc +++ b/srsue/test/ttcn3/src/lte_ttcn3_phy.cc @@ -118,7 +118,7 @@ void lte_ttcn3_phy::set_config(srslte::phy_cfg_t& config, uint32_t cc_idx, uint3 } // Measurements interface -void lte_ttcn3_phy::meas_reset(){}; +void lte_ttcn3_phy::meas_stop(){}; int lte_ttcn3_phy::meas_start(uint32_t earfcn, int pci) { diff --git a/srsue/test/upper/CMakeLists.txt b/srsue/test/upper/CMakeLists.txt index b55f424cf..29aab2188 100644 --- a/srsue/test/upper/CMakeLists.txt +++ b/srsue/test/upper/CMakeLists.txt @@ -31,6 +31,10 @@ add_executable(rrc_reconfig_test rrc_reconfig_test.cc) target_link_libraries(rrc_reconfig_test srsue_upper srslte_upper srslte_phy rrc_asn1) add_test(rrc_reconfig_test rrc_reconfig_test) +add_executable(rrc_meas_test rrc_meas_test.cc) +target_link_libraries(rrc_meas_test srsue_rrc srsue_upper srslte_upper srslte_phy rrc_asn1) +add_test(rrc_meas_test rrc_meas_test) + add_executable(nas_test nas_test.cc) target_link_libraries(nas_test srsue_upper srslte_upper srslte_phy rrc_asn1) add_test(nas_test nas_test) diff --git a/srsue/test/upper/rrc_meas_test.cc b/srsue/test/upper/rrc_meas_test.cc new file mode 100644 index 000000000..ccea390a9 --- /dev/null +++ b/srsue/test/upper/rrc_meas_test.cc @@ -0,0 +1,983 @@ +/* + * 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 "srslte/asn1/rrc_asn1.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/test_common.h" +#include "srslte/upper/pdcp.h" +#include "srsue/hdr/stack/rrc/rrc.h" +#include "srsue/hdr/stack/rrc/rrc_meas.h" +#include "srsue/hdr/stack/upper/nas.h" +#include + +using namespace asn1::rrc; +using namespace srsue; + +class phy_test final : public phy_interface_rrc_lte +{ +public: + void set_serving_cell(uint32_t pci, uint32_t earfcn) + { + serving_pci = pci; + serving_earfcn = earfcn; + } + + // Not implemented methods + void set_config(srslte::phy_cfg_t& config, + uint32_t cc_idx = 0, + uint32_t earfcn = 0, + srslte_cell_t* cell_info = nullptr) override + { + } + void set_config_tdd(srslte_tdd_config_t& tdd_config) override {} + void set_config_mbsfn_sib2(srslte::mbsfn_sf_cfg_t* cfg_list, uint32_t nof_cfgs) override {} + void set_config_mbsfn_sib13(const srslte::sib13_t& sib13) override {} + void set_config_mbsfn_mcch(const srslte::mcch_msg_t& mcch) override {} + cell_search_ret_t cell_search(phy_cell_t* cell) override { return {}; } + bool cell_is_camping() override { return false; } + bool cell_select(phy_cell_t* cell = nullptr) override { return false; } + void reset() override {} + void enable_pregen_signals(bool enable) override {} + + void set_cells_to_meas(uint32_t earfcn, std::set& pci) override + { + freqs_started.insert(earfcn); + cells_started[earfcn] = pci; + } + void meas_stop() override + { + freqs_started.clear(); + cells_started.clear(); + } + + void reset_test() + { + meas_reset_called = false; + meas_stop(); + } + + uint32_t meas_nof_freqs() { return freqs_started.size(); } + + uint32_t meas_nof_cells(uint32_t earfcn) + { + if (cells_started.count(earfcn)) { + return cells_started[earfcn].size(); + } else { + return 0; + } + } + + bool meas_freq_started(uint32_t earfcn) { return freqs_started.count(earfcn) > 0; } + bool meas_cell_started(uint32_t earfcn, uint32_t pci) + { + if (cells_started.count(earfcn)) { + return cells_started[earfcn].count(pci) > 0; + } else { + return false; + } + } + +private: + bool meas_reset_called = false; + std::set freqs_started; + std::map > cells_started; + uint32_t serving_pci = 0; + uint32_t serving_earfcn = 0; +}; + +class nas_test : public srsue::nas +{ +public: + nas_test(srslte::log* log_, srslte::timer_handler* t) : srsue::nas(log_, t) {} + bool is_attached() override { return false; } +}; + +class pdcp_test : public srslte::pdcp +{ +public: + pdcp_test(srslte::log* log_, srslte::timer_handler* t) : srslte::pdcp(t, log_) {} + void write_sdu(uint32_t lcid, srslte::unique_byte_buffer_t sdu, bool blocking = false) override + { + ul_dcch_msg_s ul_dcch_msg; + asn1::cbit_ref bref(sdu->msg, sdu->N_bytes); + if (ul_dcch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or + ul_dcch_msg.msg.type().value != ul_dcch_msg_type_c::types_opts::c1) { + return; + } + switch (ul_dcch_msg.msg.c1().type()) { + case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_recfg_complete: + if (!error) { + error = !expecting_reconf_complete; + } + received_reconf_complete = true; + break; + case ul_dcch_msg_type_c::c1_c_::types::meas_report: + if (!error) { + error = expecting_reconf_complete; + } + if (!expecting_reconf_complete) { + meas_res = ul_dcch_msg.msg.c1().meas_report().crit_exts.c1().meas_report_r8().meas_results; + meas_res_received = true; + } + break; + default: + error = true; + break; + } + } + bool get_meas_res(meas_results_s& meas_res_) + { + if (meas_res_received) { + meas_res_ = meas_res; + meas_res_received = false; + return true; + } + return false; + } + + bool get_error() { return error; } + bool expecting_reconf_complete = false; + bool received_reconf_complete = false; + +private: + bool error = false; + meas_results_s meas_res = {}; + bool meas_res_received = false; +}; + +static srslte::timer_handler global_timers; + +class rrc_test : public rrc +{ +public: + rrc_test(srslte::log* log_) : rrc(log_) + { + pool = srslte::byte_buffer_pool::get_instance(); + nastest = new nas_test(log_, &global_timers); + pdcptest = new pdcp_test(log_, &global_timers); + }; + ~rrc_test() + { + delete nastest; + delete pdcptest; + } + + void init() + { + rrc::init(&phytest, nullptr, nullptr, pdcptest, nastest, nullptr, nullptr, &global_timers, nullptr, {}); + } + + void run_tti(uint32_t tti_) + { + global_timers.step_all(); + rrc::run_tti(tti_); + } + + // Set RRC in state RRC_CONNECTED + void connect() + { + dl_ccch_msg_s dl_ccch_msg = {}; + dl_ccch_msg.msg.set_c1(); + dl_ccch_msg.msg.c1().set_rrc_conn_setup(); + dl_ccch_msg.msg.c1().rrc_conn_setup().crit_exts.set_c1().set_rrc_conn_setup_r8(); + send_ccch_msg(dl_ccch_msg); + run_tti(tti++); + } + + bool send_meas_cfg(rrc_conn_recfg_r8_ies_s& rrc_conn_recfg) + { + phytest.reset_test(); + pdcptest->received_reconf_complete = false; + + dl_dcch_msg_s dl_dcch_msg = {}; + dl_dcch_msg.msg.set_c1(); + dl_dcch_msg.msg.c1().set_rrc_conn_recfg(); + dl_dcch_msg.msg.c1().rrc_conn_recfg().crit_exts.set_c1().set_rrc_conn_recfg_r8(); + dl_dcch_msg.msg.c1().rrc_conn_recfg().crit_exts.c1().rrc_conn_recfg_r8() = rrc_conn_recfg; + send_dcch_msg(dl_dcch_msg); + + pdcptest->expecting_reconf_complete = true; + run_tti(tti++); + pdcptest->expecting_reconf_complete = false; + return !pdcptest->get_error() && pdcptest->received_reconf_complete; + } + + void send_ccch_msg(dl_ccch_msg_s& dl_ccch_msg) + { + srslte::unique_byte_buffer_t pdu = srslte::allocate_unique_buffer(*pool, true); + ; + asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); + dl_ccch_msg.pack(bref); + bref.align_bytes_zero(); + pdu->N_bytes = (uint32_t)bref.distance_bytes(pdu->msg); + pdu->set_timestamp(); + write_pdu(0, std::move(pdu)); + } + + void send_dcch_msg(dl_dcch_msg_s& dl_dcch_msg) + { + srslte::unique_byte_buffer_t pdu = srslte::allocate_unique_buffer(*pool, true); + ; + asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); + dl_dcch_msg.pack(bref); + bref.align_bytes_zero(); + pdu->N_bytes = (uint32_t)bref.distance_bytes(pdu->msg); + pdu->set_timestamp(); + write_pdu(1, std::move(pdu)); + } + + void set_serving_cell(uint32_t pci, uint32_t earfcn) + { + std::vector phy_meas = {}; + rrc_interface_phy_lte::phy_meas_t meas = {}; + meas.pci = pci; + meas.earfcn = earfcn; + phy_meas.push_back(meas); // neighbour cell + new_cell_meas(phy_meas); + run_tti(1); + phytest.set_serving_cell(pci, earfcn); + rrc::set_serving_cell({pci, earfcn}, false); + } + + bool has_neighbour_cell(const uint32_t earfcn, const uint32_t pci) { return rrc::has_neighbour_cell(earfcn, pci); } + + bool get_meas_res(meas_results_s& meas_res) { return pdcptest->get_meas_res(meas_res); } + + phy_test phytest; + +private: + pdcp_test* pdcptest; + nas_test* nastest; + uint32_t tti = 0; + srslte::byte_buffer_pool* pool = nullptr; +}; + +// Test Cell sear +int cell_select_test() +{ + srslte::log_filter log1("RRC_MEAS"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + + printf("==========================================================\n"); + printf("====== Cell Select Testing ===============\n"); + printf("==========================================================\n"); + + rrc_test rrctest(&log1); + rrctest.init(); + rrctest.connect(); + + // Add a first serving cell + rrctest.set_serving_cell(1, 1); + + // Add a second serving cell + rrctest.set_serving_cell(2, 2); + + // Select the second serving cell + rrctest.set_serving_cell(2, 2); + + TESTASSERT(!rrctest.has_neighbour_cell(2, 2)); + + return SRSLTE_SUCCESS; +} + +// Tests the measObject configuration and the successful activation of PHY cells to search for +int meas_obj_test() +{ + srslte::log_filter log1("RRC_MEAS"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + + printf("==========================================================\n"); + printf("====== Object Configuration Testing ===============\n"); + printf("==========================================================\n"); + + rrc_test rrctest(&log1); + rrctest.init(); + rrctest.connect(); + + // Configure serving cell. First add neighbour, then set it as serving cell + rrctest.set_serving_cell(1, 1); + + rrc_conn_recfg_r8_ies_s rrc_conn_recfg = {}; + rrc_conn_recfg.meas_cfg_present = true; + meas_cfg_s& meas_cfg = rrc_conn_recfg.meas_cfg; + + log1.info("Test1: Remove non-existing measObject, reportConfig and measId\n"); + meas_cfg = {}; + meas_cfg.meas_id_to_rem_list.push_back(3); + meas_cfg.meas_obj_to_rem_list.push_back(3); + meas_cfg.report_cfg_to_rem_list.push_back(3); + meas_cfg.meas_id_to_rem_list_present = true; + meas_cfg.meas_obj_to_rem_list_present = true; + + // Just test it doesn't crash + TESTASSERT(rrctest.send_meas_cfg(rrc_conn_recfg)); + TESTASSERT(rrctest.phytest.meas_nof_freqs() == 0); + + log1.info("Test2: Add measId pointing to non-existing measObject or reportConfig\n"); + meas_cfg = {}; + meas_id_to_add_mod_s m = {}; + m.meas_obj_id = 1; + m.report_cfg_id = 1; + m.meas_id = 1; + meas_cfg.meas_id_to_add_mod_list.push_back(m); + meas_cfg.meas_id_to_add_mod_list_present = true; + + // Just test it doesn't crash + TESTASSERT(rrctest.send_meas_cfg(rrc_conn_recfg)); + TESTASSERT(rrctest.phytest.meas_nof_freqs() == 0); + + log1.info("Test3: Add meaObject and report of unsupported type. Setup a supported report for later use\n"); + meas_cfg = {}; + meas_obj_to_add_mod_s obj = {}; + obj.meas_obj.set_meas_obj_utra(); + meas_cfg.meas_obj_to_add_mod_list.push_back(obj); + meas_cfg.meas_obj_to_add_mod_list_present = true; + report_cfg_to_add_mod_s rep = {}; + rep.report_cfg_id = 2; + rep.report_cfg.set_report_cfg_inter_rat().trigger_type.set_periodical().purpose = + report_cfg_inter_rat_s::trigger_type_c_::periodical_s_::purpose_opts::report_strongest_cells; + rep.report_cfg.report_cfg_inter_rat().report_interv.value = report_interv_opts::ms640; + rep.report_cfg.report_cfg_inter_rat().report_amount = report_cfg_inter_rat_s::report_amount_opts::r1; + meas_cfg.report_cfg_to_add_mod_list.push_back(rep); + rep = {}; + rep.report_cfg_id = 1; + rep.report_cfg.set_report_cfg_eutra(); + rep.report_cfg.report_cfg_eutra().report_interv = report_interv_opts::ms120; + rep.report_cfg.report_cfg_eutra().report_amount = report_cfg_eutra_s::report_amount_opts::r1; + rep.report_cfg.report_cfg_eutra().report_quant.value = report_cfg_eutra_s::report_quant_opts::both; + rep.report_cfg.report_cfg_eutra().trigger_quant.value = report_cfg_eutra_s::trigger_quant_opts::rsrp; + rep.report_cfg.report_cfg_eutra().trigger_type.set_event().event_id.set_event_a1().a1_thres.set_thres_rsrp(); + rep.report_cfg.report_cfg_eutra().trigger_type.event().time_to_trigger.value = time_to_trigger_opts::ms640; + meas_cfg.report_cfg_to_add_mod_list.push_back(rep); + meas_cfg.report_cfg_to_add_mod_list_present = true; + + // Just test it doesn't crash + TESTASSERT(rrctest.send_meas_cfg(rrc_conn_recfg)); + TESTASSERT(rrctest.phytest.meas_nof_freqs() == 0); + + log1.info("Test4: Add 2 measObjects and 2 measId both pointing to the same measObject \n"); + meas_cfg = {}; + for (int i = 0; i < 2; i++) { + m = {}; + m.meas_obj_id = 1; // same object + m.report_cfg_id = 1; + m.meas_id = 1 + i; // add 2 different measIds + meas_cfg.meas_id_to_add_mod_list.push_back(m); + } + meas_cfg.meas_id_to_add_mod_list_present = true; + for (int i = 0; i < 2; i++) { + obj = {}; + obj.meas_obj_id = 1 + i; + obj.meas_obj.set_meas_obj_eutra(); + obj.meas_obj.meas_obj_eutra().carrier_freq = 1 + i; + obj.meas_obj.meas_obj_eutra().allowed_meas_bw.value = allowed_meas_bw_opts::mbw6; + if (i == 1) { // 2nd object has cells, 1st one doesn't + for (int j = 1; j <= 4; j++) { + cells_to_add_mod_s cell = {}; + cell.pci = 10 + j; + cell.cell_idx = j; + cell.cell_individual_offset.value = q_offset_range_opts::db0; + obj.meas_obj.meas_obj_eutra().cells_to_add_mod_list.push_back(cell); + } + obj.meas_obj.meas_obj_eutra().cells_to_add_mod_list_present = true; + } + meas_cfg.meas_obj_to_add_mod_list.push_back(obj); + } + meas_cfg.meas_obj_to_add_mod_list_present = true; + + TESTASSERT(rrctest.send_meas_cfg(rrc_conn_recfg)); + // Test we configure 1 frequency with no cells + TESTASSERT(rrctest.phytest.meas_nof_freqs() == 1); + TESTASSERT(rrctest.phytest.meas_freq_started(1)); + TESTASSERT(rrctest.phytest.meas_nof_cells(1) == 0); + + log1.info("Test5: Add existing objects and measId. Now add measId for 2nd cell\n"); + meas_cfg = {}; + m = {}; + m.meas_obj_id = 2; // same object + m.report_cfg_id = 1; + m.meas_id = 3; + meas_cfg.meas_id_to_add_mod_list.push_back(m); + meas_cfg.meas_id_to_add_mod_list_present = true; + rrctest.phytest.reset_test(); + TESTASSERT(rrctest.send_meas_cfg(rrc_conn_recfg)); + + // Test we configure 2 frequency. 2nd has 4 cells + TESTASSERT(rrctest.phytest.meas_nof_freqs() == 2); + TESTASSERT(rrctest.phytest.meas_freq_started(1)); + TESTASSERT(rrctest.phytest.meas_freq_started(2)); + TESTASSERT(rrctest.phytest.meas_nof_cells(1) == 0); + TESTASSERT(rrctest.phytest.meas_nof_cells(2) == 4); + for (int j = 1; j <= 4; j++) { + TESTASSERT(rrctest.phytest.meas_cell_started(2, 10 + j)); + } + + // Reconfigure 2nd object only, we should see 8 cells now + log1.info("Test6: Add 1 cell to 1st object. Mixed add/mod and removal command.\n"); + meas_cfg = {}; + meas_cfg.meas_obj_to_add_mod_list_present = true; + + // 1st object add 1 cell, none existed + obj = {}; + obj.meas_obj_id = 1; + obj.meas_obj.set(meas_obj_to_add_mod_s::meas_obj_c_::types_opts::meas_obj_eutra); + obj.meas_obj.meas_obj_eutra().carrier_freq = 1; + obj.meas_obj.meas_obj_eutra().cells_to_add_mod_list_present = true; + obj.meas_obj.meas_obj_eutra().allowed_meas_bw.value = allowed_meas_bw_opts::mbw6; + cells_to_add_mod_s cell = {}; + cell.cell_idx = 1; + cell.pci = 1; + cell.cell_individual_offset.value = q_offset_range_opts::db0; + obj.meas_obj.meas_obj_eutra().cells_to_add_mod_list.push_back(cell); + meas_cfg.meas_obj_to_add_mod_list.push_back(obj); + + // 2nd object remove 3 cells (1 non-existing) + obj = {}; + obj.meas_obj_id = 2; + obj.meas_obj.set(meas_obj_to_add_mod_s::meas_obj_c_::types_opts::meas_obj_eutra); + obj.meas_obj.meas_obj_eutra().carrier_freq = 2; + obj.meas_obj.meas_obj_eutra().cells_to_rem_list.push_back(2); + obj.meas_obj.meas_obj_eutra().cells_to_rem_list.push_back(4); + obj.meas_obj.meas_obj_eutra().cells_to_rem_list.push_back(6); + obj.meas_obj.meas_obj_eutra().cells_to_rem_list_present = true; + + // 2nd object add 5 cells, 1 existing, 1 just removed, 3 new + uint32_t new_idx[5] = {2, 3, 5, 6}; + for (int j = 0; j < 4; j++) { + cell = {}; + cell.pci = 20 + j + 1; + cell.cell_idx = new_idx[j]; + cell.cell_individual_offset.value = q_offset_range_opts::db0; + obj.meas_obj.meas_obj_eutra().cells_to_add_mod_list.push_back(cell); + } + obj.meas_obj.meas_obj_eutra().allowed_meas_bw.value = allowed_meas_bw_opts::mbw6; + obj.meas_obj.meas_obj_eutra().cells_to_add_mod_list_present = true; + meas_cfg.meas_obj_to_add_mod_list.push_back(obj); + + rrctest.phytest.reset_test(); + TESTASSERT(rrctest.send_meas_cfg(rrc_conn_recfg)); + TESTASSERT(rrctest.phytest.meas_nof_cells(1) == 1); + TESTASSERT(rrctest.phytest.meas_cell_started(1, 1)); + TESTASSERT(rrctest.phytest.meas_nof_cells(2) == 5); + TESTASSERT(rrctest.phytest.meas_cell_started(2, 11)); // wasn't changed + TESTASSERT(!rrctest.phytest.meas_cell_started(2, 12)); // was removed + TESTASSERT(!rrctest.phytest.meas_cell_started(2, 14)); // was removed + TESTASSERT(rrctest.phytest.meas_cell_started(2, 21)); // was added + TESTASSERT(rrctest.phytest.meas_cell_started(2, 22)); // was updated + TESTASSERT(rrctest.phytest.meas_cell_started(2, 23)); // was added + TESTASSERT(rrctest.phytest.meas_cell_started(2, 24)); // was added + + log1.info("Test7: PHY finds new neigbhours in frequency 1 and 2, check RRC instructs to search them\n"); + std::vector phy_meas = {}; + phy_meas.push_back({0, 0, 1, 31}); + phy_meas.push_back({-1, 0, 1, 32}); + phy_meas.push_back({-2, 0, 1, 33}); + phy_meas.push_back({-3, 0, 1, 34}); + rrctest.new_cell_meas(phy_meas); + rrctest.run_tti(1); + phy_meas = {}; + phy_meas.push_back({-4, 0, 1, 35}); + phy_meas.push_back({-5, 0, 1, 36}); + phy_meas.push_back({-6, 0, 1, 37}); + phy_meas.push_back({1, 0, 1, 30}); + phy_meas.push_back({0, 0, 2, 31}); + rrctest.new_cell_meas(phy_meas); + rrctest.run_tti(1); + + TESTASSERT(rrctest.phytest.meas_nof_cells(1) == 8); + TESTASSERT(rrctest.phytest.meas_cell_started(1, 1)); + TESTASSERT(rrctest.phytest.meas_cell_started(1, 30)); + TESTASSERT(rrctest.phytest.meas_cell_started(1, 31)); + TESTASSERT(rrctest.phytest.meas_cell_started(1, 32)); + TESTASSERT(rrctest.phytest.meas_cell_started(1, 33)); + TESTASSERT(rrctest.phytest.meas_cell_started(1, 34)); + TESTASSERT(rrctest.phytest.meas_cell_started(1, 35)); + TESTASSERT(rrctest.phytest.meas_cell_started(1, 36)); + TESTASSERT(rrctest.phytest.meas_nof_cells(2) == 6); + TESTASSERT(rrctest.phytest.meas_cell_started(2, 11)); // wasn't changed + TESTASSERT(!rrctest.phytest.meas_cell_started(2, 12)); // was removed + TESTASSERT(!rrctest.phytest.meas_cell_started(2, 14)); // was removed + TESTASSERT(rrctest.phytest.meas_cell_started(2, 21)); // was added + TESTASSERT(rrctest.phytest.meas_cell_started(2, 22)); // was updated + TESTASSERT(rrctest.phytest.meas_cell_started(2, 23)); // was added + TESTASSERT(rrctest.phytest.meas_cell_started(2, 24)); // was added + TESTASSERT(rrctest.phytest.meas_cell_started(2, 31)); + + log1.info("Test8: Simulate a Release (reset() call) make sure resets correctly\n"); + rrctest.init(); + rrctest.run_tti(1); + rrctest.connect(); + rrctest.run_tti(1); + + log1.info("Test9: Config removal\n"); + meas_cfg = {}; + meas_cfg.meas_obj_to_rem_list.push_back(1); + meas_cfg.meas_obj_to_rem_list.push_back(2); + meas_cfg.meas_obj_to_rem_list_present = true; + meas_cfg.report_cfg_to_rem_list.push_back(1); + meas_cfg.report_cfg_to_rem_list.push_back(2); + meas_cfg.report_cfg_to_rem_list_present = true; + meas_cfg.meas_id_to_rem_list.push_back(1); + meas_cfg.meas_id_to_rem_list.push_back(2); + meas_cfg.meas_id_to_rem_list_present = true; + printf("==========================================================\n"); + return 0; +} + +void config_default_report_test(rrc_conn_recfg_r8_ies_s& rrc_conn_recfg, + eutra_event_s::event_id_c_ event_id, + time_to_trigger_e time_trigger, + uint32_t hyst, + report_cfg_eutra_s::report_amount_e_ report_amount, + report_interv_e report_interv) +{ + rrc_conn_recfg.meas_cfg_present = true; + meas_cfg_s& meas_cfg = rrc_conn_recfg.meas_cfg; + + meas_cfg = {}; + meas_id_to_add_mod_s m = {}; + m.meas_obj_id = 4; + m.report_cfg_id = 1; + m.meas_id = 1; + meas_cfg.meas_id_to_add_mod_list.push_back(m); + if (event_id.type() == eutra_event_s::event_id_c_::types::event_a3) { + m = {}; + m.meas_obj_id = 6; + m.report_cfg_id = 1; + m.meas_id = 2; + meas_cfg.meas_id_to_add_mod_list.push_back(m); + } + meas_cfg.meas_id_to_add_mod_list_present = true; + + meas_obj_to_add_mod_s obj = {}; + obj.meas_obj.set_meas_obj_eutra(); + obj.meas_obj.meas_obj_eutra().carrier_freq = 1; + obj.meas_obj.meas_obj_eutra().allowed_meas_bw.value = allowed_meas_bw_opts::mbw6; + obj.meas_obj_id = 4; + meas_cfg.meas_obj_to_add_mod_list.push_back(obj); + obj = {}; + obj.meas_obj.set_meas_obj_eutra(); + obj.meas_obj.meas_obj_eutra().carrier_freq = 2; + obj.meas_obj.meas_obj_eutra().allowed_meas_bw.value = allowed_meas_bw_opts::mbw6; + obj.meas_obj_id = 6; + meas_cfg.meas_obj_to_add_mod_list.push_back(obj); + meas_cfg.meas_obj_to_add_mod_list_present = true; + + // Disable avearging + meas_cfg.quant_cfg_present = true; + meas_cfg.quant_cfg.quant_cfg_eutra_present = true; + meas_cfg.quant_cfg.quant_cfg_eutra.filt_coef_rsrp_present = true; + meas_cfg.quant_cfg.quant_cfg_eutra.filt_coef_rsrp.value = filt_coef_opts::fc0; + + // Report event + report_cfg_to_add_mod_s rep = {}; + rep.report_cfg_id = 1; + rep.report_cfg.set_report_cfg_eutra(); + rep.report_cfg.report_cfg_eutra().trigger_type.set_event(); + rep.report_cfg.report_cfg_eutra().trigger_type.event().event_id = event_id; + rep.report_cfg.report_cfg_eutra().trigger_type.event().time_to_trigger = time_trigger; + rep.report_cfg.report_cfg_eutra().trigger_type.event().hysteresis = hyst; + rep.report_cfg.report_cfg_eutra().trigger_quant.value = report_cfg_eutra_s::trigger_quant_opts::rsrp; + rep.report_cfg.report_cfg_eutra().report_quant.value = report_cfg_eutra_s::report_quant_opts::same_as_trigger_quant; + rep.report_cfg.report_cfg_eutra().max_report_cells = 8; + rep.report_cfg.report_cfg_eutra().report_interv.value = report_interv; + rep.report_cfg.report_cfg_eutra().report_amount.value = report_amount; + meas_cfg.report_cfg_to_add_mod_list.push_back(rep); + meas_cfg.report_cfg_to_add_mod_list_present = true; +} + +void send_report(rrc_test& rrctest, + const std::vector rsrp, + const std::vector earfcn, + const std::vector pci) +{ + std::vector phy_meas = {}; + for (uint32_t i = 0; i < pci.size(); i++) { + float r = rsrp[0]; + if (rsrp.size() == pci.size()) { + r = rsrp[i]; + } + uint32_t e = earfcn[0]; + if (earfcn.size() == pci.size()) { + e = earfcn[i]; + } + phy_meas.push_back({r, -5, e, pci[i]}); + } + rrctest.new_cell_meas(phy_meas); + rrctest.run_tti(1); +} + +void middle_condition(rrc_test& rrctest, + const eutra_event_s::event_id_c_ event_id, + const uint32_t hyst, + const uint32_t earfcn, + const std::vector pci) +{ + if (event_id.type() == eutra_event_s::event_id_c_::types_opts::event_a1) { + float rsrp_th = -140 + event_id.event_a1().a1_thres.thres_rsrp() + 0.5 * hyst; + send_report(rrctest, {rsrp_th - (float)1e-2}, {earfcn}, pci); + } else { + float offset = 0.5 * event_id.event_a3().a3_offset; + std::vector rsrp = {}; + rsrp.reserve(pci.size()); + for (uint32_t i = 0; i < pci.size(); i++) { + if (i == 0) { + rsrp.push_back(-60); + } else { + rsrp.push_back(-60 + offset + 0.5 * hyst - (float)1e-2); + } + } + send_report(rrctest, rsrp, {0, earfcn}, pci); + } +} + +void enter_condition(rrc_test& rrctest, + const eutra_event_s::event_id_c_ event_id, + const uint32_t hyst, + const uint32_t earfcn, + const std::vector pci) +{ + if (event_id.type() == eutra_event_s::event_id_c_::types_opts::event_a1) { + float rsrp_th = -140 + event_id.event_a1().a1_thres.thres_rsrp() + 0.5 * hyst; + send_report(rrctest, {rsrp_th + (float)1e-2}, {earfcn}, pci); + } else { + float offset = 0.5 * event_id.event_a3().a3_offset; + std::vector rsrp = {}; + rsrp.reserve(pci.size()); + for (uint32_t i = 0; i < pci.size(); i++) { + if (i == 0) { + rsrp.push_back(-60); + } else { + rsrp.push_back(-60 + offset + 0.01 * pci[i] + 0.5 * hyst + (float)1e-2); + } + } + send_report(rrctest, rsrp, {0, earfcn}, pci); + } +} + +void no_condition(rrc_test& rrctest, const std::vector& earfcn, const std::vector& pci) +{ + std::vector rsrp = {}; + rsrp.reserve(pci.size()); + for (uint32_t i = 0; i < pci.size(); i++) { + rsrp.push_back(-60.0f); + } + send_report(rrctest, rsrp, earfcn, pci); +} + +void exit_condition(rrc_test& rrctest, + const eutra_event_s::event_id_c_ event_id, + const uint32_t hyst, + const uint32_t earfcn, + const std::vector pci) +{ + if (event_id.type() == eutra_event_s::event_id_c_::types_opts::event_a1) { + float rsrp_th_leave = -140 + event_id.event_a1().a1_thres.thres_rsrp() - 0.5 * hyst; + send_report(rrctest, {rsrp_th_leave - (float)1e-2}, {earfcn}, pci); + } else { + float offset = 0.5 * event_id.event_a3().a3_offset; + std::vector rsrp = {}; + rsrp.reserve(pci.size()); + for (uint32_t i = 0; i < pci.size(); i++) { + if (i == 0) { + rsrp.push_back(-60); + } else { + rsrp.push_back(-60 + offset - 0.5 * hyst - (float)1e-2); + } + } + send_report(rrctest, rsrp, {0, earfcn}, pci); + } +} + +// Test A1-event reporting and management of report amount and interval +int a1event_report_test(uint32_t a1_rsrp_th, + time_to_trigger_e time_trigger, + uint32_t hyst, + report_cfg_eutra_s::report_amount_e_ report_amount, + report_interv_e report_interv) +{ + + srslte::log_filter log1("RRC_MEAS"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + + printf("==========================================================\n"); + printf("============ Report Testing A1 ===============\n"); + printf("==========================================================\n"); + + rrc_test rrctest(&log1); + rrctest.init(); + rrctest.connect(); + + // Configure serving cell. First add neighbour, then set it as serving cell + rrctest.set_serving_cell(1, 1); + + // default report configuration + rrc_conn_recfg_r8_ies_s rrc_conn_recfg = {}; + eutra_event_s::event_id_c_ event_id = {}; + + event_id.set_event_a1(); + event_id.event_a1().a1_thres.set_thres_rsrp(); + event_id.event_a1().a1_thres.thres_rsrp() = a1_rsrp_th; + + config_default_report_test(rrc_conn_recfg, event_id, time_trigger, hyst, report_amount, report_interv); + + TESTASSERT(rrctest.send_meas_cfg(rrc_conn_recfg)); + + meas_results_s meas_res = {}; + + int ttt_iters = time_trigger.to_number() + 1; + + // Entering condition during half timeToTrigger, should not trigger measurement + for (int i = 0; i < ttt_iters / 2; i++) { + log1.info("Report %d/%d enter condition is true\n", i, ttt_iters / 2); + enter_condition(rrctest, event_id, hyst, 0, {1, 2}); + // Check doesn't generate measurement report + TESTASSERT(!rrctest.get_meas_res(meas_res)); + } + + log1.info("Report leaving enter condition\n"); + // Not satisfy entering condition for 1 TTI + middle_condition(rrctest, event_id, hyst, 0, {1}); + TESTASSERT(!rrctest.get_meas_res(meas_res)); + + // Should go again all timeToTrigger, should not trigger measurement until end + for (int i = 0; i < ttt_iters; i++) { + log1.info("Report %d/%d enter condition is true\n", i, ttt_iters); + enter_condition(rrctest, event_id, hyst, 0, {1, 2}); + if (i < ttt_iters - 1) { + // Check doesn't generate measurement report + TESTASSERT(!rrctest.get_meas_res(meas_res)); + } + } + + // Check report is correct: RSRP=32, RSRQ=30 and measId=1 + TESTASSERT(rrctest.get_meas_res(meas_res)); + TESTASSERT(meas_res.meas_id == 1); + TESTASSERT(!meas_res.meas_result_neigh_cells_present); + TESTASSERT(meas_res.meas_result_pcell.rsrp_result == 32); + TESTASSERT(meas_res.meas_result_pcell.rsrq_result == 30); + + // Test multiple reports are sent if report_amount > 1 + if (report_amount.to_number() > 1) { + // Trigger again entering condition for the same cell it shouldn't trigger a new report, just keep sending the + // periodic reports without restarting counter + for (int i = 0; i < ttt_iters; i++) { + log1.info("Report %d/%d enter condition is true\n", i, ttt_iters); + enter_condition(rrctest, event_id, hyst, 0, {1}); + } + // Do not expect report if timer not expired + TESTASSERT(!rrctest.get_meas_res(meas_res)); + // Wait to generate all reports + for (int i = 0; i < report_amount.to_number() - 1; i++) { + log1.info("Testing report %d/%d\n", i, report_amount.to_number()); + int interval = report_interv.to_number(); + if (i == 0) { + // already stepped these iterations + interval -= ttt_iters; + } + for (int j = 0; j < interval; j++) { + if (j == 0 && i > report_amount.to_number() - 3) { + // Exit the enter condition in the last one, should still send the last report + middle_condition(rrctest, event_id, hyst, 0, {1}); + } else { + log1.info("Stepping timer %d/%d\n", j, interval); + rrctest.run_tti(1); + } + if (j < interval - 1) { + // Do not expect report if timer not expired + TESTASSERT(!rrctest.get_meas_res(meas_res)); + } else { + // expect 1 report every interval ms + TESTASSERT(rrctest.get_meas_res(meas_res)); + } + } + } + // Do not expect more reports + for (int j = 0; j < report_interv.to_number(); j++) { + rrctest.run_tti(1); + TESTASSERT(!rrctest.get_meas_res(meas_res)); + } + // Trigger again condition + for (int i = 0; i < ttt_iters; i++) { + log1.info("Report %d/%d enter condition is true\n", i, ttt_iters); + enter_condition(rrctest, event_id, hyst, 0, {1}); + } + // Do not expect report + TESTASSERT(!rrctest.get_meas_res(meas_res)); + + // Leaving condition for timeToTrigger + for (int i = 0; i < ttt_iters; i++) { + log1.info("Report %d/%d leaving condition is true\n", i, ttt_iters); + exit_condition(rrctest, event_id, hyst, 0, {1}); + // Check doesn't generate measurement report + TESTASSERT(!rrctest.get_meas_res(meas_res)); + } + // Trigger again condition + for (int i = 0; i < ttt_iters; i++) { + log1.info("Report %d/%d enter condition is true\n", i, ttt_iters); + enter_condition(rrctest, event_id, hyst, 0, {1}); + } + // Expect report + TESTASSERT(rrctest.get_meas_res(meas_res)); + } + + printf("==========================================================\n"); + return 0; +} + +// Test A3-event reporting and management of report amount and interval +int a3event_report_test(uint32_t a3_offset, uint32_t hyst, bool report_on_leave) +{ + + srslte::log_filter log1("RRC_MEAS"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + + printf("==========================================================\n"); + printf("============ Report Testing A3 ===============\n"); + printf("==========================================================\n"); + + rrc_test rrctest(&log1); + rrctest.init(); + rrctest.connect(); + + // Configure serving cell. First add neighbour, then set it as serving cell + rrctest.set_serving_cell(1, 1); + + // default report configuration + rrc_conn_recfg_r8_ies_s rrc_conn_recfg = {}; + eutra_event_s::event_id_c_ event_id = {}; + + event_id.set_event_a3(); + event_id.event_a3().a3_offset = a3_offset; + event_id.event_a3().report_on_leave = report_on_leave; + + config_default_report_test(rrc_conn_recfg, + event_id, + time_to_trigger_opts::ms0, + hyst, + report_cfg_eutra_s::report_amount_opts::r1, + report_interv_opts::ms120); + + TESTASSERT(rrctest.send_meas_cfg(rrc_conn_recfg)); + + meas_results_s meas_res = {}; + + log1.info("Test no-enter condition and no trigger report \n"); + no_condition(rrctest, {0}, {1}); + TESTASSERT(!rrctest.get_meas_res(meas_res)); + + no_condition(rrctest, {0, 1}, {1, 0}); + TESTASSERT(!rrctest.get_meas_res(meas_res)); + + log1.info("Test enter condition triggers report. 1 neighbour cell in enter + 1 in exit \n"); + float offset = 0.5 * event_id.event_a3().a3_offset; + std::vector rsrp = {}; + rsrp.push_back(-60 + offset + 0.5 * hyst + (float)1e-2); + rsrp.push_back(-60 + offset - 0.5 * hyst - (float)1e-2); + send_report(rrctest, rsrp, {1, 1}, {0, 3}); + + // Check report is correct: RSRP=34, RSRQ=0 and measId=1 + TESTASSERT(rrctest.get_meas_res(meas_res)); + TESTASSERT(meas_res.meas_id == 1); + TESTASSERT(meas_res.meas_result_pcell.rsrp_result == 81); + TESTASSERT(meas_res.meas_result_pcell.rsrq_result == 30); + TESTASSERT(meas_res.meas_result_neigh_cells_present); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra().size() == 1); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra()[0].pci == 0); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra()[0].meas_result.rsrp_result == + 81 + (hyst + a3_offset) / 2); + + // Next iteration in entering state does not trigger another report + log1.info("Test enter condition for the same cell does not trigger report\n"); + rrctest.run_tti(1); + TESTASSERT(!rrctest.get_meas_res(meas_res)); + + log1.info("Test enter condition for different earfcn triggers report\n"); + enter_condition(rrctest, event_id, hyst, 2, {1, 3}); + TESTASSERT(rrctest.get_meas_res(meas_res)); + TESTASSERT(meas_res.meas_id == 2); + TESTASSERT(meas_res.meas_result_pcell.rsrp_result == 81); + TESTASSERT(meas_res.meas_result_pcell.rsrq_result == 30); + TESTASSERT(meas_res.meas_result_neigh_cells_present); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra().size() == 1); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra()[0].pci == 3); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra()[0].meas_result.rsrp_result == + 81 + (hyst + a3_offset) / 2); + + // if a new cell enters conditions then expect another report + log1.info("Test a new cell enter condition triggers report\n"); + enter_condition(rrctest, event_id, hyst, 1, {1, 3}); + TESTASSERT(rrctest.get_meas_res(meas_res)); + TESTASSERT(meas_res.meas_id == 1); + TESTASSERT(meas_res.meas_result_pcell.rsrp_result == 81); + TESTASSERT(meas_res.meas_result_pcell.rsrq_result == 30); + TESTASSERT(meas_res.meas_result_neigh_cells_present); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra().size() == 2); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra()[0].pci == + 3); // should be ordered by rsrp, which is proportional to pci in enter_condition() + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra()[0].meas_result.rsrp_result == + 81 + (hyst + a3_offset) / 2); + + // cell pci=0 exists condition + log1.info("Test exit condition\n"); + exit_condition(rrctest, event_id, hyst, 1, {1, 0}); + if (report_on_leave) { + TESTASSERT(rrctest.get_meas_res(meas_res)); + } + + // 2 enters again, now expect report again + log1.info("Test trigger again the cell that exited\n"); + enter_condition(rrctest, event_id, hyst, 1, {1, 0}); + TESTASSERT(rrctest.get_meas_res(meas_res)); + TESTASSERT(meas_res.meas_id == 1); + TESTASSERT(meas_res.meas_result_neigh_cells_present); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra().size() == 2); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra()[0].pci == 3); + TESTASSERT(meas_res.meas_result_neigh_cells.meas_result_list_eutra()[0].meas_result.rsrp_result == + 81 + (hyst + a3_offset) / 2); + + return SRSLTE_SUCCESS; +} + +int main(int argc, char** argv) +{ + TESTASSERT(cell_select_test() == SRSLTE_SUCCESS); + TESTASSERT(meas_obj_test() == SRSLTE_SUCCESS); + TESTASSERT( + a1event_report_test( + 30, time_to_trigger_opts::ms40, 3, report_cfg_eutra_s::report_amount_opts::r1, report_interv_opts::ms120) == + SRSLTE_SUCCESS); + TESTASSERT( + a1event_report_test( + 30, time_to_trigger_opts::ms0, 3, report_cfg_eutra_s::report_amount_opts::r1, report_interv_opts::ms120) == + SRSLTE_SUCCESS); + TESTASSERT( + a1event_report_test( + 30, time_to_trigger_opts::ms40, 3, report_cfg_eutra_s::report_amount_opts::r8, report_interv_opts::ms120) == + SRSLTE_SUCCESS); + TESTASSERT(a3event_report_test(6, 3, true) == SRSLTE_SUCCESS); + return SRSLTE_SUCCESS; +} diff --git a/srsue/test/upper/rrc_reconfig_test.cc b/srsue/test/upper/rrc_reconfig_test.cc index 044fc8a1b..e241a6548 100644 --- a/srsue/test/upper/rrc_reconfig_test.cc +++ b/srsue/test/upper/rrc_reconfig_test.cc @@ -36,7 +36,7 @@ using namespace asn1; using namespace asn1::rrc; -void nas_test() +int nas_test() { srslte::log_filter log1("NAS"); log1.set_level(srslte::LOG_LEVEL_DEBUG); @@ -93,9 +93,10 @@ void nas_test() default: break; } + return 0; } -int basic_test() +int meas_obj_test() { srslte::log_filter log1("RRC"); log1.set_level(srslte::LOG_LEVEL_DEBUG); @@ -154,6 +155,6 @@ int basic_test() int main(int argc, char** argv) { - TESTASSERT(basic_test() == 0); - nas_test(); + TESTASSERT(meas_obj_test() == 0); + TESTASSERT(nas_test() == 0); } diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index a4ffbdec6..4ccc91b6b 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -298,10 +298,6 @@ enable = false # # interpolate_subframe_enabled: Interpolates in the time domain the channel estimates within 1 subframe. Default is to average. # -# sic_pss_enabled: Applies Successive Interference Cancellation to PSS signals when searching for neighbour cells. -# Must be disabled if cells have identical channel and timing, for instance if generated from -# the same source. -# # pdsch_csi_enabled: Stores the Channel State Information and uses it for weightening the softbits. It is only # used in TM1. It is True by default. # @@ -333,7 +329,6 @@ enable = false #estimator_fil_order = 4 #snr_to_cqi_offset = 0.0 #interpolate_subframe_enabled = false -#sic_pss_enabled = true #pregenerate_signals = false #pdsch_csi_enabled = true #pdsch_8bit_decoder = false