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
This commit is contained in:
parent
d382c10948
commit
52716f8716
|
@ -80,6 +80,7 @@ option(ENABLE_TTCN3 "Enable TTCN3 test binaries" OFF)
|
||||||
option(BUILD_STATIC "Attempt to statically link external deps" OFF)
|
option(BUILD_STATIC "Attempt to statically link external deps" OFF)
|
||||||
option(RPATH "Enable RPATH" OFF)
|
option(RPATH "Enable RPATH" OFF)
|
||||||
option(ENABLE_ASAN "Enable gcc/clang address sanitizer" 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_MSAN "Enable clang memory sanitizer" OFF)
|
||||||
option(ENABLE_TIDY "Enable clang tidy" 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")
|
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")
|
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")
|
endif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
|
|
@ -188,6 +188,8 @@ public:
|
||||||
|
|
||||||
void stop() { impl()->stop(); }
|
void stop() { impl()->stop(); }
|
||||||
|
|
||||||
|
void clear() { impl()->clear(); }
|
||||||
|
|
||||||
void release()
|
void release()
|
||||||
{
|
{
|
||||||
impl()->clear();
|
impl()->clear();
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#ifndef SRSLTE_UE_INTERFACES_H
|
#ifndef SRSLTE_UE_INTERFACES_H
|
||||||
#define SRSLTE_UE_INTERFACES_H
|
#define SRSLTE_UE_INTERFACES_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "rrc_interface_types.h"
|
#include "rrc_interface_types.h"
|
||||||
|
@ -133,9 +134,17 @@ public:
|
||||||
class rrc_interface_phy_lte
|
class rrc_interface_phy_lte
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void in_sync() = 0;
|
// Measurement object from phy
|
||||||
virtual void out_of_sync() = 0;
|
typedef struct {
|
||||||
virtual void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn = -1, int pci = -1) = 0;
|
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<phy_meas_t>& meas) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// RRC interface for NAS
|
// RRC interface for NAS
|
||||||
|
@ -273,6 +282,7 @@ public:
|
||||||
virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0;
|
virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0;
|
||||||
virtual bool has_bearer(uint32_t lcid) = 0;
|
virtual bool has_bearer(uint32_t lcid) = 0;
|
||||||
virtual bool has_data(const 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;
|
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;
|
uint32_t estimator_fil_order = 4;
|
||||||
float snr_to_cqi_offset = 0.0f;
|
float snr_to_cqi_offset = 0.0f;
|
||||||
std::string sss_algorithm = "full";
|
std::string sss_algorithm = "full";
|
||||||
bool sic_pss_enabled = false;
|
|
||||||
float rx_gain_offset = 62;
|
float rx_gain_offset = 62;
|
||||||
bool pdsch_csi_enabled = true;
|
bool pdsch_csi_enabled = true;
|
||||||
bool pdsch_8bit_decoder = false;
|
bool pdsch_8bit_decoder = false;
|
||||||
|
@ -582,10 +591,6 @@ public:
|
||||||
class phy_interface_rrc_lte
|
class phy_interface_rrc_lte
|
||||||
{
|
{
|
||||||
public:
|
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,
|
virtual void set_config(srslte::phy_cfg_t& config,
|
||||||
uint32_t cc_idx = 0,
|
uint32_t cc_idx = 0,
|
||||||
uint32_t earfcn = 0,
|
uint32_t earfcn = 0,
|
||||||
|
@ -596,9 +601,8 @@ public:
|
||||||
virtual void set_config_mbsfn_mcch(const srslte::mcch_msg_t& mcch) = 0;
|
virtual void set_config_mbsfn_mcch(const srslte::mcch_msg_t& mcch) = 0;
|
||||||
|
|
||||||
/* Measurements interface */
|
/* Measurements interface */
|
||||||
virtual void meas_reset() = 0;
|
virtual void set_cells_to_meas(uint32_t earfcn, std::set<uint32_t>& pci) = 0;
|
||||||
virtual int meas_start(uint32_t earfcn, int pci = -1) = 0;
|
virtual void meas_stop() = 0;
|
||||||
virtual int meas_stop(uint32_t earfcn, int pci = -1) = 0;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
enum { CELL_FOUND = 0, CELL_NOT_FOUND, ERROR } found;
|
enum { CELL_FOUND = 0, CELL_NOT_FOUND, ERROR } found;
|
||||||
|
@ -606,8 +610,8 @@ public:
|
||||||
} cell_search_ret_t;
|
} cell_search_ret_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
srslte_cell_t cell;
|
uint32_t pci;
|
||||||
uint32_t earfcn;
|
uint32_t earfcn;
|
||||||
} phy_cell_t;
|
} phy_cell_t;
|
||||||
|
|
||||||
/* Cell search and selection procedures */
|
/* Cell search and selection procedures */
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
|
|
||||||
// MAC interface
|
// MAC interface
|
||||||
bool has_data(const uint32_t lcid);
|
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_buffer_state(const uint32_t lcid);
|
||||||
uint32_t get_total_mch_buffer_state(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);
|
int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);
|
||||||
|
|
|
@ -221,17 +221,17 @@ public:
|
||||||
|
|
||||||
bool suspend()
|
bool suspend()
|
||||||
{
|
{
|
||||||
if (is_suspended) {
|
if (suspended) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
is_suspended = true;
|
suspended = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pops all PDUs from queue and calls write_pdu() method for the bearer type
|
// Pops all PDUs from queue and calls write_pdu() method for the bearer type
|
||||||
bool resume()
|
bool resume()
|
||||||
{
|
{
|
||||||
if (!is_suspended) {
|
if (!suspended) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
pdu_t p;
|
pdu_t p;
|
||||||
|
@ -240,13 +240,13 @@ public:
|
||||||
write_pdu(p.payload, p.nof_bytes);
|
write_pdu(p.payload, p.nof_bytes);
|
||||||
free(p.payload);
|
free(p.payload);
|
||||||
}
|
}
|
||||||
is_suspended = false;
|
suspended = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_pdu_s(uint8_t* payload, uint32_t nof_bytes)
|
void write_pdu_s(uint8_t* payload, uint32_t nof_bytes)
|
||||||
{
|
{
|
||||||
if (is_suspended) {
|
if (suspended) {
|
||||||
queue_pdu(payload, nof_bytes);
|
queue_pdu(payload, nof_bytes);
|
||||||
} else {
|
} else {
|
||||||
write_pdu(payload, nof_bytes);
|
write_pdu(payload, nof_bytes);
|
||||||
|
@ -265,12 +265,13 @@ public:
|
||||||
|
|
||||||
// MAC interface
|
// MAC interface
|
||||||
virtual bool has_data() = 0;
|
virtual bool has_data() = 0;
|
||||||
|
bool is_suspended() { return suspended; };
|
||||||
virtual uint32_t get_buffer_state() = 0;
|
virtual uint32_t get_buffer_state() = 0;
|
||||||
virtual int read_pdu(uint8_t* payload, uint32_t nof_bytes) = 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;
|
virtual void write_pdu(uint8_t* payload, uint32_t nof_bytes) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_suspended = false;
|
bool suspended = false;
|
||||||
|
|
||||||
// Enqueues the PDU in the resume queue
|
// Enqueues the PDU in the resume queue
|
||||||
void queue_pdu(uint8_t* payload, uint32_t nof_bytes)
|
void queue_pdu(uint8_t* payload, uint32_t nof_bytes)
|
||||||
|
|
|
@ -260,6 +260,18 @@ bool rlc::has_data(uint32_t lcid)
|
||||||
|
|
||||||
return has_data;
|
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)
|
uint32_t rlc::get_buffer_state(uint32_t lcid)
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,7 +36,7 @@ using namespace asn1::rrc;
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
int basic_test()
|
int meas_obj_test()
|
||||||
{
|
{
|
||||||
srslte::log_filter log1("RRC");
|
srslte::log_filter log1("RRC");
|
||||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||||
|
@ -130,6 +130,6 @@ int basic_test()
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
TESTASSERT(basic_test() == 0);
|
TESTASSERT(meas_obj_test() == 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
using namespace asn1;
|
using namespace asn1;
|
||||||
using namespace asn1::rrc;
|
using namespace asn1::rrc;
|
||||||
|
|
||||||
int basic_test()
|
int meas_obj_test()
|
||||||
{
|
{
|
||||||
srslte::log_filter log1("RRC");
|
srslte::log_filter log1("RRC");
|
||||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||||
|
@ -100,6 +100,6 @@ int basic_test()
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
TESTASSERT(basic_test() == 0);
|
TESTASSERT(meas_obj_test() == 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ void basic_test_tx(rlc_am_lte* rlc, byte_buffer_t pdu_bufs[NBUFS])
|
||||||
assert(0 == rlc->get_buffer_state());
|
assert(0 == rlc->get_buffer_state());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool basic_test()
|
bool meas_obj_test()
|
||||||
{
|
{
|
||||||
srslte::log_filter log1("RLC_AM_1");
|
srslte::log_filter log1("RLC_AM_1");
|
||||||
srslte::log_filter log2("RLC_AM_2");
|
srslte::log_filter log2("RLC_AM_2");
|
||||||
|
@ -1647,7 +1647,7 @@ bool status_pdu_test()
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (basic_test()) {
|
if (meas_obj_test()) {
|
||||||
printf("basic_test failed\n");
|
printf("basic_test failed\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
};
|
};
|
||||||
|
|
|
@ -71,7 +71,7 @@ public:
|
||||||
uint32_t expected_sdu_len;
|
uint32_t expected_sdu_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
int basic_test()
|
int meas_obj_test()
|
||||||
{
|
{
|
||||||
srslte::log_filter log1("RLC_1");
|
srslte::log_filter log1("RLC_1");
|
||||||
srslte::log_filter log2("RLC_2");
|
srslte::log_filter log2("RLC_2");
|
||||||
|
@ -204,7 +204,7 @@ int basic_test()
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (basic_test()) {
|
if (meas_obj_test()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
byte_buffer_pool::get_instance()->cleanup();
|
byte_buffer_pool::get_instance()->cleanup();
|
||||||
|
|
|
@ -74,7 +74,7 @@ public:
|
||||||
rlc_um_lte rlc1, rlc2;
|
rlc_um_lte rlc1, rlc2;
|
||||||
};
|
};
|
||||||
|
|
||||||
int basic_test()
|
int meas_obj_test()
|
||||||
{
|
{
|
||||||
rlc_um_lte_test_context1 ctxt;
|
rlc_um_lte_test_context1 ctxt;
|
||||||
|
|
||||||
|
@ -392,7 +392,7 @@ int reassmble_test2()
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (basic_test()) {
|
if (meas_obj_test()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
byte_buffer_pool::get_instance()->cleanup();
|
byte_buffer_pool::get_instance()->cleanup();
|
||||||
|
|
|
@ -72,9 +72,8 @@ public:
|
||||||
cell_search_ret_t cell_search(phy_cell_t* cell) final;
|
cell_search_ret_t cell_search(phy_cell_t* cell) final;
|
||||||
bool cell_select(phy_cell_t* cell) final;
|
bool cell_select(phy_cell_t* cell) final;
|
||||||
|
|
||||||
void meas_reset() final;
|
void set_cells_to_meas(uint32_t earfcn, std::set<uint32_t>& pci) final;
|
||||||
int meas_start(uint32_t earfcn, int pci) final;
|
void meas_stop() final;
|
||||||
int meas_stop(uint32_t earfcn, int pci) final;
|
|
||||||
|
|
||||||
// also MAC interface
|
// also MAC interface
|
||||||
bool cell_is_camping() final;
|
bool cell_is_camping() final;
|
||||||
|
@ -120,10 +119,6 @@ public:
|
||||||
|
|
||||||
uint32_t get_current_tti() final;
|
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;
|
void start_plot() final;
|
||||||
|
|
||||||
const static int MAX_WORKERS = 4;
|
const static int MAX_WORKERS = 4;
|
||||||
|
|
|
@ -63,8 +63,8 @@ public:
|
||||||
float cur_pusch_power = 0.0f;
|
float cur_pusch_power = 0.0f;
|
||||||
float avg_rsrp[SRSLTE_MAX_CARRIERS] = {};
|
float avg_rsrp[SRSLTE_MAX_CARRIERS] = {};
|
||||||
float avg_rsrp_dbm[SRSLTE_MAX_CARRIERS] = {};
|
float avg_rsrp_dbm[SRSLTE_MAX_CARRIERS] = {};
|
||||||
float avg_rsrq_db = 0.0f;
|
float avg_rsrq_db[SRSLTE_MAX_CARRIERS] = {};
|
||||||
float avg_rssi_dbm = 0.0f;
|
float avg_rssi_dbm[SRSLTE_MAX_CARRIERS] = {};
|
||||||
float rx_gain_offset = 0.0f;
|
float rx_gain_offset = 0.0f;
|
||||||
float avg_snr_db_cqi[SRSLTE_MAX_CARRIERS] = {};
|
float avg_snr_db_cqi[SRSLTE_MAX_CARRIERS] = {};
|
||||||
float avg_noise[SRSLTE_MAX_CARRIERS] = {};
|
float avg_noise[SRSLTE_MAX_CARRIERS] = {};
|
||||||
|
|
|
@ -39,36 +39,33 @@ public:
|
||||||
~intra_measure();
|
~intra_measure();
|
||||||
void init(phy_common* common, rrc_interface_phy_lte* rrc, srslte::log* log_h);
|
void init(phy_common* common, rrc_interface_phy_lte* rrc, srslte::log* log_h);
|
||||||
void stop();
|
void stop();
|
||||||
void add_cell(int pci);
|
|
||||||
void rem_cell(int pci);
|
|
||||||
void set_primary_cell(uint32_t earfcn, srslte_cell_t cell);
|
void set_primary_cell(uint32_t earfcn, srslte_cell_t cell);
|
||||||
void clear_cells();
|
void set_cells_to_meas(std::set<uint32_t>& pci);
|
||||||
int get_offset(uint32_t pci);
|
void meas_stop();
|
||||||
void write(uint32_t tti, cf_t* data, uint32_t nsamples);
|
void write(uint32_t tti, cf_t* data, uint32_t nsamples);
|
||||||
|
uint32_t get_earfcn() { return current_earfcn; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void run_thread();
|
void run_thread();
|
||||||
const static int INTRA_FREQ_MEAS_PRIO = DEFAULT_PRIORITY + 5;
|
const static int INTRA_FREQ_MEAS_PRIO = DEFAULT_PRIORITY + 5;
|
||||||
|
|
||||||
scell_recv scell = {};
|
scell_recv scell = {};
|
||||||
rrc_interface_phy_lte* rrc = nullptr;
|
rrc_interface_phy_lte* rrc = nullptr;
|
||||||
srslte::log* log_h = nullptr;
|
srslte::log* log_h = nullptr;
|
||||||
phy_common* common = nullptr;
|
phy_common* common = nullptr;
|
||||||
uint32_t current_earfcn = 0;
|
uint32_t current_earfcn = 0;
|
||||||
uint32_t current_sflen = 0;
|
uint32_t current_sflen = 0;
|
||||||
srslte_cell_t primary_cell = {};
|
srslte_cell_t serving_cell = {};
|
||||||
std::vector<int> active_pci;
|
std::set<uint32_t> active_pci = {};
|
||||||
|
std::mutex active_pci_mutex = {};
|
||||||
|
|
||||||
srslte::tti_sync_cv tti_sync;
|
srslte::tti_sync_cv tti_sync;
|
||||||
|
|
||||||
cf_t* search_buffer = nullptr;
|
cf_t* search_buffer = nullptr;
|
||||||
|
|
||||||
scell_recv::cell_info_t info[scell_recv::MAX_CELLS] = {};
|
|
||||||
|
|
||||||
bool running = false;
|
bool running = false;
|
||||||
bool receive_enabled = false;
|
bool receive_enabled = false;
|
||||||
bool receiving = false;
|
bool receiving = false;
|
||||||
uint32_t measure_tti = 0;
|
|
||||||
uint32_t receive_cnt = 0;
|
uint32_t receive_cnt = 0;
|
||||||
srslte_ringbuffer_t ring_buffer = {};
|
srslte_ringbuffer_t ring_buffer = {};
|
||||||
|
|
||||||
|
|
|
@ -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 <srslte/srslte.h>
|
|
||||||
|
|
||||||
#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
|
|
|
@ -22,10 +22,10 @@
|
||||||
#ifndef SRSUE_SCELL_RECV_H
|
#ifndef SRSUE_SCELL_RECV_H
|
||||||
#define SRSUE_SCELL_RECV_H
|
#define SRSUE_SCELL_RECV_H
|
||||||
|
|
||||||
|
#include "srsue/hdr/phy/phy_common.h"
|
||||||
|
#include <set>
|
||||||
#include <srslte/srslte.h>
|
#include <srslte/srslte.h>
|
||||||
|
|
||||||
#include "measure.h"
|
|
||||||
|
|
||||||
namespace srsue {
|
namespace srsue {
|
||||||
namespace scell {
|
namespace scell {
|
||||||
|
|
||||||
|
@ -33,21 +33,10 @@ namespace scell {
|
||||||
class scell_recv
|
class scell_recv
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const static int MAX_CELLS = 8;
|
void init(srslte::log* log_h, uint32_t max_sf_window);
|
||||||
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 deinit();
|
void deinit();
|
||||||
void reset();
|
void reset();
|
||||||
int find_cells(cf_t* input_buffer,
|
std::set<uint32_t> find_cells(const cf_t* input_buffer, const srslte_cell_t serving_cell, const uint32_t nof_sf);
|
||||||
float rx_gain_offset,
|
|
||||||
srslte_cell_t current_cell,
|
|
||||||
uint32_t nof_sf,
|
|
||||||
cell_info_t found_cells[MAX_CELLS]);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 36.133 9.1.2.1 for band 7
|
// 36.133 9.1.2.1 for band 7
|
||||||
|
@ -57,9 +46,7 @@ private:
|
||||||
srslte::log* log_h;
|
srslte::log* log_h;
|
||||||
srslte_sync_t sync_find;
|
srslte_sync_t sync_find;
|
||||||
|
|
||||||
bool sic_pss_enabled;
|
|
||||||
uint32_t current_fft_sz;
|
uint32_t current_fft_sz;
|
||||||
measure measure_p;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace scell
|
} // namespace scell
|
||||||
|
|
|
@ -70,9 +70,9 @@ public:
|
||||||
bool cell_is_camping();
|
bool cell_is_camping();
|
||||||
|
|
||||||
// RRC interface for controlling the neighbour cell measurement
|
// RRC interface for controlling the neighbour cell measurement
|
||||||
void meas_reset();
|
void set_cells_to_meas(uint32_t earfcn, std::set<uint32_t>& pci);
|
||||||
int meas_start(uint32_t earfcn, int pci);
|
void set_inter_frequency_measurement(uint32_t cc_idx, uint32_t earfcn_, srslte_cell_t cell_);
|
||||||
int meas_stop(uint32_t earfcn, int pci);
|
void meas_stop();
|
||||||
|
|
||||||
// from chest_feedback_itf
|
// from chest_feedback_itf
|
||||||
void in_sync() final;
|
void in_sync() final;
|
||||||
|
@ -172,7 +172,7 @@ private:
|
||||||
// Objects for internal use
|
// Objects for internal use
|
||||||
search search_p;
|
search search_p;
|
||||||
sfn_sync sfn_p;
|
sfn_sync sfn_p;
|
||||||
scell::intra_measure intra_freq_meas;
|
std::vector<std::unique_ptr<scell::intra_measure> > intra_freq_meas;
|
||||||
|
|
||||||
uint32_t current_sflen = 0;
|
uint32_t current_sflen = 0;
|
||||||
int next_offset = 0; // Sample offset triggered by Time aligment commands
|
int next_offset = 0; // Sample offset triggered by Time aligment commands
|
||||||
|
|
|
@ -41,7 +41,6 @@ public:
|
||||||
|
|
||||||
bool init(srslte::log* log_h,
|
bool init(srslte::log* log_h,
|
||||||
mac_interface_rrc::ue_rnti_t* rntis,
|
mac_interface_rrc::ue_rnti_t* rntis,
|
||||||
srslte::timer_handler::unique_timer* timer_aligment_timer,
|
|
||||||
demux* demux_unit);
|
demux* demux_unit);
|
||||||
void reset();
|
void reset();
|
||||||
void start_pcap(srslte::mac_pcap* pcap_);
|
void start_pcap(srslte::mac_pcap* pcap_);
|
||||||
|
@ -121,7 +120,6 @@ private:
|
||||||
|
|
||||||
std::vector<dl_harq_process> proc;
|
std::vector<dl_harq_process> proc;
|
||||||
dl_harq_process bcch_proc;
|
dl_harq_process bcch_proc;
|
||||||
srslte::timer_handler::unique_timer* timer_aligment_timer = nullptr;
|
|
||||||
demux* demux_unit;
|
demux* demux_unit;
|
||||||
srslte::log* log_h;
|
srslte::log* log_h;
|
||||||
srslte::mac_pcap* pcap;
|
srslte::mac_pcap* pcap;
|
||||||
|
|
|
@ -68,9 +68,9 @@ namespace srsue {
|
||||||
class cell_t
|
class cell_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool is_valid() { return phy_cell.earfcn != 0 && srslte_cell_isvalid(&phy_cell.cell); }
|
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.cell.id); }
|
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.cell.id; }
|
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
|
// 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 greater(cell_t* x) { return rsrp > x->rsrp || std::isnan(rsrp); }
|
||||||
bool plmn_equals(asn1::rrc::plmn_id_s plmn_id)
|
bool plmn_equals(asn1::rrc::plmn_id_s plmn_id)
|
||||||
|
@ -113,29 +113,27 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_t()
|
cell_t() { cell_t({0, 0}); }
|
||||||
{
|
|
||||||
phy_interface_rrc_lte::phy_cell_t tmp = {};
|
cell_t(phy_interface_rrc_lte::phy_cell_t phy_cell_)
|
||||||
cell_t(tmp, 0);
|
|
||||||
}
|
|
||||||
cell_t(phy_interface_rrc_lte::phy_cell_t phy_cell, float rsrp_)
|
|
||||||
{
|
{
|
||||||
gettimeofday(&last_update, nullptr);
|
gettimeofday(&last_update, nullptr);
|
||||||
this->has_valid_sib1 = false;
|
has_valid_sib1 = false;
|
||||||
this->has_valid_sib2 = false;
|
has_valid_sib2 = false;
|
||||||
this->has_valid_sib3 = false;
|
has_valid_sib3 = false;
|
||||||
this->has_valid_sib13 = false;
|
has_valid_sib13 = false;
|
||||||
this->phy_cell = phy_cell;
|
phy_cell = phy_cell_;
|
||||||
rsrp = rsrp_;
|
rsrp = NAN;
|
||||||
bzero(&sib1, sizeof(sib1));
|
rsrq = NAN;
|
||||||
bzero(&sib2, sizeof(sib2));
|
sib1 = {};
|
||||||
bzero(&sib3, sizeof(sib3));
|
sib2 = {};
|
||||||
bzero(&sib13, sizeof(sib13));
|
sib3 = {};
|
||||||
|
sib13 = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_earfcn() { return phy_cell.earfcn; }
|
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_)
|
void set_rsrp(float rsrp_)
|
||||||
{
|
{
|
||||||
|
@ -144,8 +142,15 @@ public:
|
||||||
}
|
}
|
||||||
gettimeofday(&last_update, nullptr);
|
gettimeofday(&last_update, nullptr);
|
||||||
}
|
}
|
||||||
|
void set_rsrq(float rsrq_)
|
||||||
|
{
|
||||||
|
if (!std::isnan(rsrq_)) {
|
||||||
|
rsrq = rsrq_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float get_rsrp() { return rsrp; }
|
float get_rsrp() { return rsrp; }
|
||||||
|
float get_rsrq() { return rsrq; }
|
||||||
|
|
||||||
void set_sib1(asn1::rrc::sib_type1_s* sib1_)
|
void set_sib1(asn1::rrc::sib_type1_s* sib1_)
|
||||||
{
|
{
|
||||||
|
@ -244,7 +249,13 @@ public:
|
||||||
std::string print()
|
std::string print()
|
||||||
{
|
{
|
||||||
char buf[256];
|
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};
|
return std::string{buf};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +269,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float rsrp = NAN;
|
float rsrp = NAN;
|
||||||
|
float rsrq = NAN;
|
||||||
|
|
||||||
struct timeval last_update = {};
|
struct timeval last_update = {};
|
||||||
|
|
||||||
|
@ -322,7 +334,7 @@ public:
|
||||||
// PHY interface
|
// PHY interface
|
||||||
void in_sync() final;
|
void in_sync() final;
|
||||||
void out_of_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<phy_meas_t>& meas);
|
||||||
|
|
||||||
// MAC interface
|
// MAC interface
|
||||||
void ho_ra_completed(bool ra_successful);
|
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,
|
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);
|
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:
|
private:
|
||||||
typedef struct {
|
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;
|
srslte::unique_byte_buffer_t pdu;
|
||||||
uint16_t lcid;
|
uint16_t lcid;
|
||||||
} cmd_msg_t;
|
} cmd_msg_t;
|
||||||
|
@ -448,19 +465,18 @@ private:
|
||||||
std::vector<unique_cell_t> neighbour_cells;
|
std::vector<unique_cell_t> neighbour_cells;
|
||||||
unique_cell_t serving_cell = nullptr;
|
unique_cell_t serving_cell = nullptr;
|
||||||
void set_serving_cell(uint32_t cell_idx);
|
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);
|
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);
|
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);
|
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_meas_t meas);
|
||||||
bool add_neighbour_cell(phy_interface_rrc_lte::phy_cell_t phy_cell, float rsrp);
|
|
||||||
bool add_neighbour_cell(unique_cell_t new_cell);
|
bool add_neighbour_cell(unique_cell_t new_cell);
|
||||||
|
void log_neighbour_cells();
|
||||||
void sort_neighbour_cells();
|
void sort_neighbour_cells();
|
||||||
void clean_neighbours();
|
void clean_neighbours();
|
||||||
void delete_last_neighbour();
|
void delete_last_neighbour();
|
||||||
std::string print_neighbour_cells();
|
std::string print_neighbour_cells();
|
||||||
|
std::set<uint32_t> get_neighbour_pcis(uint32_t earfcn);
|
||||||
|
|
||||||
bool initiated = false;
|
bool initiated = false;
|
||||||
asn1::rrc::reest_cause_e m_reest_cause = asn1::rrc::reest_cause_e::nulltype;
|
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_started = false;
|
||||||
bool reestablishment_successful = false;
|
bool reestablishment_successful = false;
|
||||||
|
|
||||||
// Measurements sub-class
|
// Process HO completition in the background
|
||||||
class rrc_meas
|
void process_ho_ra_completed(bool ra_successful);
|
||||||
{
|
|
||||||
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:
|
// Measurements private subclass
|
||||||
const static int NOF_MEASUREMENTS = 3;
|
class rrc_meas;
|
||||||
|
rrc_meas* measurements;
|
||||||
|
|
||||||
typedef enum { RSRP = 0, RSRQ = 1, BOTH = 2 } quantity_t;
|
// Interface from rrc_meas
|
||||||
|
void send_srb1_msg(const asn1::rrc::ul_dcch_msg_s& msg);
|
||||||
|
std::set<uint32_t> 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();
|
||||||
|
|
||||||
typedef struct {
|
void process_cell_meas();
|
||||||
uint32_t pci;
|
void process_new_cell_meas(std::vector<phy_meas_t>& meas);
|
||||||
float q_offset;
|
srslte::block_queue<std::vector<phy_meas_t> > cell_meas_q;
|
||||||
} meas_cell_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t earfcn;
|
|
||||||
float q_offset;
|
|
||||||
std::map<uint32_t, meas_cell_t> meas_cells;
|
|
||||||
std::map<uint32_t, meas_cell_t> 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<uint32_t, meas_value_t> cell_values; // Value for each PCI in this object
|
|
||||||
} meas_t;
|
|
||||||
|
|
||||||
std::map<uint32_t, meas_obj_t> objects;
|
|
||||||
std::map<uint32_t, report_cfg_t> reports_cfg;
|
|
||||||
std::map<uint32_t, meas_t> 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<uint32_t, meas_t>::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_t> phy_meas_q;
|
|
||||||
|
|
||||||
// Cell selection/reselection functions/variables
|
// Cell selection/reselection functions/variables
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -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 <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace srsue {
|
||||||
|
|
||||||
|
using namespace asn1::rrc;
|
||||||
|
|
||||||
|
typedef std::vector<phy_interface_rrc_lte::phy_cell_t> 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<uint32_t, var_meas_report> 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<meas_obj_eutra_s> 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<uint32_t, meas_id_to_add_mod_s> measIdList; // Uses MeasId as key
|
||||||
|
std::map<uint32_t, meas_obj_eutra_s> measObjectsList; // Uses MeasObjectId as key
|
||||||
|
std::map<uint32_t, report_cfg_eutra_s> 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<uint32_t, std::map<uint32_t, cell_trigger_state> > 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<cells_to_add_mod_s>::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_
|
|
@ -131,6 +131,7 @@ private:
|
||||||
uint32_t neigh_index;
|
uint32_t neigh_index;
|
||||||
srslte::proc_future_t<phy_interface_rrc_lte::cell_search_ret_t> cell_search_fut;
|
srslte::proc_future_t<phy_interface_rrc_lte::cell_search_ret_t> cell_search_fut;
|
||||||
srslte::proc_future_t<void> serv_cell_cfg_fut;
|
srslte::proc_future_t<void> serv_cell_cfg_fut;
|
||||||
|
bool discard_serving = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class rrc::plmn_search_proc
|
class rrc::plmn_search_proc
|
||||||
|
@ -211,6 +212,7 @@ public:
|
||||||
static const char* name() { return "Go Idle"; }
|
static const char* name() { return "Go Idle"; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool wait = false;
|
||||||
rrc* rrc_ptr;
|
rrc* rrc_ptr;
|
||||||
static const uint32_t rlc_flush_timeout = 2000;
|
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 init(asn1::rrc::reest_cause_e cause);
|
||||||
srslte::proc_outcome_t step();
|
srslte::proc_outcome_t step();
|
||||||
static const char* name() { return "Connection re-establishment"; }
|
static const char* name() { return "Connection re-establishment"; }
|
||||||
|
uint32_t get_source_earfcn() const { return reest_source_freq; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class state_t { cell_reselection, cell_configuration } state;
|
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;
|
asn1::rrc::reest_cause_e reest_cause = asn1::rrc::reest_cause_e::nulltype;
|
||||||
uint16_t reest_rnti = 0;
|
uint16_t reest_rnti = 0;
|
||||||
uint16_t reest_source_pci = 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_reselection();
|
||||||
srslte::proc_outcome_t step_cell_configuration();
|
srslte::proc_outcome_t step_cell_configuration();
|
||||||
|
|
|
@ -77,10 +77,7 @@ public:
|
||||||
// RRC interface for PHY
|
// RRC interface for PHY
|
||||||
void in_sync() final;
|
void in_sync() final;
|
||||||
void out_of_sync() final;
|
void out_of_sync() final;
|
||||||
void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn = -1, int pci = -1)
|
void new_cell_meas(std::vector<phy_meas_t>& meas) { rrc.new_cell_meas(meas); }
|
||||||
{
|
|
||||||
rrc.new_phy_meas(rsrp, rsrq, tti, earfcn, pci);
|
|
||||||
};
|
|
||||||
|
|
||||||
// MAC Interface for PHY
|
// MAC Interface for PHY
|
||||||
uint16_t get_dl_sched_rnti(uint32_t tti) { return mac.get_dl_sched_rnti(tti); }
|
uint16_t get_dl_sched_rnti(uint32_t tti) { return mac.get_dl_sched_rnti(tti); }
|
||||||
|
|
|
@ -302,10 +302,6 @@ static int parse_args(all_args_t* args, int argc, char* argv[])
|
||||||
bpo::value<uint32_t>(&args->phy.cfo_loop_pss_conv)->default_value(DEFAULT_PSS_STABLE_TIMEOUT),
|
bpo::value<uint32_t>(&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.")
|
"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<bool>(&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",
|
("phy.interpolate_subframe_enabled",
|
||||||
bpo::value<bool>(&args->phy.interpolate_subframe_enabled)->default_value(false),
|
bpo::value<bool>(&args->phy.interpolate_subframe_enabled)->default_value(false),
|
||||||
"Interpolates in the time domain the channel estimates within 1 subframe.")
|
"Interpolates in the time domain the channel estimates within 1 subframe.")
|
||||||
|
|
|
@ -541,10 +541,11 @@ void cc_worker::update_measurements()
|
||||||
// Average RSRQ over DEFAULT_MEAS_PERIOD_MS then sent to RRC
|
// Average RSRQ over DEFAULT_MEAS_PERIOD_MS then sent to RRC
|
||||||
float rsrq_db = ue_dl.chest_res.rsrq_db;
|
float rsrq_db = ue_dl.chest_res.rsrq_db;
|
||||||
if (std::isnormal(rsrq_db)) {
|
if (std::isnormal(rsrq_db)) {
|
||||||
if (!(CURRENT_TTI % phy->pcell_report_period) || !std::isnormal(phy->avg_rsrq_db)) {
|
if (!(CURRENT_TTI % phy->pcell_report_period) || !std::isnormal(phy->avg_rsrq_db[cc_idx])) {
|
||||||
phy->avg_rsrq_db = rsrq_db;
|
phy->avg_rsrq_db[cc_idx] = rsrq_db;
|
||||||
} else {
|
} 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
|
// Store metrics
|
||||||
dl_metrics.n = phy->avg_noise[cc_idx];
|
dl_metrics.n = phy->avg_noise[cc_idx];
|
||||||
dl_metrics.rsrp = phy->avg_rsrp_dbm[cc_idx];
|
dl_metrics.rsrp = phy->avg_rsrp_dbm[cc_idx];
|
||||||
dl_metrics.rsrq = phy->avg_rsrq_db;
|
dl_metrics.rsrq = phy->avg_rsrq_db[cc_idx];
|
||||||
dl_metrics.rssi = phy->avg_rssi_dbm;
|
dl_metrics.rssi = phy->avg_rssi_dbm[cc_idx];
|
||||||
dl_metrics.pathloss = phy->pathloss[cc_idx];
|
dl_metrics.pathloss = phy->pathloss[cc_idx];
|
||||||
dl_metrics.sinr = phy->avg_snr_db_cqi[cc_idx];
|
dl_metrics.sinr = phy->avg_snr_db_cqi[cc_idx];
|
||||||
dl_metrics.sync_err = ue_dl.chest_res.sync_error;
|
dl_metrics.sync_err = ue_dl.chest_res.sync_error;
|
||||||
|
|
|
@ -253,7 +253,7 @@ void phy::get_metrics(phy_metrics_t* m)
|
||||||
{
|
{
|
||||||
uint32_t dl_earfcn = 0;
|
uint32_t dl_earfcn = 0;
|
||||||
srslte_cell_t cell = {};
|
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].pci = cell.id;
|
||||||
m->info[0].dl_earfcn = dl_earfcn;
|
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<uint32_t>& 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);
|
sfsync.meas_stop();
|
||||||
}
|
|
||||||
|
|
||||||
int phy::meas_stop(uint32_t earfcn, int pci)
|
|
||||||
{
|
|
||||||
return sfsync.meas_stop(earfcn, pci);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool phy::cell_select(phy_cell_t* cell)
|
bool phy::cell_select(phy_cell_t* cell)
|
||||||
|
@ -356,25 +351,6 @@ float phy::get_pathloss_db()
|
||||||
return common.cur_pathloss;
|
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)
|
void phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm)
|
||||||
{
|
{
|
||||||
n_ta = 0;
|
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_);
|
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) {
|
if (cc_idx == 0) {
|
||||||
prach_cfg = config_.prach_cfg;
|
prach_cfg = config_.prach_cfg;
|
||||||
} else if (cell_info) {
|
} else if (cell_info) {
|
||||||
|
|
|
@ -667,15 +667,14 @@ void phy_common::reset()
|
||||||
cur_pusch_power = 0;
|
cur_pusch_power = 0;
|
||||||
sr_last_tx_tti = -1;
|
sr_last_tx_tti = -1;
|
||||||
cur_pusch_power = 0;
|
cur_pusch_power = 0;
|
||||||
|
pcell_report_period = 20;
|
||||||
|
|
||||||
ZERO_OBJECT(pathloss);
|
ZERO_OBJECT(pathloss);
|
||||||
ZERO_OBJECT(avg_snr_db_cqi);
|
ZERO_OBJECT(avg_snr_db_cqi);
|
||||||
ZERO_OBJECT(avg_rsrp);
|
ZERO_OBJECT(avg_rsrp);
|
||||||
ZERO_OBJECT(avg_rsrp_dbm);
|
ZERO_OBJECT(avg_rsrp_dbm);
|
||||||
|
ZERO_OBJECT(avg_rsrq_db);
|
||||||
ZERO_OBJECT(scell_cfg);
|
ZERO_OBJECT(scell_cfg);
|
||||||
avg_rsrq_db = 0;
|
|
||||||
|
|
||||||
pcell_report_period = 20;
|
|
||||||
|
|
||||||
ZERO_OBJECT(pending_dl_ack);
|
ZERO_OBJECT(pending_dl_ack);
|
||||||
ZERO_OBJECT(pending_dl_dai);
|
ZERO_OBJECT(pending_dl_dai);
|
||||||
ZERO_OBJECT(pending_ul_ack);
|
ZERO_OBJECT(pending_ul_ack);
|
||||||
|
|
|
@ -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_tol,
|
||||||
worker_com->args->cfo_loop_pss_conv);
|
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
|
// Disable CP based CFO estimation during find
|
||||||
if (cfo != 0) {
|
if (cfo != 0) {
|
||||||
q->cfo_current_value = cfo / 15000;
|
q->cfo_current_value = cfo / 15000;
|
||||||
|
|
|
@ -45,18 +45,18 @@ intra_measure::~intra_measure()
|
||||||
free(search_buffer);
|
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->rrc = rrc_;
|
||||||
this->log_h = log_h;
|
this->log_h = log_h_;
|
||||||
this->common = common;
|
this->common = common_;
|
||||||
receive_enabled = false;
|
receive_enabled = false;
|
||||||
|
|
||||||
// Initialise Reference signal measurement
|
// Initialise Reference signal measurement
|
||||||
srslte_refsignal_dl_sync_init(&refsignal_dl_sync);
|
srslte_refsignal_dl_sync_init(&refsignal_dl_sync);
|
||||||
|
|
||||||
// Start scell
|
// 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 =
|
search_buffer =
|
||||||
(cf_t*)srslte_vec_malloc(common->args->intra_freq_meas_len_ms * SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB) * sizeof(cf_t));
|
(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;
|
this->current_earfcn = earfcn;
|
||||||
current_sflen = (uint32_t)SRSLTE_SF_LEN_PRB(cell.nof_prb);
|
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;
|
receive_enabled = false;
|
||||||
receiving = false;
|
receiving = false;
|
||||||
receive_cnt = 0;
|
receive_cnt = 0;
|
||||||
srslte_ringbuffer_reset(&ring_buffer);
|
srslte_ringbuffer_reset(&ring_buffer);
|
||||||
}
|
if (log_h) {
|
||||||
|
log_h->info("INTRA: Disabled neighbour cell search for EARFCN %d\n", get_earfcn());
|
||||||
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)
|
void intra_measure::set_cells_to_meas(std::set<uint32_t>& pci)
|
||||||
{
|
{
|
||||||
for (auto& i : info) {
|
active_pci_mutex.lock();
|
||||||
if (i.pci == pci) {
|
active_pci = pci;
|
||||||
return i.offset;
|
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);
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void intra_measure::rem_cell(int 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void intra_measure::write(uint32_t tti, cf_t* data, uint32_t nsamples)
|
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) {
|
if ((tti % common->args->intra_freq_meas_period_ms) == 0) {
|
||||||
receiving = true;
|
receiving = true;
|
||||||
receive_cnt = 0;
|
receive_cnt = 0;
|
||||||
measure_tti = tti;
|
|
||||||
srslte_ringbuffer_reset(&ring_buffer);
|
srslte_ringbuffer_reset(&ring_buffer);
|
||||||
}
|
}
|
||||||
if (receiving) {
|
if (receiving) {
|
||||||
|
@ -157,6 +131,8 @@ void intra_measure::write(uint32_t tti, cf_t* data, uint32_t nsamples)
|
||||||
|
|
||||||
void intra_measure::run_thread()
|
void intra_measure::run_thread()
|
||||||
{
|
{
|
||||||
|
std::set<uint32_t> cells_to_measure = {};
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
if (running) {
|
if (running) {
|
||||||
tti_sync.wait();
|
tti_sync.wait();
|
||||||
|
@ -164,77 +140,62 @@ void intra_measure::run_thread()
|
||||||
|
|
||||||
if (running) {
|
if (running) {
|
||||||
|
|
||||||
|
active_pci_mutex.lock();
|
||||||
|
cells_to_measure = active_pci;
|
||||||
|
active_pci_mutex.unlock();
|
||||||
|
|
||||||
// Read data from buffer and find cells in it
|
// Read data from buffer and find cells in it
|
||||||
srslte_ringbuffer_read(
|
srslte_ringbuffer_read(
|
||||||
&ring_buffer, search_buffer, common->args->intra_freq_meas_len_ms * current_sflen * sizeof(cf_t));
|
&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<uint32_t> 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;
|
receiving = false;
|
||||||
|
|
||||||
// Look for other cells not found automatically
|
std::vector<rrc_interface_phy_lte::phy_meas_t> neigbhour_cells = {};
|
||||||
// Using Cell Reference signal synchronization for all known active PCI
|
|
||||||
for (auto q : active_pci) {
|
// Use Cell Reference signal to measure cells in the time domain for all known active PCI
|
||||||
srslte_cell_t cell = primary_cell;
|
for (auto id : cells_to_measure) {
|
||||||
cell.id = q;
|
// 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_set_cell(&refsignal_dl_sync, cell);
|
||||||
srslte_refsignal_dl_sync_run(
|
srslte_refsignal_dl_sync_run(
|
||||||
&refsignal_dl_sync, search_buffer, common->args->intra_freq_meas_len_ms * current_sflen);
|
&refsignal_dl_sync, search_buffer, common->args->intra_freq_meas_len_ms * current_sflen);
|
||||||
|
|
||||||
if (refsignal_dl_sync.found) {
|
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",
|
rrc_interface_phy_lte::phy_meas_t m = {};
|
||||||
cell.id,
|
m.pci = cell.id;
|
||||||
refsignal_dl_sync.rsrp_dBfs,
|
m.earfcn = current_earfcn;
|
||||||
refsignal_dl_sync.rsrq_dB,
|
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.peak_index,
|
||||||
refsignal_dl_sync.cfo_Hz);
|
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
|
// Send measurements to RRC
|
||||||
for (int i = 0; i < found_cells; i++) {
|
if (neigbhour_cells.size() > 0) {
|
||||||
rrc->new_phy_meas(info[i].rsrp, info[i].rsrq, measure_tti, current_earfcn, info[i].pci);
|
rrc->new_cell_meas(neigbhour_cells);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
|
@ -21,19 +21,6 @@
|
||||||
|
|
||||||
#include "srsue/hdr/phy/scell/scell_recv.h"
|
#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 srsue {
|
||||||
namespace scell {
|
namespace scell {
|
||||||
|
|
||||||
|
@ -41,10 +28,9 @@ namespace scell {
|
||||||
* Secondary cell receiver
|
* 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;
|
log_h = _log_h;
|
||||||
sic_pss_enabled = _sic_pss_enabled;
|
|
||||||
|
|
||||||
// and a separate ue_sync instance
|
// 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);
|
sf_buffer[0] = (cf_t*)srslte_vec_malloc(sizeof(cf_t) * max_sf_size);
|
||||||
if (!sf_buffer[0]) {
|
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;
|
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.
|
// 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)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
srslte_sync_set_sss_algorithm(&sync_find, SSS_FULL);
|
srslte_sync_set_sss_algorithm(&sync_find, SSS_FULL);
|
||||||
|
@ -92,179 +77,86 @@ void scell_recv::deinit()
|
||||||
void scell_recv::reset()
|
void scell_recv::reset()
|
||||||
{
|
{
|
||||||
current_fft_sz = 0;
|
current_fft_sz = 0;
|
||||||
measure_p.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int scell_recv::find_cells(cf_t* input_buffer,
|
std::set<uint32_t>
|
||||||
float rx_gain_offset,
|
scell_recv::find_cells(const cf_t* input_buffer, const srslte_cell_t serving_cell, const uint32_t nof_sf)
|
||||||
srslte_cell_t cell,
|
|
||||||
uint32_t nof_sf,
|
|
||||||
cell_info_t cells[MAX_CELLS])
|
|
||||||
{
|
{
|
||||||
uint32_t fft_sz = srslte_symbol_sz(cell.nof_prb);
|
std::set<uint32_t> found_cell_ids = {};
|
||||||
|
|
||||||
|
uint32_t fft_sz = srslte_symbol_sz(serving_cell.nof_prb);
|
||||||
uint32_t sf_len = SRSLTE_SF_LEN(fft_sz);
|
uint32_t sf_len = SRSLTE_SF_LEN(fft_sz);
|
||||||
|
|
||||||
if (fft_sz != current_fft_sz) {
|
if (fft_sz != current_fft_sz) {
|
||||||
if (srslte_sync_resize(&sync_find, nof_sf * sf_len, 5 * sf_len, 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);
|
log_h->error("Error resizing sync nof_sf=%d, sf_len=%d, fft_sz=%d\n", nof_sf, sf_len, fft_sz);
|
||||||
return SRSLTE_ERROR;
|
return found_cell_ids;
|
||||||
}
|
}
|
||||||
current_fft_sz = fft_sz;
|
current_fft_sz = fft_sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nof_cells = 0;
|
|
||||||
uint32_t peak_idx = 0;
|
uint32_t peak_idx = 0;
|
||||||
uint32_t sf_idx = 0;
|
|
||||||
int cell_id = 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++) {
|
for (uint32_t n_id_2 = 0; n_id_2 < 3; n_id_2++) {
|
||||||
|
|
||||||
found_cell.id = 10000;
|
if (n_id_2 != (serving_cell.id % 3)) {
|
||||||
|
|
||||||
if (n_id_2 != (cell.id % 3) || sic_pss_enabled) {
|
|
||||||
srslte_sync_set_N_id_2(&sync_find, n_id_2);
|
srslte_sync_set_N_id_2(&sync_find, n_id_2);
|
||||||
|
|
||||||
srslte_sync_find_ret_t sync_res;
|
srslte_sync_find_ret_t sync_res;
|
||||||
|
|
||||||
do {
|
srslte_sync_reset(&sync_find);
|
||||||
srslte_sync_reset(&sync_find);
|
srslte_sync_cfo_reset(&sync_find);
|
||||||
srslte_sync_cfo_reset(&sync_find);
|
|
||||||
|
|
||||||
sync_res = SRSLTE_SYNC_NOFOUND;
|
sync_res = SRSLTE_SYNC_NOFOUND;
|
||||||
bool sss_detected = false;
|
bool sss_detected = false;
|
||||||
cell_id = 0;
|
cell_id = 0;
|
||||||
float max_peak = -1;
|
float max_peak = -1;
|
||||||
uint32_t max_sf5 = 0;
|
|
||||||
uint32_t max_sf_idx = 0;
|
|
||||||
|
|
||||||
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++) {
|
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);
|
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",
|
if (sync_res == SRSLTE_SYNC_ERROR) {
|
||||||
n_id_2,
|
log_h->error("INTRA: Error calling sync_find()\n");
|
||||||
sf5_cnt,
|
return found_cell_ids;
|
||||||
nof_sf / 5,
|
}
|
||||||
sync_res,
|
|
||||||
srslte_sync_get_sf_idx(&sync_find),
|
|
||||||
peak_idx,
|
|
||||||
sync_find.peak_value,
|
|
||||||
srslte_sync_sss_detected(&sync_find));
|
|
||||||
|
|
||||||
if (sync_find.peak_value > max_peak && sync_res == SRSLTE_SYNC_FOUND &&
|
if (sync_find.peak_value > max_peak && sync_res == SRSLTE_SYNC_FOUND && srslte_sync_sss_detected(&sync_find)) {
|
||||||
srslte_sync_sss_detected(&sync_find)) {
|
|
||||||
max_sf5 = sf5_cnt;
|
|
||||||
max_sf_idx = srslte_sync_get_sf_idx(&sync_find);
|
|
||||||
|
|
||||||
// Uses the cell ID from the highest SSS correlation peak
|
// Uses the cell ID from the highest SSS correlation peak
|
||||||
if (sss_correlation_peak_max < srslte_sync_sss_correlation_peak(&sync_find)) {
|
if (sss_correlation_peak_max < srslte_sync_sss_correlation_peak(&sync_find)) {
|
||||||
// Set the cell ID
|
// Set the cell ID
|
||||||
cell_id = srslte_sync_get_cell_id(&sync_find);
|
cell_id = srslte_sync_get_cell_id(&sync_find);
|
||||||
|
|
||||||
// Update the maximum value
|
// Update the maximum value
|
||||||
sss_correlation_peak_max = srslte_sync_sss_correlation_peak(&sync_find);
|
sss_correlation_peak_max = srslte_sync_sss_correlation_peak(&sync_find);
|
||||||
}
|
|
||||||
sss_detected = true;
|
|
||||||
}
|
}
|
||||||
|
sss_detected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the SSS was not detected, the cell id is not reliable. So, consider no sync found
|
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, "
|
||||||
if (!sss_detected) {
|
"sss_detected=%d\n",
|
||||||
sync_res = SRSLTE_SYNC_NOFOUND;
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
switch (sync_res) {
|
// If the SSS was not detected, the serving_cell id is not reliable. So, consider no sync found
|
||||||
case SRSLTE_SYNC_ERROR:
|
if (sync_res == SRSLTE_SYNC_FOUND && sss_detected && cell_id >= 0) {
|
||||||
return SRSLTE_ERROR;
|
// We have found a new cell, add to the list
|
||||||
ERROR("Error finding correlation peak\n");
|
found_cell_ids.insert((uint32_t)cell_id);
|
||||||
return SRSLTE_ERROR;
|
log_h->debug("INTRA: Detected new cell_id=%d using PSS/SSS\n", cell_id);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nof_cells;
|
return found_cell_ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace scell
|
} // namespace scell
|
||||||
|
|
|
@ -297,7 +297,7 @@ void sf_worker::update_measurements()
|
||||||
30)
|
30)
|
||||||
: 0;
|
: 0;
|
||||||
if (std::isnormal(rssi_dbm)) {
|
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) {
|
if (!rssi_read_cnt) {
|
||||||
|
@ -310,27 +310,30 @@ void sf_worker::update_measurements()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run measurements in all carriers
|
// Run measurements in all carriers
|
||||||
|
std::vector<rrc_interface_phy_lte::phy_meas_t> serving_cells = {};
|
||||||
for (uint32_t cc_idx = 0; cc_idx < cc_workers.size(); cc_idx++) {
|
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
|
// Update measurement of the Component Carrier
|
||||||
cc_workers[cc_idx]->update_measurements();
|
cc_workers[cc_idx]->update_measurements();
|
||||||
|
|
||||||
// Send measurements
|
// Send measurements for serving cells
|
||||||
if ((tti % phy->pcell_report_period) == phy->pcell_report_period - 1) {
|
if (active && ((tti % phy->pcell_report_period) == phy->pcell_report_period - 1)) {
|
||||||
if (cc_idx == 0) {
|
rrc_interface_phy_lte::phy_meas_t meas = {};
|
||||||
// Send report for PCell
|
meas.rsrp = phy->avg_rsrp_dbm[cc_idx];
|
||||||
phy->stack->new_phy_meas(phy->avg_rsrp_dbm[0], phy->avg_rsrq_db, tti);
|
meas.rsrq = phy->avg_rsrq_db[cc_idx];
|
||||||
} else {
|
// Save EARFCN and PCI for secondary cells, primary cell has earfcn=0
|
||||||
// Send report for SCell (if enabled)
|
if (cc_idx > 0) {
|
||||||
if (phy->scell_cfg[cc_idx].enabled) {
|
meas.earfcn = phy->scell_cfg[cc_idx].earfcn;
|
||||||
phy->stack->new_phy_meas(phy->avg_rsrp_dbm[cc_idx],
|
meas.pci = phy->scell_cfg[cc_idx].pci;
|
||||||
phy->avg_rsrq_db,
|
|
||||||
tti,
|
|
||||||
phy->scell_cfg[cc_idx].earfcn,
|
|
||||||
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
|
// 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) {
|
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) {
|
||||||
|
|
|
@ -98,7 +98,11 @@ void sync::init(srslte::radio_interface_phy* _radio,
|
||||||
sfn_p.init(&ue_sync, sf_buffer[0], log_h);
|
sfn_p.init(&ue_sync, sf_buffer[0], log_h);
|
||||||
|
|
||||||
// Start intra-frequency measurement
|
// 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<scell::intra_measure>(q));
|
||||||
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
running = true;
|
running = true;
|
||||||
|
@ -136,7 +140,9 @@ sync::~sync()
|
||||||
void sync::stop()
|
void sync::stop()
|
||||||
{
|
{
|
||||||
worker_com->semaphore.wait_all();
|
worker_com->semaphore.wait_all();
|
||||||
intra_freq_meas.stop();
|
for (auto& q : intra_freq_meas) {
|
||||||
|
q->stop();
|
||||||
|
}
|
||||||
running = false;
|
running = false;
|
||||||
wait_thread_finish();
|
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();
|
phy_state.go_idle();
|
||||||
worker_com->reset();
|
worker_com->reset();
|
||||||
|
|
||||||
|
// Stop all intra-frequency measurement before changing frequency
|
||||||
|
meas_stop();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (current_earfcn != (int)earfcn.at(cellsearch_earfcn_index)) {
|
if (current_earfcn != (int)earfcn.at(cellsearch_earfcn_index)) {
|
||||||
current_earfcn = (int)earfcn[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:
|
case search::CELL_FOUND:
|
||||||
// If a cell is found, configure it, synchronize and measure it
|
// If a cell is found, configure it, synchronize and measure it
|
||||||
if (set_cell()) {
|
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");
|
Info("Cell Search: Setting sampling rate and synchronizing SFN...\n");
|
||||||
set_sampling_rate();
|
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);
|
log_h->info("Cell Search: Sync OK. Camping on cell PCI=%d\n", cell.id);
|
||||||
if (found_cell) {
|
if (found_cell) {
|
||||||
found_cell->earfcn = current_earfcn;
|
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;
|
ret.found = phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND;
|
||||||
} else {
|
} else {
|
||||||
|
@ -282,14 +293,11 @@ bool sync::cell_select(phy_interface_rrc_lte::phy_cell_t* new_cell)
|
||||||
if (!new_cell) {
|
if (!new_cell) {
|
||||||
Info("Cell Select: Starting cell resynchronization\n");
|
Info("Cell Select: Starting cell resynchronization\n");
|
||||||
} else {
|
} else {
|
||||||
if (!srslte_cell_isvalid(&new_cell->cell)) {
|
if (!srslte_cellid_isvalid(new_cell->pci)) {
|
||||||
log_h->error("Cell Select: Invalid cell. ID=%d, PRB=%d, ports=%d\n", cell.id, cell.nof_prb, cell.nof_ports);
|
log_h->error("Cell Select: Invalid cell_id=%d\n", cell.id);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
Info("Cell Select: Starting cell selection for PCI=%d, n_prb=%d, EARFCN=%d\n",
|
Info("Cell Select: Starting cell selection for PCI=%d, EARFCN=%d\n", new_cell->pci, new_cell->earfcn);
|
||||||
new_cell->cell.id,
|
|
||||||
new_cell->cell.nof_prb,
|
|
||||||
new_cell->earfcn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for any pending PHICH
|
// 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 */
|
/* Reconfigure cell if necessary */
|
||||||
if (new_cell) {
|
if (new_cell) {
|
||||||
if (new_cell->cell.id != cell.id) {
|
if (new_cell->pci != cell.id) {
|
||||||
Info("Cell Select: Reconfiguring cell\n");
|
Info("Cell Select: Reconfiguring cell ID\n");
|
||||||
cell = new_cell->cell;
|
cell.id = new_cell->pci;
|
||||||
if (!set_cell()) {
|
if (!set_cell()) {
|
||||||
Error("Cell Select: Reconfiguring cell\n");
|
Error("Cell Select: Reconfiguring cell\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -321,6 +329,10 @@ bool sync::cell_select(phy_interface_rrc_lte::phy_cell_t* new_cell)
|
||||||
/* Select new frequency if necessary */
|
/* Select new frequency if necessary */
|
||||||
if ((int)new_cell->earfcn != current_earfcn) {
|
if ((int)new_cell->earfcn != current_earfcn) {
|
||||||
current_earfcn = new_cell->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);
|
Info("Cell Select: Setting new frequency EARFCN=%d\n", new_cell->earfcn);
|
||||||
if (!set_frequency()) {
|
if (!set_frequency()) {
|
||||||
Error("Cell Select: Setting new frequency EARFCN=%d\n", new_cell->earfcn);
|
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 */
|
// Reconfigure first intra-frequency measurement
|
||||||
intra_freq_meas.set_primary_cell(current_earfcn, cell);
|
intra_freq_meas[0]->set_primary_cell(current_earfcn, cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Change sampling rate if necessary */
|
/* Change sampling rate if necessary */
|
||||||
if (srate_mode != SRATE_CAMP) {
|
if (srate_mode != SRATE_CAMP) {
|
||||||
set_sampling_rate();
|
|
||||||
log_h->info("Cell Select: Setting CAMPING sampling rate\n");
|
log_h->info("Cell Select: Setting CAMPING sampling rate\n");
|
||||||
|
set_sampling_rate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SFN synchronization */
|
/* SFN synchronization */
|
||||||
|
@ -375,7 +387,8 @@ bool sync::cell_is_camping()
|
||||||
void sync::run_thread()
|
void sync::run_thread()
|
||||||
{
|
{
|
||||||
sf_worker* worker = nullptr;
|
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 is_end_of_burst = false;
|
||||||
bool force_camping_sfn_sync = 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
|
/* SFN synchronization using MIB. run_subframe() receives and processes 1 subframe
|
||||||
* and returns
|
* 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:
|
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();
|
stack->in_sync();
|
||||||
phy_state.state_exit();
|
phy_state.state_exit();
|
||||||
break;
|
break;
|
||||||
|
@ -459,16 +482,22 @@ void sync::run_thread()
|
||||||
|
|
||||||
// Force decode MIB if required
|
// Force decode MIB if required
|
||||||
if (force_camping_sfn_sync) {
|
if (force_camping_sfn_sync) {
|
||||||
uint32_t _tti = 0;
|
uint32_t _tti = 0;
|
||||||
srslte_cell_t temp_cell = {};
|
temp_cell = cell;
|
||||||
sync::sfn_sync::ret_code ret = sfn_p.decode_mib(&temp_cell, &_tti, buffer[0], mib);
|
sync::sfn_sync::ret_code ret = sfn_p.decode_mib(&temp_cell, &_tti, buffer[0], mib);
|
||||||
|
|
||||||
if (ret == sfn_sync::SFN_FOUND) {
|
if (ret == sfn_sync::SFN_FOUND) {
|
||||||
// Force tti
|
// Force tti
|
||||||
tti = _tti;
|
tti = _tti;
|
||||||
|
|
||||||
// Disable
|
// Disable
|
||||||
force_camping_sfn_sync = false;
|
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);
|
workers_pool->start_worker(worker);
|
||||||
|
|
||||||
// Save signal for Intra-frequency measurement
|
// 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)) {
|
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;
|
break;
|
||||||
case 0:
|
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
|
// 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));
|
next_offset = (int)round((time_adv_sec - time_adv_sec_) * srslte_sampling_freq_hz(cell.nof_prb));
|
||||||
this->next_time_adv_sec = time_adv_sec;
|
next_time_adv_sec = time_adv_sec_;
|
||||||
Info("Applying time_adv_sec=%.1f us, next_offset=%d\n", time_adv_sec * 1e6, next_offset);
|
Info("Applying time_adv_sec=%.1f us, next_offset=%d\n", time_adv_sec_ * 1e6, next_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
float sync::get_tx_cfo()
|
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_tol,
|
||||||
worker_com->args->cfo_loop_pss_conv);
|
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
|
// Disable CP based CFO estimation during find
|
||||||
if (cfo != 0) {
|
if (cfo != 0) {
|
||||||
q->cfo_current_value = cfo / 15000;
|
q->cfo_current_value = cfo / 15000;
|
||||||
|
@ -802,6 +827,11 @@ bool sync::set_cell()
|
||||||
return false;
|
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
|
// Set cell in all objects
|
||||||
if (srslte_ue_sync_set_cell(&ue_sync, cell)) {
|
if (srslte_ue_sync_set_cell(&ue_sync, cell)) {
|
||||||
Error("SYNC: Setting cell: initiating ue_sync\n");
|
Error("SYNC: Setting cell: initiating ue_sync\n");
|
||||||
|
@ -826,15 +856,15 @@ bool sync::set_cell()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sync::set_earfcn(std::vector<uint32_t> earfcn)
|
void sync::set_earfcn(std::vector<uint32_t> 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;
|
dl_freq = dl_freq_;
|
||||||
this->ul_freq = ul_freq;
|
ul_freq = ul_freq_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sync::set_frequency()
|
bool sync::set_frequency()
|
||||||
|
@ -842,9 +872,9 @@ bool sync::set_frequency()
|
||||||
double set_dl_freq = 0;
|
double set_dl_freq = 0;
|
||||||
double set_ul_freq = 0;
|
double set_ul_freq = 0;
|
||||||
|
|
||||||
if (this->dl_freq > 0 && this->ul_freq > 0) {
|
if (dl_freq > 0 && ul_freq > 0) {
|
||||||
set_dl_freq = this->dl_freq;
|
set_dl_freq = dl_freq;
|
||||||
set_ul_freq = this->ul_freq;
|
set_ul_freq = ul_freq;
|
||||||
} else {
|
} else {
|
||||||
set_dl_freq = 1e6 * srslte_band_fd(current_earfcn);
|
set_dl_freq = 1e6 * srslte_band_fd(current_earfcn);
|
||||||
if (srslte_band_is_tdd(srslte_band_get_band(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;
|
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) {
|
if (cell_) {
|
||||||
*cell = this->cell;
|
*cell_ = cell;
|
||||||
}
|
}
|
||||||
if (earfcn) {
|
if (earfcn_) {
|
||||||
*earfcn = current_earfcn;
|
*earfcn_ = current_earfcn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -977,13 +1007,13 @@ sync::search::~search()
|
||||||
srslte_ue_cellsearch_free(&cs);
|
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;
|
log_h = log_h_;
|
||||||
this->p = parent;
|
p = parent;
|
||||||
|
|
||||||
for (int i = 0; i < SRSLTE_MAX_PORTS; i++) {
|
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)) {
|
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<uint8_t, SRSLTE_BCH_PAYLOAD_LEN>& bch_payload)
|
sync::search::ret_code sync::search::run(srslte_cell_t* cell_, std::array<uint8_t, SRSLTE_BCH_PAYLOAD_LEN>& bch_payload)
|
||||||
{
|
{
|
||||||
if (!cell) {
|
srslte_cell_t new_cell = {};
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
srslte_ue_cellsearch_result_t found_cells[3];
|
srslte_ue_cellsearch_result_t found_cells[3];
|
||||||
|
|
||||||
bzero(cell, sizeof(srslte_cell_t));
|
|
||||||
bzero(found_cells, 3 * sizeof(srslte_ue_cellsearch_result_t));
|
bzero(found_cells, 3 * sizeof(srslte_ue_cellsearch_result_t));
|
||||||
|
|
||||||
if (p->srate_mode != SRATE_FIND) {
|
if (p->srate_mode != SRATE_FIND) {
|
||||||
|
@ -1065,19 +1092,19 @@ sync::search::ret_code sync::search::run(srslte_cell_t* cell, std::array<uint8_t
|
||||||
return CELL_NOT_FOUND;
|
return CELL_NOT_FOUND;
|
||||||
}
|
}
|
||||||
// Save result
|
// Save result
|
||||||
cell->id = found_cells[max_peak_cell].cell_id;
|
new_cell.id = found_cells[max_peak_cell].cell_id;
|
||||||
cell->cp = found_cells[max_peak_cell].cp;
|
new_cell.cp = found_cells[max_peak_cell].cp;
|
||||||
cell->frame_type = found_cells[max_peak_cell].frame_type;
|
new_cell.frame_type = found_cells[max_peak_cell].frame_type;
|
||||||
float cfo = found_cells[max_peak_cell].cfo;
|
float cfo = found_cells[max_peak_cell].cfo;
|
||||||
|
|
||||||
log_h->console("\n");
|
log_h->console("\n");
|
||||||
Info("SYNC: PSS/SSS detected: Mode=%s, PCI=%d, CFO=%.1f KHz, CP=%s\n",
|
Info("SYNC: PSS/SSS detected: Mode=%s, PCI=%d, CFO=%.1f KHz, CP=%s\n",
|
||||||
cell->frame_type ? "TDD" : "FDD",
|
new_cell.frame_type ? "TDD" : "FDD",
|
||||||
cell->id,
|
new_cell.id,
|
||||||
cfo / 1000,
|
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");
|
Error("SYNC: Setting UE MIB cell\n");
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
|
@ -1089,9 +1116,9 @@ sync::search::ret_code sync::search::run(srslte_cell_t* cell, std::array<uint8_t
|
||||||
|
|
||||||
/* Find and decode MIB */
|
/* Find and decode MIB */
|
||||||
int sfn_offset;
|
int sfn_offset;
|
||||||
ret = srslte_ue_mib_sync_decode(&ue_mib_sync, 40, bch_payload.data(), &cell->nof_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) {
|
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
|
// pack MIB and store inplace for PCAP dump
|
||||||
std::array<uint8_t, SRSLTE_BCH_PAYLOAD_LEN / 8> mib_packed;
|
std::array<uint8_t, SRSLTE_BCH_PAYLOAD_LEN / 8> mib_packed;
|
||||||
srslte_bit_pack_vector(bch_payload.data(), mib_packed.data(), SRSLTE_BCH_PAYLOAD_LEN);
|
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::array<uint8_t
|
||||||
|
|
||||||
fprintf(stdout,
|
fprintf(stdout,
|
||||||
"Found Cell: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n",
|
"Found Cell: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n",
|
||||||
cell->frame_type ? "TDD" : "FDD",
|
new_cell.frame_type ? "TDD" : "FDD",
|
||||||
cell->id,
|
new_cell.id,
|
||||||
cell->nof_prb,
|
new_cell.nof_prb,
|
||||||
cell->nof_ports,
|
new_cell.nof_ports,
|
||||||
cfo / 1000);
|
cfo / 1000);
|
||||||
|
|
||||||
Info("SYNC: MIB Decoded: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n",
|
Info("SYNC: MIB Decoded: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n",
|
||||||
cell->frame_type ? "TDD" : "FDD",
|
new_cell.frame_type ? "TDD" : "FDD",
|
||||||
cell->id,
|
new_cell.id,
|
||||||
cell->nof_prb,
|
new_cell.nof_prb,
|
||||||
cell->nof_ports,
|
new_cell.nof_ports,
|
||||||
cfo / 1000);
|
cfo / 1000);
|
||||||
|
|
||||||
if (!srslte_cell_isvalid(cell)) {
|
if (!srslte_cell_isvalid(&new_cell)) {
|
||||||
Error("SYNC: Detected invalid cell.\n");
|
Error("SYNC: Detected invalid cell.\n");
|
||||||
return CELL_NOT_FOUND;
|
return CELL_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save cell pointer
|
||||||
|
if (cell_) {
|
||||||
|
*cell_ = new_cell;
|
||||||
|
}
|
||||||
|
|
||||||
return CELL_FOUND;
|
return CELL_FOUND;
|
||||||
} else if (ret == 0) {
|
} else if (ret == 0) {
|
||||||
Warning("SYNC: Found PSS but could not decode PBCH\n");
|
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);
|
srslte_ue_mib_free(&ue_mib);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sync::sfn_sync::init(srslte_ue_sync_t* ue_sync,
|
void sync::sfn_sync::init(srslte_ue_sync_t* ue_sync_,
|
||||||
cf_t* buffer[SRSLTE_MAX_PORTS],
|
cf_t* buffer_[SRSLTE_MAX_PORTS],
|
||||||
srslte::log* log_h,
|
srslte::log* log_h_,
|
||||||
uint32_t nof_subframes)
|
uint32_t nof_subframes)
|
||||||
{
|
{
|
||||||
this->log_h = log_h;
|
log_h = log_h_;
|
||||||
this->ue_sync = ue_sync;
|
ue_sync = ue_sync_;
|
||||||
this->timeout = nof_subframes;
|
timeout = nof_subframes;
|
||||||
|
|
||||||
for (int p = 0; p < SRSLTE_MAX_PORTS; p++) {
|
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");
|
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");
|
Error("SYNC: Setting cell: initiating ue_mib\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1169,7 +1202,7 @@ void sync::sfn_sync::reset()
|
||||||
srslte_ue_mib_reset(&ue_mib);
|
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,
|
uint32_t* tti_cnt,
|
||||||
std::array<uint8_t, SRSLTE_BCH_PAYLOAD_LEN>& bch_payload,
|
std::array<uint8_t, SRSLTE_BCH_PAYLOAD_LEN>& bch_payload,
|
||||||
bool sfidx_only)
|
bool sfidx_only)
|
||||||
|
@ -1182,7 +1215,7 @@ sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t*
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 1) {
|
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) {
|
if (ret2 != SFN_NOFOUND) {
|
||||||
return ret2;
|
return ret2;
|
||||||
}
|
}
|
||||||
|
@ -1199,7 +1232,7 @@ sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t*
|
||||||
return IDLE;
|
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,
|
uint32_t* tti_cnt,
|
||||||
cf_t* ext_buffer[SRSLTE_MAX_PORTS],
|
cf_t* ext_buffer[SRSLTE_MAX_PORTS],
|
||||||
std::array<uint8_t, SRSLTE_BCH_PAYLOAD_LEN>& bch_payload,
|
std::array<uint8_t, SRSLTE_BCH_PAYLOAD_LEN>& bch_payload,
|
||||||
|
@ -1228,7 +1261,7 @@ sync::sfn_sync::ret_code sync::sfn_sync::decode_mib(srslte_cell_t* cell,
|
||||||
return ERROR;
|
return ERROR;
|
||||||
case SRSLTE_UE_MIB_FOUND:
|
case SRSLTE_UE_MIB_FOUND:
|
||||||
uint32_t sfn;
|
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;
|
sfn = (sfn + sfn_offset) % 1024;
|
||||||
if (tti_cnt) {
|
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
|
if (cc_idx < intra_freq_meas.size()) {
|
||||||
intra_freq_meas.clear_cells();
|
intra_freq_meas[cc_idx]->set_primary_cell(earfcn_, cell_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
void sync::set_cells_to_meas(uint32_t earfcn_, std::set<uint32_t>& pci)
|
||||||
int sync::meas_start(uint32_t earfcn, int pci)
|
|
||||||
{
|
{
|
||||||
if ((int)earfcn == current_earfcn) {
|
bool found = false;
|
||||||
if (pci != (int)cell.id) {
|
for (size_t i = 0; i < intra_freq_meas.size() and not found; i++) {
|
||||||
intra_freq_meas.add_cell(pci);
|
if (earfcn_ == intra_freq_meas[i]->get_earfcn()) {
|
||||||
|
intra_freq_meas[i]->set_cells_to_meas(pci);
|
||||||
|
found = true;
|
||||||
}
|
}
|
||||||
return 0;
|
}
|
||||||
} else {
|
if (!found) {
|
||||||
Warning("INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested measurement for %d)\n",
|
log_h->error("Neighbour cell measurement not supported in secondary carrier. EARFCN=%d\n", earfcn_);
|
||||||
current_earfcn,
|
|
||||||
earfcn);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int sync::meas_stop(uint32_t earfcn, int pci)
|
void sync::meas_stop()
|
||||||
{
|
{
|
||||||
if ((int)earfcn == current_earfcn) {
|
for (auto& q : intra_freq_meas) {
|
||||||
intra_freq_meas.rem_cell(pci);
|
q->meas_stop();
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
Warning(
|
|
||||||
"INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested stop measurement for %d)\n",
|
|
||||||
current_earfcn,
|
|
||||||
earfcn);
|
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace srsue
|
} // namespace srsue
|
||||||
|
|
|
@ -34,25 +34,20 @@ namespace srsue {
|
||||||
|
|
||||||
dl_harq_entity::dl_harq_entity() : proc(SRSLTE_MAX_HARQ_PROC)
|
dl_harq_entity::dl_harq_entity() : proc(SRSLTE_MAX_HARQ_PROC)
|
||||||
{
|
{
|
||||||
pcap = NULL;
|
pcap = nullptr;
|
||||||
demux_unit = NULL;
|
demux_unit = nullptr;
|
||||||
log_h = NULL;
|
log_h = nullptr;
|
||||||
timer_aligment_timer = NULL;
|
|
||||||
si_window_start = 0;
|
si_window_start = 0;
|
||||||
last_temporal_crnti = 0;
|
last_temporal_crnti = 0;
|
||||||
average_retx = 0;
|
average_retx = 0;
|
||||||
nof_pkts = 0;
|
nof_pkts = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dl_harq_entity::init(srslte::log* log_h,
|
bool dl_harq_entity::init(srslte::log* log_h_, mac_interface_rrc::ue_rnti_t* rntis_, demux* demux_unit_)
|
||||||
mac_interface_rrc::ue_rnti_t* rntis,
|
|
||||||
srslte::timer_handler::unique_timer* timer_aligment_timer_,
|
|
||||||
demux* demux_unit)
|
|
||||||
{
|
{
|
||||||
timer_aligment_timer = timer_aligment_timer_;
|
demux_unit = demux_unit_;
|
||||||
this->demux_unit = demux_unit;
|
log_h = log_h_;
|
||||||
this->log_h = log_h;
|
rntis = rntis_;
|
||||||
this->rntis = rntis;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < SRSLTE_MAX_HARQ_PROC; i++) {
|
for (uint32_t i = 0; i < SRSLTE_MAX_HARQ_PROC; i++) {
|
||||||
if (!proc[i].init(i, this)) {
|
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 if Broadcast Control Channel
|
||||||
// Do not generate ACK
|
action->generate_ack = not is_bcch;
|
||||||
action->generate_ack = false;
|
|
||||||
} else {
|
|
||||||
action->generate_ack = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dl_harq_entity::dl_harq_process::dl_tb_process::tb_decoded(mac_interface_phy_lte::mac_grant_dl_t grant,
|
void dl_harq_entity::dl_harq_process::dl_tb_process::tb_decoded(mac_interface_phy_lte::mac_grant_dl_t grant,
|
||||||
|
|
|
@ -90,7 +90,7 @@ bool mac::init(phy_interface_mac_lte* phy,
|
||||||
|
|
||||||
// Create UL/DL unique HARQ pointers
|
// Create UL/DL unique HARQ pointers
|
||||||
ul_harq.at(0)->init(log_h, &uernti, &ra_procedure, &mux_unit);
|
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();
|
reset();
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ void mac::reconfiguration(const uint32_t& cc_idx, const bool& enable)
|
||||||
}
|
}
|
||||||
while (dl_harq.size() < cc_idx + 1) {
|
while (dl_harq.size() < cc_idx + 1) {
|
||||||
auto dl = dl_harq_entity_ptr(new dl_harq_entity());
|
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) {
|
if (pcap) {
|
||||||
dl->start_pcap(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)
|
void mac::timer_expired(uint32_t timer_id)
|
||||||
{
|
{
|
||||||
if (timer_id == timer_alignment.id()) {
|
if (timer_id == timer_alignment.id()) {
|
||||||
|
Info("Time Alignment Timer expired\n");
|
||||||
timer_alignment_expire();
|
timer_alignment_expire();
|
||||||
} else {
|
} else {
|
||||||
Warning("Received callback from unknown timer_id=%d\n", timer_id);
|
Warning("Received callback from unknown timer_id=%d\n", timer_id);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
# and at http://www.gnu.org/licenses/.
|
# 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})
|
add_library(srsue_rrc STATIC ${SOURCES})
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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");
|
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.
|
// 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");
|
Info("No more space for neighbour cells\n");
|
||||||
return proc_outcome_t::success;
|
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()) {
|
if (not rrc_ptr->phy->cell_is_camping()) {
|
||||||
Warning("Could not camp on found cell.\n");
|
Warning("Could not camp on found cell.\n");
|
||||||
|
@ -340,11 +340,14 @@ proc_outcome_t rrc::cell_selection_proc::init()
|
||||||
neigh_index = 0;
|
neigh_index = 0;
|
||||||
cs_result = cs_result_t::no_cell;
|
cs_result = cs_result_t::no_cell;
|
||||||
state = search_state_t::cell_selection;
|
state = search_state_t::cell_selection;
|
||||||
|
discard_serving = false;
|
||||||
return step();
|
return step();
|
||||||
}
|
}
|
||||||
|
|
||||||
proc_outcome_t rrc::cell_selection_proc::step_cell_selection()
|
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
|
// Neighbour cells are sorted in descending order of RSRP
|
||||||
for (; neigh_index < rrc_ptr->neighbour_cells.size(); ++neigh_index) {
|
for (; neigh_index < rrc_ptr->neighbour_cells.size(); ++neigh_index) {
|
||||||
/*TODO: CHECK that PLMN matches. Currently we don't receive SIB1 of neighbour cells
|
/*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)) {
|
(rrc_ptr->cell_selection_criteria(rsrp) and rsrp > rrc_ptr->serving_cell->get_rsrp() + 5)) {
|
||||||
// currently connected and verifies cell selection criteria
|
// currently connected and verifies cell selection criteria
|
||||||
// Try to select Cell
|
// 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());
|
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 */
|
/* BLOCKING CALL */
|
||||||
if (rrc_ptr->phy->cell_select(&rrc_ptr->serving_cell->phy_cell)) {
|
if (rrc_ptr->phy->cell_select(&rrc_ptr->serving_cell->phy_cell)) {
|
||||||
Info("Wait PHY to be in-synch\n");
|
Info("Wait PHY to be in-synch\n");
|
||||||
state = search_state_t::wait_in_sync;
|
state = search_state_t::wait_in_sync;
|
||||||
|
rrc_ptr->phy_sync_state = phy_unknown_sync;
|
||||||
return step();
|
return step();
|
||||||
} else {
|
} else {
|
||||||
rrc_ptr->phy_sync_state = phy_unknown_sync;
|
rrc_ptr->phy_sync_state = phy_unknown_sync;
|
||||||
Error("Could not camp on serving cell.\n");
|
Error("Could not camp on serving cell.\n");
|
||||||
|
discard_serving = true;
|
||||||
// Continue to next neighbour cell
|
// 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()) {
|
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());
|
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");
|
Info("Selected serving cell OK.\n");
|
||||||
} else {
|
} else {
|
||||||
rrc_ptr->phy_sync_state = phy_unknown_sync;
|
rrc_ptr->phy_sync_state = phy_unknown_sync;
|
||||||
|
rrc_ptr->serving_cell->set_rsrp(-INFINITY);
|
||||||
Error("Could not camp on serving cell.\n");
|
Error("Could not camp on serving cell.\n");
|
||||||
return proc_outcome_t::error;
|
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()
|
proc_outcome_t rrc::cell_selection_proc::step_wait_in_sync()
|
||||||
{
|
{
|
||||||
if (rrc_ptr->phy_sync_state == phy_in_sync) {
|
if (rrc_ptr->phy_sync_state == phy_in_sync) {
|
||||||
Info("PHY is in SYNC\n");
|
if (rrc_ptr->cell_selection_criteria(rrc_ptr->serving_cell->get_rsrp())) {
|
||||||
if (not rrc_ptr->serv_cell_cfg.launch(&serv_cell_cfg_fut, rrc_ptr->ue_required_sibs)) {
|
Info("PHY is in SYNC and cell selection passed\n");
|
||||||
return proc_outcome_t::error;
|
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;
|
return proc_outcome_t::yield;
|
||||||
}
|
}
|
||||||
|
@ -432,6 +447,7 @@ proc_outcome_t rrc::cell_selection_proc::step_cell_config()
|
||||||
return proc_outcome_t::yield;
|
return proc_outcome_t::yield;
|
||||||
}
|
}
|
||||||
if (serv_cell_cfg_fut.is_success()) {
|
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");
|
Info("All SIBs of serving cell obtained successfully\n");
|
||||||
cs_result = cs_result_t::changed_cell;
|
cs_result = cs_result_t::changed_cell;
|
||||||
return proc_outcome_t::success;
|
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()
|
proc_outcome_t rrc::plmn_search_proc::init()
|
||||||
{
|
{
|
||||||
Info("Starting PLMN search\n");
|
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)) {
|
if (not rrc_ptr->cell_searcher.launch(&cell_search_fut)) {
|
||||||
Error("Failed due to fail to init cell search...\n");
|
Error("Failed due to fail to init cell search...\n");
|
||||||
return proc_outcome_t::error;
|
return proc_outcome_t::error;
|
||||||
|
@ -804,7 +821,7 @@ proc_outcome_t rrc::go_idle_proc::init()
|
||||||
{
|
{
|
||||||
rlc_flush_counter = 0;
|
rlc_flush_counter = 0;
|
||||||
Info("Starting...\n");
|
Info("Starting...\n");
|
||||||
return proc_outcome_t::yield;
|
return step();
|
||||||
}
|
}
|
||||||
|
|
||||||
proc_outcome_t rrc::go_idle_proc::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;
|
return proc_outcome_t::success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the RLC SRB1 is not suspended
|
||||||
// wait for max. 2s for RLC on SRB1 to be flushed
|
// 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();
|
rrc_ptr->leave_connected();
|
||||||
return proc_outcome_t::success;
|
return proc_outcome_t::success;
|
||||||
} else {
|
} else {
|
||||||
|
@ -854,8 +873,7 @@ proc_outcome_t rrc::cell_reselection_proc::step()
|
||||||
Info("Cell Selection completed. Handling its result...\n");
|
Info("Cell Selection completed. Handling its result...\n");
|
||||||
switch (*cell_selection_fut.value()) {
|
switch (*cell_selection_fut.value()) {
|
||||||
case cs_result_t::changed_cell:
|
case cs_result_t::changed_cell:
|
||||||
Info("New cell has been selected, start receiving PCCH\n");
|
Info("New cell has been selected\n");
|
||||||
rrc_ptr->mac->pcch_start_rx();
|
|
||||||
break;
|
break;
|
||||||
case cs_result_t::no_cell:
|
case cs_result_t::no_cell:
|
||||||
Warning("Could not find any cell to camp on\n");
|
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)
|
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
|
// Save Current RNTI before MAC Reset
|
||||||
mac_interface_rrc::ue_rnti_t uernti;
|
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_rnti = uernti.crnti;
|
||||||
reest_cause = cause;
|
reest_cause = cause;
|
||||||
reest_source_pci = rrc_ptr->serving_cell->get_pci(); // needed for reestablishment with another cell
|
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
|
// 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
|
// 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
|
// Run cell reselection
|
||||||
if (not rrc_ptr->cell_reselector.run()) {
|
if (not rrc_ptr->cell_reselector.run()) {
|
||||||
// Check T311
|
// Check T311
|
||||||
if (!rrc_ptr->t311.is_running()) {
|
if (not rrc_ptr->t311.is_running()) {
|
||||||
// Abort procedure if T311 expires
|
// 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;
|
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_sib1(),
|
||||||
rrc_ptr->serving_cell->has_sib2(),
|
rrc_ptr->serving_cell->has_sib2(),
|
||||||
rrc_ptr->serving_cell->has_sib3());
|
rrc_ptr->serving_cell->has_sib3());
|
||||||
std::vector<uint32_t> required_sibs = {1, 2, 3};
|
std::vector<uint32_t> required_sibs = {0, 1, 2};
|
||||||
if (!rrc_ptr->serv_cell_cfg.launch(required_sibs)) {
|
if (!rrc_ptr->serv_cell_cfg.launch(required_sibs)) {
|
||||||
Error("Failed to initiate configure serving cell\n");
|
Error("Failed to initiate configure serving cell\n");
|
||||||
return proc_outcome_t::error;
|
return proc_outcome_t::error;
|
||||||
|
@ -991,7 +1014,8 @@ proc_outcome_t rrc::connection_reest_proc::step_cell_configuration()
|
||||||
// Check T311
|
// Check T311
|
||||||
if (!rrc_ptr->t311.is_running()) {
|
if (!rrc_ptr->t311.is_running()) {
|
||||||
// Abort procedure if T311 expires
|
// 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;
|
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;
|
// 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);
|
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
|
// Upon selecting an inter-RAT cell
|
||||||
Info("Reestablishment Cell Selection criteria failed.\n");
|
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;
|
return proc_outcome_t::success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,25 +240,28 @@ public:
|
||||||
|
|
||||||
void in_sync() override {}
|
void in_sync() override {}
|
||||||
void out_of_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<phy_meas_t>& meas) override
|
||||||
{
|
{
|
||||||
if (!cells.count(pci)) {
|
for (auto& m : meas) {
|
||||||
cells[pci].rsrp_min = rsrp;
|
uint32_t pci = m.pci;
|
||||||
cells[pci].rsrp_max = rsrp;
|
if (!cells.count(pci)) {
|
||||||
cells[pci].rsrp_avg = rsrp;
|
cells[pci].rsrp_min = m.rsrp;
|
||||||
cells[pci].rsrq_min = rsrq;
|
cells[pci].rsrp_max = m.rsrp;
|
||||||
cells[pci].rsrq_max = rsrq;
|
cells[pci].rsrp_avg = m.rsrp;
|
||||||
cells[pci].rsrq_avg = rsrq;
|
cells[pci].rsrq_min = m.rsrq;
|
||||||
cells[pci].count = 1;
|
cells[pci].rsrq_max = m.rsrq;
|
||||||
} else {
|
cells[pci].rsrq_avg = m.rsrq;
|
||||||
cells[pci].rsrp_min = SRSLTE_MIN(cells[pci].rsrp_min, rsrp);
|
cells[pci].count = 1;
|
||||||
cells[pci].rsrp_max = SRSLTE_MAX(cells[pci].rsrp_max, rsrp);
|
} else {
|
||||||
cells[pci].rsrp_avg = (rsrp + cells[pci].rsrp_avg * cells[pci].count) / (cells[pci].count + 1);
|
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, rsrq);
|
cells[pci].rsrq_min = SRSLTE_MIN(cells[pci].rsrq_min, m.rsrq);
|
||||||
cells[pci].rsrq_max = SRSLTE_MAX(cells[pci].rsrq_max, rsrq);
|
cells[pci].rsrq_max = SRSLTE_MAX(cells[pci].rsrq_max, m.rsrq);
|
||||||
cells[pci].rsrq_avg = (rsrq + cells[pci].rsrq_avg * cells[pci].count) / (cells[pci].count + 1);
|
cells[pci].rsrq_avg = (m.rsrq + cells[pci].rsrq_avg * cells[pci].count) / (cells[pci].count + 1);
|
||||||
cells[pci].count++;
|
cells[pci].count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +396,6 @@ int main(int argc, char** argv)
|
||||||
phy_args.estimator_fil_auto = false;
|
phy_args.estimator_fil_auto = false;
|
||||||
phy_args.estimator_fil_order = 4;
|
phy_args.estimator_fil_order = 4;
|
||||||
phy_args.estimator_fil_stddev = 1.0f;
|
phy_args.estimator_fil_stddev = 1.0f;
|
||||||
phy_args.sic_pss_enabled = false;
|
|
||||||
phy_args.interpolate_subframe_enabled = false;
|
phy_args.interpolate_subframe_enabled = false;
|
||||||
phy_args.nof_rx_ant = 1;
|
phy_args.nof_rx_ant = 1;
|
||||||
phy_args.cfo_is_doppler = true;
|
phy_args.cfo_is_doppler = true;
|
||||||
|
@ -450,6 +452,8 @@ int main(int argc, char** argv)
|
||||||
intra_measure.init(&common, &rrc, &logger);
|
intra_measure.init(&common, &rrc, &logger);
|
||||||
intra_measure.set_primary_cell(serving_cell_id, cell_base);
|
intra_measure.set_primary_cell(serving_cell_id, cell_base);
|
||||||
|
|
||||||
|
std::set<uint32_t> pcis_to_meas = {};
|
||||||
|
|
||||||
if (earfcn_dl >= 0) {
|
if (earfcn_dl >= 0) {
|
||||||
// Create radio log
|
// Create radio log
|
||||||
radio_log = std::unique_ptr<srslte::log_filter>(new srslte::log_filter("Radio"));
|
radio_log = std::unique_ptr<srslte::log_filter>(new srslte::log_filter("Radio"));
|
||||||
|
@ -490,7 +494,7 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
// Add cell to known cells
|
// Add cell to known cells
|
||||||
if (cell_list.empty()) {
|
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") {
|
if (cell_list == "all") {
|
||||||
// Add all possible cells
|
// Add all possible cells
|
||||||
for (int i = 0; i < 504; i++) {
|
for (int i = 0; i < 504; i++) {
|
||||||
intra_measure.add_cell(i);
|
pcis_to_meas.insert(i);
|
||||||
}
|
}
|
||||||
} else if (cell_list == "none") {
|
} else if (cell_list == "none") {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
@ -516,10 +520,13 @@ int main(int argc, char** argv)
|
||||||
while (ss.good()) {
|
while (ss.good()) {
|
||||||
std::string substr;
|
std::string substr;
|
||||||
getline(ss, 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
|
// Run loop
|
||||||
for (uint32_t sf_idx = 0; sf_idx < duration_execution_s * 1000; sf_idx++) {
|
for (uint32_t sf_idx = 0; sf_idx < duration_execution_s * 1000; sf_idx++) {
|
||||||
srslte_dl_sf_cfg_t sf_cfg_dl = {};
|
srslte_dl_sf_cfg_t sf_cfg_dl = {};
|
||||||
|
|
|
@ -85,10 +85,13 @@ private:
|
||||||
|
|
||||||
void in_sync() override { notify_in_sync(); }
|
void in_sync() override { notify_in_sync(); }
|
||||||
void out_of_sync() override { notify_out_of_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<phy_meas_t>& meas) override
|
||||||
{
|
{
|
||||||
notify_new_phy_meas();
|
for (auto& m : meas) {
|
||||||
log_h.info("New measurement earfcn=%d; pci=%d; rsrp=%+.1fdBm; rsrq=%+.1fdB;\n", earfcn, pci, rsrp, rsrq);
|
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_dl_sched_rnti(uint32_t tti) override { return rnti; }
|
||||||
uint16_t get_ul_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);
|
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(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(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
|
// 2. Cell select
|
||||||
phy_test->get_phy_interface_rrc()->cell_select(&phy_cell);
|
phy_test->get_phy_interface_rrc()->cell_select(&phy_cell);
|
||||||
|
|
|
@ -78,7 +78,7 @@ public:
|
||||||
void set_config_mbsfn_mcch(const srslte::mcch_msg_t& mcch){};
|
void set_config_mbsfn_mcch(const srslte::mcch_msg_t& mcch){};
|
||||||
|
|
||||||
// Measurements interface
|
// Measurements interface
|
||||||
void meas_reset();
|
void meas_stop();
|
||||||
int meas_start(uint32_t earfcn, int pci = -1);
|
int meas_start(uint32_t earfcn, int pci = -1);
|
||||||
int meas_stop(uint32_t earfcn, int pci = -1);
|
int meas_stop(uint32_t earfcn, int pci = -1);
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,7 @@ void lte_ttcn3_phy::set_config(srslte::phy_cfg_t& config, uint32_t cc_idx, uint3
|
||||||
}
|
}
|
||||||
|
|
||||||
// Measurements interface
|
// 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)
|
int lte_ttcn3_phy::meas_start(uint32_t earfcn, int pci)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
target_link_libraries(rrc_reconfig_test srsue_upper srslte_upper srslte_phy rrc_asn1)
|
||||||
add_test(rrc_reconfig_test rrc_reconfig_test)
|
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)
|
add_executable(nas_test nas_test.cc)
|
||||||
target_link_libraries(nas_test srsue_upper srslte_upper srslte_phy rrc_asn1)
|
target_link_libraries(nas_test srsue_upper srslte_upper srslte_phy rrc_asn1)
|
||||||
add_test(nas_test nas_test)
|
add_test(nas_test nas_test)
|
||||||
|
|
|
@ -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 <iostream>
|
||||||
|
|
||||||
|
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<uint32_t>& 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<uint32_t> freqs_started;
|
||||||
|
std::map<uint32_t, std::set<uint32_t> > 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<rrc_interface_phy_lte::phy_meas_t> 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<rrc_interface_phy_lte::phy_meas_t> 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<float> rsrp,
|
||||||
|
const std::vector<uint32_t> earfcn,
|
||||||
|
const std::vector<uint32_t> pci)
|
||||||
|
{
|
||||||
|
std::vector<rrc_interface_phy_lte::phy_meas_t> 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<uint32_t> 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<float> 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<uint32_t> 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<float> 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<uint32_t>& earfcn, const std::vector<uint32_t>& pci)
|
||||||
|
{
|
||||||
|
std::vector<float> 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<uint32_t> 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<float> 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<float> 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;
|
||||||
|
}
|
|
@ -36,7 +36,7 @@
|
||||||
using namespace asn1;
|
using namespace asn1;
|
||||||
using namespace asn1::rrc;
|
using namespace asn1::rrc;
|
||||||
|
|
||||||
void nas_test()
|
int nas_test()
|
||||||
{
|
{
|
||||||
srslte::log_filter log1("NAS");
|
srslte::log_filter log1("NAS");
|
||||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||||
|
@ -93,9 +93,10 @@ void nas_test()
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int basic_test()
|
int meas_obj_test()
|
||||||
{
|
{
|
||||||
srslte::log_filter log1("RRC");
|
srslte::log_filter log1("RRC");
|
||||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||||
|
@ -154,6 +155,6 @@ int basic_test()
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
TESTASSERT(basic_test() == 0);
|
TESTASSERT(meas_obj_test() == 0);
|
||||||
nas_test();
|
TESTASSERT(nas_test() == 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,10 +298,6 @@ enable = false
|
||||||
#
|
#
|
||||||
# interpolate_subframe_enabled: Interpolates in the time domain the channel estimates within 1 subframe. Default is to average.
|
# 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
|
# 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.
|
# used in TM1. It is True by default.
|
||||||
#
|
#
|
||||||
|
@ -333,7 +329,6 @@ enable = false
|
||||||
#estimator_fil_order = 4
|
#estimator_fil_order = 4
|
||||||
#snr_to_cqi_offset = 0.0
|
#snr_to_cqi_offset = 0.0
|
||||||
#interpolate_subframe_enabled = false
|
#interpolate_subframe_enabled = false
|
||||||
#sic_pss_enabled = true
|
|
||||||
#pregenerate_signals = false
|
#pregenerate_signals = false
|
||||||
#pdsch_csi_enabled = true
|
#pdsch_csi_enabled = true
|
||||||
#pdsch_8bit_decoder = false
|
#pdsch_8bit_decoder = false
|
||||||
|
|
Loading…
Reference in New Issue