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
master
Ismael Gomez 5 years ago committed by Andre Puschmann
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(RPATH "Enable RPATH" OFF)
option(ENABLE_ASAN "Enable gcc/clang address sanitizer" OFF)
option(ENABLE_GCOV "Enable gcc/clang address sanitizer" OFF)
option(ENABLE_MSAN "Enable clang memory sanitizer" OFF)
option(ENABLE_TIDY "Enable clang tidy" OFF)
@ -435,6 +436,11 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fPIE -pie")
endif (ENABLE_MSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang")
if (ENABLE_GCOV)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
endif (ENABLE_GCOV)
endif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")

@ -188,6 +188,8 @@ public:
void stop() { impl()->stop(); }
void clear() { impl()->clear(); }
void release()
{
impl()->clear();

@ -28,6 +28,7 @@
#ifndef SRSLTE_UE_INTERFACES_H
#define SRSLTE_UE_INTERFACES_H
#include <set>
#include <string>
#include "rrc_interface_types.h"
@ -133,9 +134,17 @@ public:
class rrc_interface_phy_lte
{
public:
virtual void in_sync() = 0;
virtual void out_of_sync() = 0;
virtual void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn = -1, int pci = -1) = 0;
// Measurement object from phy
typedef struct {
float rsrp;
float rsrq;
uint32_t earfcn;
uint32_t pci;
} phy_meas_t;
virtual void in_sync() = 0;
virtual void out_of_sync() = 0;
virtual void new_cell_meas(std::vector<phy_meas_t>& meas) = 0;
};
// RRC interface for NAS
@ -273,6 +282,7 @@ public:
virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0;
virtual bool has_bearer(uint32_t lcid) = 0;
virtual bool has_data(const uint32_t lcid) = 0;
virtual bool is_suspended(const uint32_t lcid) = 0;
virtual void write_sdu(uint32_t lcid, srslte::unique_byte_buffer_t sdu, bool blocking = true) = 0;
};
@ -514,7 +524,6 @@ typedef struct {
uint32_t estimator_fil_order = 4;
float snr_to_cqi_offset = 0.0f;
std::string sss_algorithm = "full";
bool sic_pss_enabled = false;
float rx_gain_offset = 62;
bool pdsch_csi_enabled = true;
bool pdsch_8bit_decoder = false;
@ -582,10 +591,6 @@ public:
class phy_interface_rrc_lte
{
public:
virtual void get_current_cell(srslte_cell_t* cell, uint32_t* current_earfcn = NULL) = 0;
virtual uint32_t get_current_earfcn() = 0;
virtual uint32_t get_current_pci() = 0;
virtual void set_config(srslte::phy_cfg_t& config,
uint32_t cc_idx = 0,
uint32_t earfcn = 0,
@ -596,9 +601,8 @@ public:
virtual void set_config_mbsfn_mcch(const srslte::mcch_msg_t& mcch) = 0;
/* Measurements interface */
virtual void meas_reset() = 0;
virtual int meas_start(uint32_t earfcn, int pci = -1) = 0;
virtual int meas_stop(uint32_t earfcn, int pci = -1) = 0;
virtual void set_cells_to_meas(uint32_t earfcn, std::set<uint32_t>& pci) = 0;
virtual void meas_stop() = 0;
typedef struct {
enum { CELL_FOUND = 0, CELL_NOT_FOUND, ERROR } found;
@ -606,8 +610,8 @@ public:
} cell_search_ret_t;
typedef struct {
srslte_cell_t cell;
uint32_t earfcn;
uint32_t pci;
uint32_t earfcn;
} phy_cell_t;
/* Cell search and selection procedures */

@ -58,6 +58,7 @@ public:
// MAC interface
bool has_data(const uint32_t lcid);
bool is_suspended(const uint32_t lcid);
uint32_t get_buffer_state(const uint32_t lcid);
uint32_t get_total_mch_buffer_state(uint32_t lcid);
int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);

@ -221,17 +221,17 @@ public:
bool suspend()
{
if (is_suspended) {
if (suspended) {
return false;
}
is_suspended = true;
suspended = true;
return true;
}
// Pops all PDUs from queue and calls write_pdu() method for the bearer type
bool resume()
{
if (!is_suspended) {
if (!suspended) {
return false;
}
pdu_t p;
@ -240,13 +240,13 @@ public:
write_pdu(p.payload, p.nof_bytes);
free(p.payload);
}
is_suspended = false;
suspended = false;
return true;
}
void write_pdu_s(uint8_t* payload, uint32_t nof_bytes)
{
if (is_suspended) {
if (suspended) {
queue_pdu(payload, nof_bytes);
} else {
write_pdu(payload, nof_bytes);
@ -265,12 +265,13 @@ public:
// MAC interface
virtual bool has_data() = 0;
bool is_suspended() { return suspended; };
virtual uint32_t get_buffer_state() = 0;
virtual int read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0;
virtual void write_pdu(uint8_t* payload, uint32_t nof_bytes) = 0;
private:
bool is_suspended = false;
bool suspended = false;
// Enqueues the PDU in the resume queue
void queue_pdu(uint8_t* payload, uint32_t nof_bytes)

@ -260,6 +260,18 @@ bool rlc::has_data(uint32_t lcid)
return has_data;
}
bool rlc::is_suspended(const uint32_t lcid)
{
bool ret = false;
pthread_rwlock_rdlock(&rwlock);
if (valid_lcid(lcid)) {
ret = rlc_array.at(lcid)->is_suspended();
}
pthread_rwlock_unlock(&rwlock);
return ret;
}
uint32_t rlc::get_buffer_state(uint32_t lcid)
{

@ -36,7 +36,7 @@ using namespace asn1::rrc;
} \
}
int basic_test()
int meas_obj_test()
{
srslte::log_filter log1("RRC");
log1.set_level(srslte::LOG_LEVEL_DEBUG);
@ -130,6 +130,6 @@ int basic_test()
int main(int argc, char** argv)
{
TESTASSERT(basic_test() == 0);
TESTASSERT(meas_obj_test() == 0);
return 0;
}

@ -38,7 +38,7 @@
using namespace asn1;
using namespace asn1::rrc;
int basic_test()
int meas_obj_test()
{
srslte::log_filter log1("RRC");
log1.set_level(srslte::LOG_LEVEL_DEBUG);
@ -100,6 +100,6 @@ int basic_test()
int main(int argc, char** argv)
{
TESTASSERT(basic_test() == 0);
TESTASSERT(meas_obj_test() == 0);
return 0;
}

@ -131,7 +131,7 @@ void basic_test_tx(rlc_am_lte* rlc, byte_buffer_t pdu_bufs[NBUFS])
assert(0 == rlc->get_buffer_state());
}
bool basic_test()
bool meas_obj_test()
{
srslte::log_filter log1("RLC_AM_1");
srslte::log_filter log2("RLC_AM_2");
@ -1647,7 +1647,7 @@ bool status_pdu_test()
int main(int argc, char** argv)
{
if (basic_test()) {
if (meas_obj_test()) {
printf("basic_test failed\n");
exit(-1);
};

@ -71,7 +71,7 @@ public:
uint32_t expected_sdu_len;
};
int basic_test()
int meas_obj_test()
{
srslte::log_filter log1("RLC_1");
srslte::log_filter log2("RLC_2");
@ -204,7 +204,7 @@ int basic_test()
int main(int argc, char** argv)
{
if (basic_test()) {
if (meas_obj_test()) {
return -1;
}
byte_buffer_pool::get_instance()->cleanup();

@ -74,7 +74,7 @@ public:
rlc_um_lte rlc1, rlc2;
};
int basic_test()
int meas_obj_test()
{
rlc_um_lte_test_context1 ctxt;
@ -392,7 +392,7 @@ int reassmble_test2()
int main(int argc, char** argv)
{
if (basic_test()) {
if (meas_obj_test()) {
return -1;
}
byte_buffer_pool::get_instance()->cleanup();

@ -72,9 +72,8 @@ public:
cell_search_ret_t cell_search(phy_cell_t* cell) final;
bool cell_select(phy_cell_t* cell) final;
void meas_reset() final;
int meas_start(uint32_t earfcn, int pci) final;
int meas_stop(uint32_t earfcn, int pci) final;
void set_cells_to_meas(uint32_t earfcn, std::set<uint32_t>& pci) final;
void meas_stop() final;
// also MAC interface
bool cell_is_camping() final;
@ -120,10 +119,6 @@ public:
uint32_t get_current_tti() final;
void get_current_cell(srslte_cell_t* cell, uint32_t* current_earfcn) final;
uint32_t get_current_earfcn() final;
uint32_t get_current_pci() final;
void start_plot() final;
const static int MAX_WORKERS = 4;

@ -63,8 +63,8 @@ public:
float cur_pusch_power = 0.0f;
float avg_rsrp[SRSLTE_MAX_CARRIERS] = {};
float avg_rsrp_dbm[SRSLTE_MAX_CARRIERS] = {};
float avg_rsrq_db = 0.0f;
float avg_rssi_dbm = 0.0f;
float avg_rsrq_db[SRSLTE_MAX_CARRIERS] = {};
float avg_rssi_dbm[SRSLTE_MAX_CARRIERS] = {};
float rx_gain_offset = 0.0f;
float avg_snr_db_cqi[SRSLTE_MAX_CARRIERS] = {};
float avg_noise[SRSLTE_MAX_CARRIERS] = {};

@ -39,36 +39,33 @@ public:
~intra_measure();
void init(phy_common* common, rrc_interface_phy_lte* rrc, srslte::log* log_h);
void stop();
void add_cell(int pci);
void rem_cell(int pci);
void set_primary_cell(uint32_t earfcn, srslte_cell_t cell);
void clear_cells();
int get_offset(uint32_t pci);
void set_cells_to_meas(std::set<uint32_t>& pci);
void meas_stop();
void write(uint32_t tti, cf_t* data, uint32_t nsamples);
uint32_t get_earfcn() { return current_earfcn; };
private:
void run_thread();
const static int INTRA_FREQ_MEAS_PRIO = DEFAULT_PRIORITY + 5;
scell_recv scell = {};
rrc_interface_phy_lte* rrc = nullptr;
srslte::log* log_h = nullptr;
phy_common* common = nullptr;
uint32_t current_earfcn = 0;
uint32_t current_sflen = 0;
srslte_cell_t primary_cell = {};
std::vector<int> active_pci;
scell_recv scell = {};
rrc_interface_phy_lte* rrc = nullptr;
srslte::log* log_h = nullptr;
phy_common* common = nullptr;
uint32_t current_earfcn = 0;
uint32_t current_sflen = 0;
srslte_cell_t serving_cell = {};
std::set<uint32_t> active_pci = {};
std::mutex active_pci_mutex = {};
srslte::tti_sync_cv tti_sync;
cf_t* search_buffer = nullptr;
scell_recv::cell_info_t info[scell_recv::MAX_CELLS] = {};
bool running = false;
bool receive_enabled = false;
bool receiving = false;
uint32_t measure_tti = 0;
uint32_t receive_cnt = 0;
srslte_ringbuffer_t ring_buffer = {};

@ -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
#define SRSUE_SCELL_RECV_H
#include "srsue/hdr/phy/phy_common.h"
#include <set>
#include <srslte/srslte.h>
#include "measure.h"
namespace srsue {
namespace scell {
@ -33,21 +33,10 @@ namespace scell {
class scell_recv
{
public:
const static int MAX_CELLS = 8;
typedef struct {
uint32_t pci;
float rsrp;
float rsrq;
uint32_t offset;
} cell_info_t;
void init(srslte::log* log_h, bool sic_pss_enabled, uint32_t max_sf_window, phy_common* worker_com);
void init(srslte::log* log_h, uint32_t max_sf_window);
void deinit();
void reset();
int find_cells(cf_t* input_buffer,
float rx_gain_offset,
srslte_cell_t current_cell,
uint32_t nof_sf,
cell_info_t found_cells[MAX_CELLS]);
std::set<uint32_t> find_cells(const cf_t* input_buffer, const srslte_cell_t serving_cell, const uint32_t nof_sf);
private:
// 36.133 9.1.2.1 for band 7
@ -57,9 +46,7 @@ private:
srslte::log* log_h;
srslte_sync_t sync_find;
bool sic_pss_enabled;
uint32_t current_fft_sz;
measure measure_p;
};
} // namespace scell

@ -70,9 +70,9 @@ public:
bool cell_is_camping();
// RRC interface for controlling the neighbour cell measurement
void meas_reset();
int meas_start(uint32_t earfcn, int pci);
int meas_stop(uint32_t earfcn, int pci);
void set_cells_to_meas(uint32_t earfcn, std::set<uint32_t>& pci);
void set_inter_frequency_measurement(uint32_t cc_idx, uint32_t earfcn_, srslte_cell_t cell_);
void meas_stop();
// from chest_feedback_itf
void in_sync() final;
@ -172,7 +172,7 @@ private:
// Objects for internal use
search search_p;
sfn_sync sfn_p;
scell::intra_measure intra_freq_meas;
std::vector<std::unique_ptr<scell::intra_measure> > intra_freq_meas;
uint32_t current_sflen = 0;
int next_offset = 0; // Sample offset triggered by Time aligment commands

@ -41,7 +41,6 @@ public:
bool init(srslte::log* log_h,
mac_interface_rrc::ue_rnti_t* rntis,
srslte::timer_handler::unique_timer* timer_aligment_timer,
demux* demux_unit);
void reset();
void start_pcap(srslte::mac_pcap* pcap_);
@ -121,7 +120,6 @@ private:
std::vector<dl_harq_process> proc;
dl_harq_process bcch_proc;
srslte::timer_handler::unique_timer* timer_aligment_timer = nullptr;
demux* demux_unit;
srslte::log* log_h;
srslte::mac_pcap* pcap;

@ -68,9 +68,9 @@ namespace srsue {
class cell_t
{
public:
bool is_valid() { return phy_cell.earfcn != 0 && srslte_cell_isvalid(&phy_cell.cell); }
bool equals(cell_t* x) { return equals(x->phy_cell.earfcn, x->phy_cell.cell.id); }
bool equals(uint32_t earfcn, uint32_t pci) { return earfcn == phy_cell.earfcn && pci == phy_cell.cell.id; }
bool is_valid() { return phy_cell.earfcn != 0 && srslte_cellid_isvalid(phy_cell.pci); }
bool equals(cell_t* x) { return equals(x->phy_cell.earfcn, x->phy_cell.pci); }
bool equals(uint32_t earfcn, uint32_t pci) { return earfcn == phy_cell.earfcn && pci == phy_cell.pci; }
// NaN means an RSRP value has not yet been obtained. Keep then in the list and clean them if never updated
bool greater(cell_t* x) { return rsrp > x->rsrp || std::isnan(rsrp); }
bool plmn_equals(asn1::rrc::plmn_id_s plmn_id)
@ -113,29 +113,27 @@ public:
}
}
cell_t()
{
phy_interface_rrc_lte::phy_cell_t tmp = {};
cell_t(tmp, 0);
}
cell_t(phy_interface_rrc_lte::phy_cell_t phy_cell, float rsrp_)
cell_t() { cell_t({0, 0}); }
cell_t(phy_interface_rrc_lte::phy_cell_t phy_cell_)
{
gettimeofday(&last_update, nullptr);
this->has_valid_sib1 = false;
this->has_valid_sib2 = false;
this->has_valid_sib3 = false;
this->has_valid_sib13 = false;
this->phy_cell = phy_cell;
rsrp = rsrp_;
bzero(&sib1, sizeof(sib1));
bzero(&sib2, sizeof(sib2));
bzero(&sib3, sizeof(sib3));
bzero(&sib13, sizeof(sib13));
has_valid_sib1 = false;
has_valid_sib2 = false;
has_valid_sib3 = false;
has_valid_sib13 = false;
phy_cell = phy_cell_;
rsrp = NAN;
rsrq = NAN;
sib1 = {};
sib2 = {};
sib3 = {};
sib13 = {};
}
uint32_t get_earfcn() { return phy_cell.earfcn; }
uint32_t get_pci() { return phy_cell.cell.id; }
uint32_t get_pci() { return phy_cell.pci; }
void set_rsrp(float rsrp_)
{
@ -144,8 +142,15 @@ public:
}
gettimeofday(&last_update, nullptr);
}
void set_rsrq(float rsrq_)
{
if (!std::isnan(rsrq_)) {
rsrq = rsrq_;
}
}
float get_rsrp() { return rsrp; }
float get_rsrq() { return rsrq; }
void set_sib1(asn1::rrc::sib_type1_s* sib1_)
{
@ -244,7 +249,13 @@ public:
std::string print()
{
char buf[256];
snprintf(buf, 256, "{cell_id: 0x%x, pci: %d, dl_earfcn: %d}\n", get_cell_id(), get_pci(), get_earfcn());
snprintf(buf,
256,
"{cell_id: 0x%x, pci: %d, dl_earfcn: %d, rsrp=%+.1f}",
get_cell_id(),
get_pci(),
get_earfcn(),
get_rsrp());
return std::string{buf};
}
@ -258,6 +269,7 @@ public:
private:
float rsrp = NAN;
float rsrq = NAN;
struct timeval last_update = {};
@ -322,7 +334,7 @@ public:
// PHY interface
void in_sync() final;
void out_of_sync() final;
void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn, int pci) final;
void new_cell_meas(std::vector<phy_meas_t>& meas);
// MAC interface
void ho_ra_completed(bool ra_successful);
@ -345,9 +357,14 @@ public:
void cell_search_completed(const phy_interface_rrc_lte::cell_search_ret_t& cs_ret,
const phy_interface_rrc_lte::phy_cell_t& found_cell);
protected:
// Moved to protected to be accessible by unit tests
void set_serving_cell(phy_interface_rrc_lte::phy_cell_t phy_cell, bool discard_serving);
bool has_neighbour_cell(const uint32_t earfcn, const uint32_t pci);
private:
typedef struct {
enum { PDU, PCCH, PDU_MCH, RLF, PDU_BCCH_DLSCH, STOP } command;
enum { PDU, PCCH, PDU_MCH, RLF, PDU_BCCH_DLSCH, HO_COMPLETE, STOP } command;
srslte::unique_byte_buffer_t pdu;
uint16_t lcid;
} cmd_msg_t;
@ -448,19 +465,18 @@ private:
std::vector<unique_cell_t> neighbour_cells;
unique_cell_t serving_cell = nullptr;
void set_serving_cell(uint32_t cell_idx);
void set_serving_cell(phy_interface_rrc_lte::phy_cell_t phy_cell);
unique_cell_t remove_neighbour_cell(const uint32_t earfcn, const uint32_t pci);
cell_t* get_neighbour_cell_handle(const uint32_t earfcn, const uint32_t pci);
bool has_neighbour_cell(const uint32_t earfcn, const uint32_t pci);
int find_neighbour_cell(uint32_t earfcn, uint32_t pci);
bool add_neighbour_cell(uint32_t earfcn, uint32_t pci, float rsrp);
bool add_neighbour_cell(phy_interface_rrc_lte::phy_cell_t phy_cell, float rsrp);
bool add_neighbour_cell(phy_meas_t meas);
bool add_neighbour_cell(unique_cell_t new_cell);
void log_neighbour_cells();
void sort_neighbour_cells();
void clean_neighbours();
void delete_last_neighbour();
std::string print_neighbour_cells();
std::set<uint32_t> get_neighbour_pcis(uint32_t earfcn);
bool initiated = false;
asn1::rrc::reest_cause_e m_reest_cause = asn1::rrc::reest_cause_e::nulltype;
@ -469,117 +485,23 @@ private:
bool reestablishment_started = false;
bool reestablishment_successful = false;
// Measurements sub-class
class rrc_meas
{
public:
void init(rrc* parent);
void reset();
bool parse_meas_config(asn1::rrc::meas_cfg_s* meas_config);
void new_phy_meas(uint32_t earfcn, uint32_t pci, float rsrp, float rsrq, uint32_t tti);
void run_tti(uint32_t tti);
bool timer_expired(uint32_t timer_id);
void ho_finish();
void delete_report(uint32_t earfcn, uint32_t pci);
private:
const static int NOF_MEASUREMENTS = 3;
typedef enum { RSRP = 0, RSRQ = 1, BOTH = 2 } quantity_t;
typedef struct {
uint32_t pci;
float q_offset;
} meas_cell_t;
typedef struct {
uint32_t earfcn;
float q_offset;
std::map<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;
// Process HO completition in the background
void process_ho_ra_completed(bool ra_successful);
// Measurements private subclass
class rrc_meas;
rrc_meas* measurements;
// Interface from rrc_meas
void send_srb1_msg(const asn1::rrc::ul_dcch_msg_s& msg);
std::set<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();
void process_cell_meas();
void process_new_cell_meas(std::vector<phy_meas_t>& meas);
srslte::block_queue<std::vector<phy_meas_t> > cell_meas_q;
// Cell selection/reselection functions/variables
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;
srslte::proc_future_t<phy_interface_rrc_lte::cell_search_ret_t> cell_search_fut;
srslte::proc_future_t<void> serv_cell_cfg_fut;
bool discard_serving = false;
};
class rrc::plmn_search_proc
@ -211,6 +212,7 @@ public:
static const char* name() { return "Go Idle"; }
private:
bool wait = false;
rrc* rrc_ptr;
static const uint32_t rlc_flush_timeout = 2000;
@ -238,6 +240,7 @@ public:
srslte::proc_outcome_t init(asn1::rrc::reest_cause_e cause);
srslte::proc_outcome_t step();
static const char* name() { return "Connection re-establishment"; }
uint32_t get_source_earfcn() const { return reest_source_freq; }
private:
enum class state_t { cell_reselection, cell_configuration } state;
@ -246,6 +249,7 @@ private:
asn1::rrc::reest_cause_e reest_cause = asn1::rrc::reest_cause_e::nulltype;
uint16_t reest_rnti = 0;
uint16_t reest_source_pci = 0;
uint32_t reest_source_freq = 0;
srslte::proc_outcome_t step_cell_reselection();
srslte::proc_outcome_t step_cell_configuration();

@ -77,10 +77,7 @@ public:
// RRC interface for PHY
void in_sync() final;
void out_of_sync() final;
void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn = -1, int pci = -1)
{
rrc.new_phy_meas(rsrp, rsrq, tti, earfcn, pci);
};
void new_cell_meas(std::vector<phy_meas_t>& meas) { rrc.new_cell_meas(meas); }
// MAC Interface for PHY
uint16_t get_dl_sched_rnti(uint32_t tti) { return mac.get_dl_sched_rnti(tti); }

@ -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),
"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",
bpo::value<bool>(&args->phy.interpolate_subframe_enabled)->default_value(false),
"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
float rsrq_db = ue_dl.chest_res.rsrq_db;
if (std::isnormal(rsrq_db)) {
if (!(CURRENT_TTI % phy->pcell_report_period) || !std::isnormal(phy->avg_rsrq_db)) {
phy->avg_rsrq_db = rsrq_db;
if (!(CURRENT_TTI % phy->pcell_report_period) || !std::isnormal(phy->avg_rsrq_db[cc_idx])) {
phy->avg_rsrq_db[cc_idx] = rsrq_db;
} else {
phy->avg_rsrq_db = SRSLTE_VEC_CMA(rsrq_db, phy->avg_rsrq_db, CURRENT_TTI % phy->pcell_report_period);
phy->avg_rsrq_db[cc_idx] =
SRSLTE_VEC_CMA(rsrq_db, phy->avg_rsrq_db[cc_idx], CURRENT_TTI % phy->pcell_report_period);
}
}
@ -597,8 +598,8 @@ void cc_worker::update_measurements()
// Store metrics
dl_metrics.n = phy->avg_noise[cc_idx];
dl_metrics.rsrp = phy->avg_rsrp_dbm[cc_idx];
dl_metrics.rsrq = phy->avg_rsrq_db;
dl_metrics.rssi = phy->avg_rssi_dbm;
dl_metrics.rsrq = phy->avg_rsrq_db[cc_idx];
dl_metrics.rssi = phy->avg_rssi_dbm[cc_idx];
dl_metrics.pathloss = phy->pathloss[cc_idx];
dl_metrics.sinr = phy->avg_snr_db_cqi[cc_idx];
dl_metrics.sync_err = ue_dl.chest_res.sync_error;

@ -253,7 +253,7 @@ void phy::get_metrics(phy_metrics_t* m)
{
uint32_t dl_earfcn = 0;
srslte_cell_t cell = {};
get_current_cell(&cell, &dl_earfcn);
sfsync.get_current_cell(&cell, &dl_earfcn);
m->info[0].pci = cell.id;
m->info[0].dl_earfcn = dl_earfcn;
@ -315,19 +315,14 @@ void phy::configure_prach_params()
}
}
void phy::meas_reset()
void phy::set_cells_to_meas(uint32_t earfcn, std::set<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);
}
int phy::meas_stop(uint32_t earfcn, int pci)
{
return sfsync.meas_stop(earfcn, pci);
sfsync.meas_stop();
}
bool phy::cell_select(phy_cell_t* cell)
@ -356,25 +351,6 @@ float phy::get_pathloss_db()
return common.cur_pathloss;
}
void phy::get_current_cell(srslte_cell_t* cell, uint32_t* current_earfcn)
{
sfsync.get_current_cell(cell, current_earfcn);
}
uint32_t phy::get_current_pci()
{
srslte_cell_t cell;
sfsync.get_current_cell(&cell);
return cell.id;
}
uint32_t phy::get_current_earfcn()
{
uint32_t earfcn;
sfsync.get_current_cell(nullptr, &earfcn);
return earfcn;
}
void phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm)
{
n_ta = 0;
@ -479,6 +455,11 @@ void phy::set_config(srslte::phy_cfg_t& config_, uint32_t cc_idx, uint32_t earfc
workers[i]->set_config(cc_idx, config_);
}
// Set inter-frequency measurement primary cell
if (cell_info) {
sfsync.set_inter_frequency_measurement(cc_idx, earfcn, *cell_info);
}
if (cc_idx == 0) {
prach_cfg = config_.prach_cfg;
} else if (cell_info) {

@ -667,15 +667,14 @@ void phy_common::reset()
cur_pusch_power = 0;
sr_last_tx_tti = -1;
cur_pusch_power = 0;
pcell_report_period = 20;
ZERO_OBJECT(pathloss);
ZERO_OBJECT(avg_snr_db_cqi);
ZERO_OBJECT(avg_rsrp);
ZERO_OBJECT(avg_rsrp_dbm);
ZERO_OBJECT(avg_rsrq_db);
ZERO_OBJECT(scell_cfg);
avg_rsrq_db = 0;
pcell_report_period = 20;
ZERO_OBJECT(pending_dl_ack);
ZERO_OBJECT(pending_dl_dai);
ZERO_OBJECT(pending_ul_ack);

@ -259,8 +259,6 @@ void async_scell_recv::set_ue_sync_opts(srslte_ue_sync_t* q, float cfo)
worker_com->args->cfo_loop_pss_tol,
worker_com->args->cfo_loop_pss_conv);
q->strack.pss.chest_on_filter = worker_com->args->sic_pss_enabled;
// Disable CP based CFO estimation during find
if (cfo != 0) {
q->cfo_current_value = cfo / 15000;

@ -45,18 +45,18 @@ intra_measure::~intra_measure()
free(search_buffer);
}
void intra_measure::init(phy_common* common, rrc_interface_phy_lte* rrc, srslte::log* log_h)
void intra_measure::init(phy_common* common_, rrc_interface_phy_lte* rrc_, srslte::log* log_h_)
{
this->rrc = rrc;
this->log_h = log_h;
this->common = common;
this->rrc = rrc_;
this->log_h = log_h_;
this->common = common_;
receive_enabled = false;
// Initialise Reference signal measurement
srslte_refsignal_dl_sync_init(&refsignal_dl_sync);
// Start scell
scell.init(log_h, common->args->sic_pss_enabled, common->args->intra_freq_meas_len_ms, common);
scell.init(log_h, common->args->intra_freq_meas_len_ms);
search_buffer =
(cf_t*)srslte_vec_malloc(common->args->intra_freq_meas_len_ms * SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB) * sizeof(cf_t));
@ -83,52 +83,27 @@ void intra_measure::set_primary_cell(uint32_t earfcn, srslte_cell_t cell)
{
this->current_earfcn = earfcn;
current_sflen = (uint32_t)SRSLTE_SF_LEN_PRB(cell.nof_prb);
this->primary_cell = cell;
this->serving_cell = cell;
}
void intra_measure::clear_cells()
void intra_measure::meas_stop()
{
active_pci.clear();
receive_enabled = false;
receiving = false;
receive_cnt = 0;
srslte_ringbuffer_reset(&ring_buffer);
}
void intra_measure::add_cell(int pci)
{
if (std::find(active_pci.begin(), active_pci.end(), pci) == active_pci.end()) {
active_pci.push_back(pci);
receive_enabled = true;
Info("INTRA: Starting intra-frequency measurement for pci=%d\n", pci);
} else {
Debug("INTRA: Requested to start already existing intra-frequency measurement for PCI=%d\n", pci);
}
}
int intra_measure::get_offset(uint32_t pci)
{
for (auto& i : info) {
if (i.pci == pci) {
return i.offset;
}
if (log_h) {
log_h->info("INTRA: Disabled neighbour cell search for EARFCN %d\n", get_earfcn());
}
return -1;
}
void intra_measure::rem_cell(int pci)
void intra_measure::set_cells_to_meas(std::set<uint32_t>& pci)
{
auto newEnd = std::remove(active_pci.begin(), active_pci.end(), pci);
if (newEnd != active_pci.end()) {
active_pci.erase(newEnd, active_pci.end());
if (active_pci.empty()) {
receive_enabled = false;
}
Info("INTRA: Stopping intra-frequency measurement for pci=%d. Number of cells: %zu\n", pci, active_pci.size());
} else {
Warning("INTRA: Requested to stop non-existing intra-frequency measurement for PCI=%d\n", pci);
}
active_pci_mutex.lock();
active_pci = pci;
active_pci_mutex.unlock();
receive_enabled = true;
log_h->info("INTRA: Received list of %lu neighbour cells to measure in EARFCN %d.\n", pci.size(), current_earfcn);
}
void intra_measure::write(uint32_t tti, cf_t* data, uint32_t nsamples)
@ -137,7 +112,6 @@ void intra_measure::write(uint32_t tti, cf_t* data, uint32_t nsamples)
if ((tti % common->args->intra_freq_meas_period_ms) == 0) {
receiving = true;
receive_cnt = 0;
measure_tti = tti;
srslte_ringbuffer_reset(&ring_buffer);
}
if (receiving) {
@ -157,6 +131,8 @@ void intra_measure::write(uint32_t tti, cf_t* data, uint32_t nsamples)
void intra_measure::run_thread()
{
std::set<uint32_t> cells_to_measure = {};
while (running) {
if (running) {
tti_sync.wait();
@ -164,77 +140,62 @@ void intra_measure::run_thread()
if (running) {
active_pci_mutex.lock();
cells_to_measure = active_pci;
active_pci_mutex.unlock();
// Read data from buffer and find cells in it
srslte_ringbuffer_read(
&ring_buffer, search_buffer, common->args->intra_freq_meas_len_ms * current_sflen * sizeof(cf_t));
int found_cells = scell.find_cells(
search_buffer, common->rx_gain_offset, primary_cell, common->args->intra_freq_meas_len_ms, info);
// Detect new cells using PSS/SSS
std::set<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;
// Look for other cells not found automatically
// Using Cell Reference signal synchronization for all known active PCI
for (auto q : active_pci) {
srslte_cell_t cell = primary_cell;
cell.id = q;
std::vector<rrc_interface_phy_lte::phy_meas_t> neigbhour_cells = {};
// Use Cell Reference signal to measure cells in the time domain for all known active PCI
for (auto id : cells_to_measure) {
// Do not measure serving cell here since it's measured by workers
if (id == serving_cell.id) {
continue;
}
srslte_cell_t cell = serving_cell;
cell.id = id;
srslte_refsignal_dl_sync_set_cell(&refsignal_dl_sync, cell);
srslte_refsignal_dl_sync_run(
&refsignal_dl_sync, search_buffer, common->args->intra_freq_meas_len_ms * current_sflen);
if (refsignal_dl_sync.found) {
Info("INTRA: Found neighbour cell: PCI=%03d, RSRP=%5.1f dBm, RSRQ=%5.1f, peak_idx=%5d, CFO=%+.1fHz\n",
cell.id,
refsignal_dl_sync.rsrp_dBfs,
refsignal_dl_sync.rsrq_dB,
rrc_interface_phy_lte::phy_meas_t m = {};
m.pci = cell.id;
m.earfcn = current_earfcn;
m.rsrp = refsignal_dl_sync.rsrp_dBfs - common->rx_gain_offset;
m.rsrq = refsignal_dl_sync.rsrq_dB;
neigbhour_cells.push_back(m);
Info("INTRA: Found neighbour cell: EARFCN=%d, PCI=%03d, RSRP=%5.1f dBm, RSRQ=%5.1f, peak_idx=%5d, "
"CFO=%+.1fHz\n",
m.earfcn,
m.pci,
m.rsrp,
m.rsrq,
refsignal_dl_sync.peak_index,
refsignal_dl_sync.cfo_Hz);
bool found = false;
float weakest_rsrp_value = +INFINITY;
uint32_t weakest_rsrp_index = 0;
// Try to find PCI in info list
for (int i = 0; i < found_cells && !found; i++) {
// Finds cell, update
if (info[i].pci == cell.id) {
info[i].rsrp = refsignal_dl_sync.rsrp_dBfs;
info[i].rsrq = refsignal_dl_sync.rsrq_dB;
info[i].offset = refsignal_dl_sync.peak_index;
found = true;
} else if (weakest_rsrp_value > info[i].rsrp) {
// Update weakest
weakest_rsrp_value = info[i].rsrp;
weakest_rsrp_index = i;
}
}
if (!found) {
// If number of cells exceeds
if (found_cells >= scell_recv::MAX_CELLS) {
// overwrite weakest cell if stronger
if (refsignal_dl_sync.rsrp_dBfs > weakest_rsrp_value) {
info[weakest_rsrp_index].pci = cell.id;
info[weakest_rsrp_index].rsrp = refsignal_dl_sync.rsrp_dBfs;
info[weakest_rsrp_index].rsrq = refsignal_dl_sync.rsrq_dB;
info[weakest_rsrp_index].offset = refsignal_dl_sync.peak_index;
} else {
// Ignore measurement
}
} else {
// Otherwise append cell
info[found_cells].pci = cell.id;
info[found_cells].rsrp = refsignal_dl_sync.rsrp_dBfs;
info[found_cells].rsrq = refsignal_dl_sync.rsrq_dB;
info[found_cells].offset = refsignal_dl_sync.peak_index;
found_cells++;
}
}
}
}
// Send measurements to RRC
for (int i = 0; i < found_cells; i++) {
rrc->new_phy_meas(info[i].rsrp, info[i].rsrq, measure_tti, current_earfcn, info[i].pci);
if (neigbhour_cells.size() > 0) {
rrc->new_cell_meas(neigbhour_cells);
}
}
}

@ -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"
#define Error(fmt, ...) \
if (SRSLTE_DEBUG_ENABLED) \
log_h->error(fmt, ##__VA_ARGS__)
#define Warning(fmt, ...) \
if (SRSLTE_DEBUG_ENABLED) \
log_h->warning(fmt, ##__VA_ARGS__)
#define Info(fmt, ...) \
if (SRSLTE_DEBUG_ENABLED) \
log_h->info(fmt, ##__VA_ARGS__)
#define Debug(fmt, ...) \
if (SRSLTE_DEBUG_ENABLED) \
log_h->debug(fmt, ##__VA_ARGS__)
namespace srsue {
namespace scell {
@ -41,10 +28,9 @@ namespace scell {
* Secondary cell receiver
*/
void scell_recv::init(srslte::log* _log_h, bool _sic_pss_enabled, uint32_t max_sf_window, phy_common* worker_com)
void scell_recv::init(srslte::log* _log_h, uint32_t max_sf_window)
{
log_h = _log_h;
sic_pss_enabled = _sic_pss_enabled;
// and a separate ue_sync instance
@ -53,14 +39,13 @@ void scell_recv::init(srslte::log* _log_h, bool _sic_pss_enabled, uint32_t max_s
sf_buffer[0] = (cf_t*)srslte_vec_malloc(sizeof(cf_t) * max_sf_size);
if (!sf_buffer[0]) {
ERROR("Error allocating %d samples for scell\n", max_sf_size);
log_h->error("Error allocating %d samples for scell\n", max_sf_size);
return;
}
measure_p.init(sf_buffer, log_h, 1, worker_com, max_sf_window);
// do this different we don't need all this search window.
if (srslte_sync_init(&sync_find, max_sf_window * max_sf_size, 5 * max_sf_size, max_fft_sz)) {
ERROR("Error initiating sync_find\n");
log_h->error("Error initiating sync_find\n");
return;
}
srslte_sync_set_sss_algorithm(&sync_find, SSS_FULL);
@ -92,179 +77,86 @@ void scell_recv::deinit()
void scell_recv::reset()
{
current_fft_sz = 0;
measure_p.reset();
}
int scell_recv::find_cells(cf_t* input_buffer,
float rx_gain_offset,
srslte_cell_t cell,
uint32_t nof_sf,
cell_info_t cells[MAX_CELLS])
std::set<uint32_t>
scell_recv::find_cells(const cf_t* input_buffer, const srslte_cell_t serving_cell, const uint32_t nof_sf)
{
uint32_t fft_sz = srslte_symbol_sz(cell.nof_prb);
std::set<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);
if (fft_sz != current_fft_sz) {
if (srslte_sync_resize(&sync_find, nof_sf * sf_len, 5 * sf_len, fft_sz)) {
ERROR("Error resizing sync nof_sf=%d, sf_len=%d, fft_sz=%d\n", nof_sf, sf_len, fft_sz);
return SRSLTE_ERROR;
log_h->error("Error resizing sync nof_sf=%d, sf_len=%d, fft_sz=%d\n", nof_sf, sf_len, fft_sz);
return found_cell_ids;
}
current_fft_sz = fft_sz;
}
int nof_cells = 0;
uint32_t peak_idx = 0;
uint32_t sf_idx = 0;
int cell_id = 0;
srslte_cell_t found_cell;
found_cell = cell;
measure_p.set_rx_gain_offset(rx_gain_offset);
for (uint32_t n_id_2 = 0; n_id_2 < 3; n_id_2++) {
found_cell.id = 10000;
if (n_id_2 != (cell.id % 3) || sic_pss_enabled) {
if (n_id_2 != (serving_cell.id % 3)) {
srslte_sync_set_N_id_2(&sync_find, n_id_2);
srslte_sync_find_ret_t sync_res;
do {
srslte_sync_reset(&sync_find);
srslte_sync_cfo_reset(&sync_find);
srslte_sync_reset(&sync_find);
srslte_sync_cfo_reset(&sync_find);
sync_res = SRSLTE_SYNC_NOFOUND;
bool sss_detected = false;
cell_id = 0;
float max_peak = -1;
uint32_t max_sf5 = 0;
uint32_t max_sf_idx = 0;
sync_res = SRSLTE_SYNC_NOFOUND;
bool sss_detected = false;
cell_id = 0;
float max_peak = -1;
float sss_correlation_peak_max = 0.0f;
float sss_correlation_peak_max = 0.0f;
for (uint32_t sf5_cnt = 0; sf5_cnt < nof_sf / 5; sf5_cnt++) {
sync_res = srslte_sync_find(&sync_find, input_buffer, sf5_cnt * 5 * sf_len, &peak_idx);
Debug("INTRA: n_id_2=%d, cnt=%d/%d, sync_res=%d, sf_idx=%d, peak_idx=%d, peak_value=%f, sss_detected=%d\n",
n_id_2,
sf5_cnt,
nof_sf / 5,
sync_res,
srslte_sync_get_sf_idx(&sync_find),
peak_idx,
sync_find.peak_value,
srslte_sync_sss_detected(&sync_find));
for (uint32_t sf5_cnt = 0; sf5_cnt < nof_sf / 5; sf5_cnt++) {
sync_res = srslte_sync_find(&sync_find, input_buffer, sf5_cnt * 5 * sf_len, &peak_idx);
if (sync_res == SRSLTE_SYNC_ERROR) {
log_h->error("INTRA: Error calling sync_find()\n");
return found_cell_ids;
}
if (sync_find.peak_value > max_peak && sync_res == SRSLTE_SYNC_FOUND &&
srslte_sync_sss_detected(&sync_find)) {
max_sf5 = sf5_cnt;
max_sf_idx = srslte_sync_get_sf_idx(&sync_find);
if (sync_find.peak_value > max_peak && sync_res == SRSLTE_SYNC_FOUND && srslte_sync_sss_detected(&sync_find)) {
// Uses the cell ID from the highest SSS correlation peak
if (sss_correlation_peak_max < srslte_sync_sss_correlation_peak(&sync_find)) {
// Set the cell ID
cell_id = srslte_sync_get_cell_id(&sync_find);
// Uses the cell ID from the highest SSS correlation peak
if (sss_correlation_peak_max < srslte_sync_sss_correlation_peak(&sync_find)) {
// Set the cell ID
cell_id = srslte_sync_get_cell_id(&sync_find);
// Update the maximum value
sss_correlation_peak_max = srslte_sync_sss_correlation_peak(&sync_find);
}
sss_detected = true;
// Update the maximum value
sss_correlation_peak_max = srslte_sync_sss_correlation_peak(&sync_find);
}
sss_detected = true;
}
// If the SSS was not detected, the cell id is not reliable. So, consider no sync found
if (!sss_detected) {
sync_res = SRSLTE_SYNC_NOFOUND;
}
switch (sync_res) {
case SRSLTE_SYNC_ERROR:
return SRSLTE_ERROR;
ERROR("Error finding correlation peak\n");
return SRSLTE_ERROR;
case SRSLTE_SYNC_FOUND:
sf_idx = (10 - max_sf_idx - 5 * (max_sf5 % 2)) % 10;
if (cell_id >= 0) {
// We found the same cell as before, look another N_id_2
if ((uint32_t)cell_id == found_cell.id || (uint32_t)cell_id == cell.id) {
Debug("INTRA: n_id_2=%d, PCI=%d, found_cell.id=%d, cell.id=%d\n",
n_id_2,
cell_id,
found_cell.id,
cell.id);
sync_res = SRSLTE_SYNC_NOFOUND;
} else {
// We found a new cell ID
found_cell.id = (uint32_t)cell_id;
found_cell.nof_ports = 1; // Use port 0 only for measurement
measure_p.set_cell(found_cell);
// Correct CFO
/*
srslte_cfo_correct(&sync_find.cfo_corr_frame,
input_buffer,
input_cfo_corrected,
-srslte_sync_get_cfo(&sync_find)/sync_find.fft_size);
*/
switch (measure_p.run_multiple_subframes(input_buffer, peak_idx, sf_idx, nof_sf)) {
default:
// Consider a cell to be detectable 8.1.2.2.1.1 from 36.133. Currently only using first condition
if (measure_p.rsrp() > ABSOLUTE_RSRP_THRESHOLD_DBM) {
cells[nof_cells].pci = found_cell.id;
cells[nof_cells].rsrp = measure_p.rsrp();
cells[nof_cells].rsrq = measure_p.rsrq();
cells[nof_cells].offset = measure_p.frame_st_idx();
Info("INTRA: Found neighbour cell %d: PCI=%03d, RSRP=%5.1f dBm, SNR=%5.1f dB, peak_idx=%5d, "
"peak_value=%3.2f, "
"sf=%d, max_sf=%d, n_id_2=%d, CFO=%6.1f/%6.1fHz\n",
nof_cells,
cell_id,
measure_p.rsrp(),
measure_p.snr(),
measure_p.frame_st_idx(),
sync_find.peak_value,
sf_idx,
max_sf5,
n_id_2,
15000 * srslte_sync_get_cfo(&sync_find),
15000 * measure_p.cfo());
nof_cells++;
/*
if (sic_pss_enabled) {
srslte_pss_sic(&sync_find.pss, &input_buffer[sf5_cnt * 5 * sf_len + sf_len / 2 - fft_sz]);
}*/
} else {
Info("INTRA: Found neighbour cell but RSRP=%.1f dBm is below threshold (%.1f dBm)\n",
measure_p.rsrp(),
ABSOLUTE_RSRP_THRESHOLD_DBM);
}
break;
case measure::ERROR:
Error("INTRA: Measuring neighbour cell\n");
return SRSLTE_ERROR;
}
}
} else {
sync_res = SRSLTE_SYNC_NOFOUND;
}
break;
case SRSLTE_SYNC_FOUND_NOSPACE:
/* If a peak was found but there is not enough space for SSS/CP detection, discard a few samples */
break;
default:
break;
}
} while (sync_res == SRSLTE_SYNC_FOUND && sic_pss_enabled && nof_cells < MAX_CELLS);
log_h->debug("INTRA: n_id_2=%d, cnt=%d/%d, sync_res=%d, cell_id=%d, sf_idx=%d, peak_idx=%d, peak_value=%f, "
"sss_detected=%d\n",
n_id_2,
sf5_cnt,
nof_sf / 5,
sync_res,
cell_id,
srslte_sync_get_sf_idx(&sync_find),
peak_idx,
sync_find.peak_value,
srslte_sync_sss_detected(&sync_find));
}
// If the SSS was not detected, the serving_cell id is not reliable. So, consider no sync found
if (sync_res == SRSLTE_SYNC_FOUND && sss_detected && cell_id >= 0) {
// We have found a new cell, add to the list
found_cell_ids.insert((uint32_t)cell_id);
log_h->debug("INTRA: Detected new cell_id=%d using PSS/SSS\n", cell_id);
}
}
}
return nof_cells;
return found_cell_ids;
}
} // namespace scell

@ -297,7 +297,7 @@ void sf_worker::update_measurements()
30)
: 0;
if (std::isnormal(rssi_dbm)) {
phy->avg_rssi_dbm = SRSLTE_VEC_EMA(rssi_dbm, phy->avg_rssi_dbm, phy->args->snr_ema_coeff);
phy->avg_rssi_dbm[0] = SRSLTE_VEC_EMA(rssi_dbm, phy->avg_rssi_dbm[0], phy->args->snr_ema_coeff);
}
if (!rssi_read_cnt) {
@ -310,27 +310,30 @@ void sf_worker::update_measurements()
}
// Run measurements in all carriers
std::vector<rrc_interface_phy_lte::phy_meas_t> serving_cells = {};
for (uint32_t cc_idx = 0; cc_idx < cc_workers.size(); cc_idx++) {
bool active = (cc_idx == 0 || phy->scell_cfg[cc_idx].configured);
// Update measurement of the Component Carrier
cc_workers[cc_idx]->update_measurements();
// Send measurements
if ((tti % phy->pcell_report_period) == phy->pcell_report_period - 1) {
if (cc_idx == 0) {
// Send report for PCell
phy->stack->new_phy_meas(phy->avg_rsrp_dbm[0], phy->avg_rsrq_db, tti);
} else {
// Send report for SCell (if enabled)
if (phy->scell_cfg[cc_idx].enabled) {
phy->stack->new_phy_meas(phy->avg_rsrp_dbm[cc_idx],
phy->avg_rsrq_db,
tti,
phy->scell_cfg[cc_idx].earfcn,
phy->scell_cfg[cc_idx].pci);
}
// Send measurements for serving cells
if (active && ((tti % phy->pcell_report_period) == phy->pcell_report_period - 1)) {
rrc_interface_phy_lte::phy_meas_t meas = {};
meas.rsrp = phy->avg_rsrp_dbm[cc_idx];
meas.rsrq = phy->avg_rsrq_db[cc_idx];
// Save EARFCN and PCI for secondary cells, primary cell has earfcn=0
if (cc_idx > 0) {
meas.earfcn = phy->scell_cfg[cc_idx].earfcn;
meas.pci = phy->scell_cfg[cc_idx].pci;
}
serving_cells.push_back(meas);
}
}
// Send report to stack
if (not serving_cells.empty()) {
phy->stack->new_cell_meas(serving_cells);
}
// Check in-sync / out-sync conditions
if (phy->avg_rsrp_dbm[0] > phy->args->in_sync_rsrp_dbm_th && phy->avg_snr_db_cqi[0] > phy->args->in_sync_snr_db_th) {

@ -98,7 +98,11 @@ void sync::init(srslte::radio_interface_phy* _radio,
sfn_p.init(&ue_sync, sf_buffer[0], log_h);
// Start intra-frequency measurement
intra_freq_meas.init(worker_com, stack, log_h);
for (uint32_t i = 0; i < worker_com->args->nof_carriers; i++) {
auto q = new scell::intra_measure;
q->init(worker_com, stack, log_h);
intra_freq_meas.push_back(std::unique_ptr<scell::intra_measure>(q));
}
reset();
running = true;
@ -136,7 +140,9 @@ sync::~sync()
void sync::stop()
{
worker_com->semaphore.wait_all();
intra_freq_meas.stop();
for (auto& q : intra_freq_meas) {
q->stop();
}
running = false;
wait_thread_finish();
}
@ -204,6 +210,9 @@ phy_interface_rrc_lte::cell_search_ret_t sync::cell_search(phy_interface_rrc_lte
phy_state.go_idle();
worker_com->reset();
// Stop all intra-frequency measurement before changing frequency
meas_stop();
try {
if (current_earfcn != (int)earfcn.at(cellsearch_earfcn_index)) {
current_earfcn = (int)earfcn[cellsearch_earfcn_index];
@ -224,7 +233,9 @@ phy_interface_rrc_lte::cell_search_ret_t sync::cell_search(phy_interface_rrc_lte
case search::CELL_FOUND:
// If a cell is found, configure it, synchronize and measure it
if (set_cell()) {
intra_freq_meas.set_primary_cell(current_earfcn, cell);
// Reconfigure first intra-frequency measurement
intra_freq_meas[0]->set_primary_cell(current_earfcn, cell);
Info("Cell Search: Setting sampling rate and synchronizing SFN...\n");
set_sampling_rate();
@ -234,7 +245,7 @@ phy_interface_rrc_lte::cell_search_ret_t sync::cell_search(phy_interface_rrc_lte
log_h->info("Cell Search: Sync OK. Camping on cell PCI=%d\n", cell.id);
if (found_cell) {
found_cell->earfcn = current_earfcn;
found_cell->cell = cell;
found_cell->pci = cell.id;
}
ret.found = phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND;
} else {
@ -282,14 +293,11 @@ bool sync::cell_select(phy_interface_rrc_lte::phy_cell_t* new_cell)
if (!new_cell) {
Info("Cell Select: Starting cell resynchronization\n");
} else {
if (!srslte_cell_isvalid(&new_cell->cell)) {
log_h->error("Cell Select: Invalid cell. ID=%d, PRB=%d, ports=%d\n", cell.id, cell.nof_prb, cell.nof_ports);
if (!srslte_cellid_isvalid(new_cell->pci)) {
log_h->error("Cell Select: Invalid cell_id=%d\n", cell.id);
return ret;
}
Info("Cell Select: Starting cell selection for PCI=%d, n_prb=%d, EARFCN=%d\n",
new_cell->cell.id,
new_cell->cell.nof_prb,
new_cell->earfcn);
Info("Cell Select: Starting cell selection for PCI=%d, EARFCN=%d\n", new_cell->pci, new_cell->earfcn);
}
// Wait for any pending PHICH
@ -309,9 +317,9 @@ bool sync::cell_select(phy_interface_rrc_lte::phy_cell_t* new_cell)
/* Reconfigure cell if necessary */
if (new_cell) {
if (new_cell->cell.id != cell.id) {
Info("Cell Select: Reconfiguring cell\n");
cell = new_cell->cell;
if (new_cell->pci != cell.id) {
Info("Cell Select: Reconfiguring cell ID\n");
cell.id = new_cell->pci;
if (!set_cell()) {
Error("Cell Select: Reconfiguring cell\n");
return ret;
@ -321,6 +329,10 @@ bool sync::cell_select(phy_interface_rrc_lte::phy_cell_t* new_cell)
/* Select new frequency if necessary */
if ((int)new_cell->earfcn != current_earfcn) {
current_earfcn = new_cell->earfcn;
// Stop all intra-frequency measurement before changing frequency
meas_stop();
Info("Cell Select: Setting new frequency EARFCN=%d\n", new_cell->earfcn);
if (!set_frequency()) {
Error("Cell Select: Setting new frequency EARFCN=%d\n", new_cell->earfcn);
@ -328,14 +340,14 @@ bool sync::cell_select(phy_interface_rrc_lte::phy_cell_t* new_cell)
}
}
/* Reconfigure intra-frequency measurement */
intra_freq_meas.set_primary_cell(current_earfcn, cell);
// Reconfigure first intra-frequency measurement
intra_freq_meas[0]->set_primary_cell(current_earfcn, cell);
}
/* Change sampling rate if necessary */
if (srate_mode != SRATE_CAMP) {
set_sampling_rate();
log_h->info("Cell Select: Setting CAMPING sampling rate\n");
set_sampling_rate();
}
/* SFN synchronization */
@ -375,7 +387,8 @@ bool sync::cell_is_camping()
void sync::run_thread()
{
sf_worker* worker = nullptr;
cf_t* buffer[SRSLTE_MAX_RADIOS][SRSLTE_MAX_PORTS] = {NULL};
cf_t* buffer[SRSLTE_MAX_RADIOS][SRSLTE_MAX_PORTS] = {};
srslte_cell_t temp_cell = {};
bool is_end_of_burst = false;
bool force_camping_sfn_sync = false;
@ -419,8 +432,18 @@ void sync::run_thread()
/* SFN synchronization using MIB. run_subframe() receives and processes 1 subframe
* and returns
*/
switch (sfn_p.run_subframe(&cell, &tti, mib)) {
temp_cell = cell;
switch (sfn_p.run_subframe(&temp_cell, &tti, mib)) {
case sfn_sync::SFN_FOUND:
if (memcmp(&cell, &temp_cell, sizeof(srslte_cell_t))) {
srslte_cell_fprint(stdout, &cell, 0);
srslte_cell_fprint(stdout, &temp_cell, 0);
log_h->error("Detected cell during SFN synchronization differs from configured cell. Cell reselection to "
"cells with different MIB is not supported\n");
log_h->console("Detected cell during SFN synchronization differs from configured cell. Cell reselection "
"to cells with different MIB is not supported\n");
phy_state.state_exit(false);
}
stack->in_sync();
phy_state.state_exit();
break;
@ -459,16 +482,22 @@ void sync::run_thread()
// Force decode MIB if required
if (force_camping_sfn_sync) {
uint32_t _tti = 0;
srslte_cell_t temp_cell = {};
uint32_t _tti = 0;
temp_cell = cell;
sync::sfn_sync::ret_code ret = sfn_p.decode_mib(&temp_cell, &_tti, buffer[0], mib);
if (ret == sfn_sync::SFN_FOUND) {
// Force tti
tti = _tti;
// Disable
force_camping_sfn_sync = false;
if (memcmp(&cell, &temp_cell, sizeof(srslte_cell_t))) {
log_h->error("Detected cell during SFN synchronization differs from configured cell. Cell "
"reselection to cells with different MIB is not supported\n");
log_h->console("Detected cell during SFN synchronization differs from configured cell. Cell "
"reselection to cells with different MIB is not supported\n");
}
}
}
@ -561,12 +590,10 @@ void sync::run_thread()
workers_pool->start_worker(worker);
// Save signal for Intra-frequency measurement
if ((tti % 5) == 0 && worker_com->args->sic_pss_enabled) {
srslte_pss_sic(&ue_sync.strack.pss,
&buffer[0][0][SRSLTE_SF_LEN_PRB(cell.nof_prb) / 2 - ue_sync.strack.fft_size]);
}
if (srslte_cell_isvalid(&cell)) {
intra_freq_meas.write(tti, buffer[0][0], SRSLTE_SF_LEN_PRB(cell.nof_prb));
for (size_t i = 0; i < intra_freq_meas.size(); i++) {
intra_freq_meas[i]->write(tti, worker->get_buffer(i, 0), SRSLTE_SF_LEN_PRB(cell.nof_prb));
}
}
break;
case 0:
@ -723,12 +750,12 @@ void sync::set_agc_enable(bool enable)
}
}
void sync::set_time_adv_sec(float time_adv_sec)
void sync::set_time_adv_sec(float time_adv_sec_)
{
// If transmitting earlier, transmit less samples to align time advance. If transmit later just delay next TX
next_offset = (int)round((this->time_adv_sec - time_adv_sec) * srslte_sampling_freq_hz(cell.nof_prb));
this->next_time_adv_sec = time_adv_sec;
Info("Applying time_adv_sec=%.1f us, next_offset=%d\n", time_adv_sec * 1e6, next_offset);
next_offset = (int)round((time_adv_sec - time_adv_sec_) * srslte_sampling_freq_hz(cell.nof_prb));
next_time_adv_sec = time_adv_sec_;
Info("Applying time_adv_sec=%.1f us, next_offset=%d\n", time_adv_sec_ * 1e6, next_offset);
}
float sync::get_tx_cfo()
@ -766,8 +793,6 @@ void sync::set_ue_sync_opts(srslte_ue_sync_t* q, float cfo)
worker_com->args->cfo_loop_pss_tol,
worker_com->args->cfo_loop_pss_conv);
q->strack.pss.chest_on_filter = worker_com->args->sic_pss_enabled;
// Disable CP based CFO estimation during find
if (cfo != 0) {
q->cfo_current_value = cfo / 15000;
@ -802,6 +827,11 @@ bool sync::set_cell()
return false;
}
if (!srslte_cell_isvalid(&cell)) {
Error("SYNC: Setting cell: invalid cell (nof_prb=%d, pci=%d, ports=%d)\n", cell.nof_prb, cell.id, cell.nof_ports);
return false;
}
// Set cell in all objects
if (srslte_ue_sync_set_cell(&ue_sync, cell)) {
Error("SYNC: Setting cell: initiating ue_sync\n");
@ -826,15 +856,15 @@ bool sync::set_cell()
return true;
}
void sync::set_earfcn(std::vector<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;
this->ul_freq = ul_freq;
dl_freq = dl_freq_;
ul_freq = ul_freq_;
}
bool sync::set_frequency()
@ -842,9 +872,9 @@ bool sync::set_frequency()
double set_dl_freq = 0;
double set_ul_freq = 0;
if (this->dl_freq > 0 && this->ul_freq > 0) {
set_dl_freq = this->dl_freq;
set_ul_freq = this->ul_freq;
if (dl_freq > 0 && ul_freq > 0) {
set_dl_freq = dl_freq;
set_ul_freq = ul_freq;
} else {
set_dl_freq = 1e6 * srslte_band_fd(current_earfcn);
if (srslte_band_is_tdd(srslte_band_get_band(current_earfcn))) {
@ -907,13 +937,13 @@ uint32_t sync::get_current_tti()
return tti;
}
void sync::get_current_cell(srslte_cell_t* cell, uint32_t* earfcn)
void sync::get_current_cell(srslte_cell_t* cell_, uint32_t* earfcn_)
{
if (cell) {
*cell = this->cell;
if (cell_) {
*cell_ = cell;
}
if (earfcn) {
*earfcn = current_earfcn;
if (earfcn_) {
*earfcn_ = current_earfcn;
}
}
@ -977,13 +1007,13 @@ sync::search::~search()
srslte_ue_cellsearch_free(&cs);
}
void sync::search::init(cf_t* buffer[SRSLTE_MAX_PORTS], srslte::log* log_h, uint32_t nof_rx_antennas, sync* parent)
void sync::search::init(cf_t* buffer_[SRSLTE_MAX_PORTS], srslte::log* log_h_, uint32_t nof_rx_antennas, sync* parent)
{
this->log_h = log_h;
this->p = parent;
log_h = log_h_;
p = parent;
for (int i = 0; i < SRSLTE_MAX_PORTS; i++) {
this->buffer[i] = buffer[i];
buffer[i] = buffer_[i];
}
if (srslte_ue_cellsearch_init_multi(&cs, 8, radio_recv_callback, nof_rx_antennas, parent)) {
@ -1025,15 +1055,12 @@ void sync::search::set_agc_enable(bool enable)
}
}
sync::search::ret_code sync::search::run(srslte_cell_t* cell, std::array<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) {
return ERROR;
}
srslte_cell_t new_cell = {};
srslte_ue_cellsearch_result_t found_cells[3];
bzero(cell, sizeof(srslte_cell_t));
bzero(found_cells, 3 * sizeof(srslte_ue_cellsearch_result_t));
if (p->srate_mode != SRATE_FIND) {
@ -1065,19 +1092,19 @@ sync::search::ret_code sync::search::run(srslte_cell_t* cell, std::array<uint8_t
return CELL_NOT_FOUND;
}
// Save result
cell->id = found_cells[max_peak_cell].cell_id;
cell->cp = found_cells[max_peak_cell].cp;
cell->frame_type = found_cells[max_peak_cell].frame_type;
float cfo = found_cells[max_peak_cell].cfo;
new_cell.id = found_cells[max_peak_cell].cell_id;
new_cell.cp = found_cells[max_peak_cell].cp;
new_cell.frame_type = found_cells[max_peak_cell].frame_type;
float cfo = found_cells[max_peak_cell].cfo;
log_h->console("\n");
Info("SYNC: PSS/SSS detected: Mode=%s, PCI=%d, CFO=%.1f KHz, CP=%s\n",
cell->frame_type ? "TDD" : "FDD",
cell->id,
new_cell.frame_type ? "TDD" : "FDD",
new_cell.id,
cfo / 1000,
srslte_cp_string(cell->cp));
srslte_cp_string(new_cell.cp));
if (srslte_ue_mib_sync_set_cell(&ue_mib_sync, *cell)) {
if (srslte_ue_mib_sync_set_cell(&ue_mib_sync, new_cell)) {
Error("SYNC: Setting UE MIB cell\n");
return ERROR;
}
@ -1089,9 +1116,9 @@ sync::search::ret_code sync::search::run(srslte_cell_t* cell, std::array<uint8_t
/* Find and decode MIB */
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) {
srslte_pbch_mib_unpack(bch_payload.data(), cell, NULL);
srslte_pbch_mib_unpack(bch_payload.data(), &new_cell, NULL);
// pack MIB and store inplace for PCAP dump
std::array<uint8_t, SRSLTE_BCH_PAYLOAD_LEN / 8> mib_packed;
srslte_bit_pack_vector(bch_payload.data(), mib_packed.data(), SRSLTE_BCH_PAYLOAD_LEN);
@ -1099,23 +1126,29 @@ sync::search::ret_code sync::search::run(srslte_cell_t* cell, std::array<uint8_t
fprintf(stdout,
"Found Cell: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n",
cell->frame_type ? "TDD" : "FDD",
cell->id,
cell->nof_prb,
cell->nof_ports,
new_cell.frame_type ? "TDD" : "FDD",
new_cell.id,
new_cell.nof_prb,
new_cell.nof_ports,
cfo / 1000);
Info("SYNC: MIB Decoded: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n",
cell->frame_type ? "TDD" : "FDD",
cell->id,
cell->nof_prb,
cell->nof_ports,
new_cell.frame_type ? "TDD" : "FDD",
new_cell.id,
new_cell.nof_prb,
new_cell.nof_ports,
cfo / 1000);
if (!srslte_cell_isvalid(cell)) {
if (!srslte_cell_isvalid(&new_cell)) {
Error("SYNC: Detected invalid cell.\n");
return CELL_NOT_FOUND;
}
// Save cell pointer
if (cell_) {
*cell_ = new_cell;
}
return CELL_FOUND;
} else if (ret == 0) {
Warning("SYNC: Found PSS but could not decode PBCH\n");
@ -1135,27 +1168,27 @@ sync::sfn_sync::~sfn_sync()
srslte_ue_mib_free(&ue_mib);
}
void sync::sfn_sync::init(srslte_ue_sync_t* ue_sync,
cf_t* buffer[SRSLTE_MAX_PORTS],
srslte::log* log_h,
void sync::sfn_sync::init(srslte_ue_sync_t* ue_sync_,
cf_t* buffer_[SRSLTE_MAX_PORTS],
srslte::log* log_h_,
uint32_t nof_subframes)
{
this->log_h = log_h;
this->ue_sync = ue_sync;
this->timeout = nof_subframes;
log_h = log_h_;
ue_sync = ue_sync_;
timeout = nof_subframes;
for (int p = 0; p < SRSLTE_MAX_PORTS; p++) {
this->buffer[p] = buffer[p];
buffer[p] = buffer_[p];
}
if (srslte_ue_mib_init(&ue_mib, this->buffer, SRSLTE_MAX_PRB)) {
if (srslte_ue_mib_init(&ue_mib, buffer, SRSLTE_MAX_PRB)) {
Error("SYNC: Initiating UE MIB decoder\n");
}
}
bool sync::sfn_sync::set_cell(srslte_cell_t cell)
bool sync::sfn_sync::set_cell(srslte_cell_t cell_)
{
if (srslte_ue_mib_set_cell(&ue_mib, cell)) {
if (srslte_ue_mib_set_cell(&ue_mib, cell_)) {
Error("SYNC: Setting cell: initiating ue_mib\n");
return false;
}
@ -1169,7 +1202,7 @@ void sync::sfn_sync::reset()
srslte_ue_mib_reset(&ue_mib);
}
sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t* cell,
sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t* cell_,
uint32_t* tti_cnt,
std::array<uint8_t, SRSLTE_BCH_PAYLOAD_LEN>& bch_payload,
bool sfidx_only)
@ -1182,7 +1215,7 @@ sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t*
}
if (ret == 1) {
sync::sfn_sync::ret_code ret2 = decode_mib(cell, tti_cnt, NULL, bch_payload, sfidx_only);
sync::sfn_sync::ret_code ret2 = decode_mib(cell_, tti_cnt, NULL, bch_payload, sfidx_only);
if (ret2 != SFN_NOFOUND) {
return ret2;
}
@ -1199,7 +1232,7 @@ sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t*
return IDLE;
}
sync::sfn_sync::ret_code sync::sfn_sync::decode_mib(srslte_cell_t* cell,
sync::sfn_sync::ret_code sync::sfn_sync::decode_mib(srslte_cell_t* cell_,
uint32_t* tti_cnt,
cf_t* ext_buffer[SRSLTE_MAX_PORTS],
std::array<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;
case SRSLTE_UE_MIB_FOUND:
uint32_t sfn;
srslte_pbch_mib_unpack(bch_payload.data(), cell, &sfn);
srslte_pbch_mib_unpack(bch_payload.data(), cell_, &sfn);
sfn = (sfn + sfn_offset) % 1024;
if (tti_cnt) {
@ -1252,39 +1285,31 @@ sync::sfn_sync::ret_code sync::sfn_sync::decode_mib(srslte_cell_t* cell,
*
*/
void sync::meas_reset()
void sync::set_inter_frequency_measurement(uint32_t cc_idx, uint32_t earfcn_, srslte_cell_t cell_)
{
// Stop all measurements
intra_freq_meas.clear_cells();
if (cc_idx < intra_freq_meas.size()) {
intra_freq_meas[cc_idx]->set_primary_cell(earfcn_, cell_);
}
}
int sync::meas_start(uint32_t earfcn, int pci)
void sync::set_cells_to_meas(uint32_t earfcn_, std::set<uint32_t>& pci)
{
if ((int)earfcn == current_earfcn) {
if (pci != (int)cell.id) {
intra_freq_meas.add_cell(pci);
bool found = false;
for (size_t i = 0; i < intra_freq_meas.size() and not found; i++) {
if (earfcn_ == intra_freq_meas[i]->get_earfcn()) {
intra_freq_meas[i]->set_cells_to_meas(pci);
found = true;
}
return 0;
} else {
Warning("INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested measurement for %d)\n",
current_earfcn,
earfcn);
return -1;
}
if (!found) {
log_h->error("Neighbour cell measurement not supported in secondary carrier. EARFCN=%d\n", earfcn_);
}
}
int sync::meas_stop(uint32_t earfcn, int pci)
void sync::meas_stop()
{
if ((int)earfcn == current_earfcn) {
intra_freq_meas.rem_cell(pci);
return 0;
} else {
Warning(
"INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested stop measurement for %d)\n",
current_earfcn,
earfcn);
for (auto& q : intra_freq_meas) {
q->meas_stop();
}
return -1;
}
} // namespace srsue

@ -34,25 +34,20 @@ namespace srsue {
dl_harq_entity::dl_harq_entity() : proc(SRSLTE_MAX_HARQ_PROC)
{
pcap = NULL;
demux_unit = NULL;
log_h = NULL;
timer_aligment_timer = NULL;
pcap = nullptr;
demux_unit = nullptr;
log_h = nullptr;
si_window_start = 0;
last_temporal_crnti = 0;
average_retx = 0;
nof_pkts = 0;
}
bool dl_harq_entity::init(srslte::log* log_h,
mac_interface_rrc::ue_rnti_t* rntis,
srslte::timer_handler::unique_timer* timer_aligment_timer_,
demux* demux_unit)
bool dl_harq_entity::init(srslte::log* log_h_, mac_interface_rrc::ue_rnti_t* rntis_, demux* demux_unit_)
{
timer_aligment_timer = timer_aligment_timer_;
this->demux_unit = demux_unit;
this->log_h = log_h;
this->rntis = rntis;
demux_unit = demux_unit_;
log_h = log_h_;
rntis = rntis_;
for (uint32_t i = 0; i < SRSLTE_MAX_HARQ_PROC; i++) {
if (!proc[i].init(i, this)) {
@ -329,12 +324,8 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::new_grant_dl(mac_interface_
}
}
if (is_bcch || harq_entity->timer_aligment_timer->is_expired()) {
// Do not generate ACK
action->generate_ack = false;
} else {
action->generate_ack = true;
}
// Do NOT generate ACK if Broadcast Control Channel
action->generate_ack = not is_bcch;
}
void dl_harq_entity::dl_harq_process::dl_tb_process::tb_decoded(mac_interface_phy_lte::mac_grant_dl_t grant,

@ -90,7 +90,7 @@ bool mac::init(phy_interface_mac_lte* phy,
// Create UL/DL unique HARQ pointers
ul_harq.at(0)->init(log_h, &uernti, &ra_procedure, &mux_unit);
dl_harq.at(0)->init(log_h, &uernti, &timer_alignment, &demux_unit);
dl_harq.at(0)->init(log_h, &uernti, &demux_unit);
reset();
@ -137,7 +137,7 @@ void mac::reconfiguration(const uint32_t& cc_idx, const bool& enable)
}
while (dl_harq.size() < cc_idx + 1) {
auto dl = dl_harq_entity_ptr(new dl_harq_entity());
dl->init(log_h, &uernti, &timer_alignment, &demux_unit);
dl->init(log_h, &uernti, &demux_unit);
if (pcap) {
dl->start_pcap(pcap);
@ -538,6 +538,7 @@ void mac::setup_timers(int time_alignment_timer)
void mac::timer_expired(uint32_t timer_id)
{
if (timer_id == timer_alignment.id()) {
Info("Time Alignment Timer expired\n");
timer_alignment_expire();
} else {
Warning("Received callback from unknown timer_id=%d\n", timer_id);

@ -18,7 +18,7 @@
# and at http://www.gnu.org/licenses/.
#
set(SOURCES rrc.cc rrc_procedures.cc)
set(SOURCES rrc.cc rrc_procedures.cc rrc_meas.cc)
add_library(srsue_rrc STATIC ${SOURCES})

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");
// Create a cell with NaN RSRP. Will be updated by new_phy_meas() during SIB search.
if (not rrc_ptr->add_neighbour_cell(new_cell, NAN)) {
if (not rrc_ptr->add_neighbour_cell(unique_cell_t(new cell_t(new_cell)))) {
Info("No more space for neighbour cells\n");
return proc_outcome_t::success;
}
rrc_ptr->set_serving_cell(new_cell);
rrc_ptr->set_serving_cell(new_cell, false);
if (not rrc_ptr->phy->cell_is_camping()) {
Warning("Could not camp on found cell.\n");
@ -340,11 +340,14 @@ proc_outcome_t rrc::cell_selection_proc::init()
neigh_index = 0;
cs_result = cs_result_t::no_cell;
state = search_state_t::cell_selection;
discard_serving = false;
return step();
}
proc_outcome_t rrc::cell_selection_proc::step_cell_selection()
{
Info("Current serving cell: %s\n", rrc_ptr->serving_cell->print().c_str());
// Neighbour cells are sorted in descending order of RSRP
for (; neigh_index < rrc_ptr->neighbour_cells.size(); ++neigh_index) {
/*TODO: CHECK that PLMN matches. Currently we don't receive SIB1 of neighbour cells
@ -356,23 +359,25 @@ proc_outcome_t rrc::cell_selection_proc::step_cell_selection()
(rrc_ptr->cell_selection_criteria(rsrp) and rsrp > rrc_ptr->serving_cell->get_rsrp() + 5)) {
// currently connected and verifies cell selection criteria
// Try to select Cell
rrc_ptr->set_serving_cell(rrc_ptr->neighbour_cells.at(neigh_index)->phy_cell);
rrc_ptr->set_serving_cell(rrc_ptr->neighbour_cells.at(neigh_index)->phy_cell, discard_serving);
discard_serving = false;
Info("Selected cell: %s\n", rrc_ptr->serving_cell->print().c_str());
rrc_ptr->rrc_log->console("Selected cell: %s\n", rrc_ptr->serving_cell->print().c_str());
/* BLOCKING CALL */
if (rrc_ptr->phy->cell_select(&rrc_ptr->serving_cell->phy_cell)) {
Info("Wait PHY to be in-synch\n");
state = search_state_t::wait_in_sync;
rrc_ptr->phy_sync_state = phy_unknown_sync;
return step();
} else {
rrc_ptr->phy_sync_state = phy_unknown_sync;
Error("Could not camp on serving cell.\n");
discard_serving = true;
// Continue to next neighbour cell
}
}
}
if (rrc_ptr->phy_sync_state == phy_in_sync) {
if (rrc_ptr->phy_sync_state == phy_in_sync && rrc_ptr->cell_selection_criteria(rrc_ptr->serving_cell->get_rsrp())) {
if (not rrc_ptr->phy->cell_is_camping()) {
Info("Serving cell %s is in-sync but not camping. Selecting it...\n", rrc_ptr->serving_cell->print().c_str());
@ -381,6 +386,7 @@ proc_outcome_t rrc::cell_selection_proc::step_cell_selection()
Info("Selected serving cell OK.\n");
} else {
rrc_ptr->phy_sync_state = phy_unknown_sync;
rrc_ptr->serving_cell->set_rsrp(-INFINITY);
Error("Could not camp on serving cell.\n");
return proc_outcome_t::error;
}
@ -401,11 +407,20 @@ proc_outcome_t rrc::cell_selection_proc::step_cell_selection()
proc_outcome_t rrc::cell_selection_proc::step_wait_in_sync()
{
if (rrc_ptr->phy_sync_state == phy_in_sync) {
Info("PHY is in SYNC\n");
if (not rrc_ptr->serv_cell_cfg.launch(&serv_cell_cfg_fut, rrc_ptr->ue_required_sibs)) {
return proc_outcome_t::error;
if (rrc_ptr->cell_selection_criteria(rrc_ptr->serving_cell->get_rsrp())) {
Info("PHY is in SYNC and cell selection passed\n");
serv_cell_cfg_fut = rrc_ptr->serv_cell_cfg.get_future();
if (not rrc_ptr->serv_cell_cfg.launch(&serv_cell_cfg_fut, rrc_ptr->ue_required_sibs)) {
return proc_outcome_t::error;
}
state = search_state_t::cell_config;
} else {
Info("PHY is in SYNC but cell selection did not pass. Go back to select step.\n");
neigh_index = 0;
cs_result = cs_result_t::no_cell;
state = search_state_t::cell_selection;
discard_serving = true; // Discard this cell
}
state = search_state_t::cell_config;
}
return proc_outcome_t::yield;
}
@ -432,6 +447,7 @@ proc_outcome_t rrc::cell_selection_proc::step_cell_config()
return proc_outcome_t::yield;
}
if (serv_cell_cfg_fut.is_success()) {
rrc_ptr->rrc_log->console("Selected cell: %s\n", rrc_ptr->serving_cell->print().c_str());
Info("All SIBs of serving cell obtained successfully\n");
cs_result = cs_result_t::changed_cell;
return proc_outcome_t::success;
@ -476,7 +492,8 @@ rrc::plmn_search_proc::plmn_search_proc(rrc* parent_) : rrc_ptr(parent_), log_h(
proc_outcome_t rrc::plmn_search_proc::init()
{
Info("Starting PLMN search\n");
nof_plmns = 0;
nof_plmns = 0;
cell_search_fut = rrc_ptr->cell_searcher.get_future();
if (not rrc_ptr->cell_searcher.launch(&cell_search_fut)) {
Error("Failed due to fail to init cell search...\n");
return proc_outcome_t::error;
@ -804,7 +821,7 @@ proc_outcome_t rrc::go_idle_proc::init()
{
rlc_flush_counter = 0;
Info("Starting...\n");
return proc_outcome_t::yield;
return step();
}
proc_outcome_t rrc::go_idle_proc::step()
@ -814,8 +831,10 @@ proc_outcome_t rrc::go_idle_proc::step()
return proc_outcome_t::success;
}
// If the RLC SRB1 is not suspended
// wait for max. 2s for RLC on SRB1 to be flushed
if (not rrc_ptr->rlc->has_data(RB_ID_SRB1) || ++rlc_flush_counter > rlc_flush_timeout) {
if (rrc_ptr->rlc->is_suspended(RB_ID_SRB1) || not rrc_ptr->rlc->has_data(RB_ID_SRB1) ||
++rlc_flush_counter > rlc_flush_timeout) {
rrc_ptr->leave_connected();
return proc_outcome_t::success;
} else {
@ -854,8 +873,7 @@ proc_outcome_t rrc::cell_reselection_proc::step()
Info("Cell Selection completed. Handling its result...\n");
switch (*cell_selection_fut.value()) {
case cs_result_t::changed_cell:
Info("New cell has been selected, start receiving PCCH\n");
rrc_ptr->mac->pcch_start_rx();
Info("New cell has been selected\n");
break;
case cs_result_t::no_cell:
Warning("Could not find any cell to camp on\n");
@ -879,7 +897,10 @@ rrc::connection_reest_proc::connection_reest_proc(srsue::rrc* rrc_) : rrc_ptr(rr
proc_outcome_t rrc::connection_reest_proc::init(asn1::rrc::reest_cause_e cause)
{
Info("Starting...\n");
Info("Starting... Cause: %s\n",
cause == asn1::rrc::reest_cause_opts::recfg_fail
? "Reconfiguration failure"
: cause == asn1::rrc::reest_cause_opts::ho_fail ? "Handover failure" : "Other failure");
// Save Current RNTI before MAC Reset
mac_interface_rrc::ue_rnti_t uernti;
@ -891,6 +912,7 @@ proc_outcome_t rrc::connection_reest_proc::init(asn1::rrc::reest_cause_e cause)
reest_rnti = uernti.crnti;
reest_cause = cause;
reest_source_pci = rrc_ptr->serving_cell->get_pci(); // needed for reestablishment with another cell
reest_source_freq = rrc_ptr->serving_cell->get_earfcn();
// the initiation of reestablishment procedure as indicates in 3GPP 36.331 Section 5.3.7.2
// Cannot be called from here because it has PHY-MAC re-configuration that should be performed in a different thread
@ -947,9 +969,10 @@ srslte::proc_outcome_t rrc::connection_reest_proc::step_cell_reselection()
// Run cell reselection
if (not rrc_ptr->cell_reselector.run()) {
// Check T311
if (!rrc_ptr->t311.is_running()) {
if (not rrc_ptr->t311.is_running()) {
// Abort procedure if T311 expires
Info("T311 expired during cell reselection. Aborting.\n");
Info("T311 expired during cell reselection. Going to IDLE.\n");
rrc_ptr->start_go_idle();
return proc_outcome_t::success;
}
@ -964,7 +987,7 @@ srslte::proc_outcome_t rrc::connection_reest_proc::step_cell_reselection()
rrc_ptr->serving_cell->has_sib1(),
rrc_ptr->serving_cell->has_sib2(),
rrc_ptr->serving_cell->has_sib3());
std::vector<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)) {
Error("Failed to initiate configure serving cell\n");
return proc_outcome_t::error;
@ -991,7 +1014,8 @@ proc_outcome_t rrc::connection_reest_proc::step_cell_configuration()
// Check T311
if (!rrc_ptr->t311.is_running()) {
// Abort procedure if T311 expires
Info("T311 expired during cell configuration. Aborting.\n");
Info("T311 expired during cell configuration. Going to IDLE.\n");
rrc_ptr->start_go_idle();
return proc_outcome_t::success;
}
@ -1040,10 +1064,17 @@ srslte::proc_outcome_t rrc::connection_reest_proc::cell_criteria()
// initiate transmission of the RRCConnectionReestablishmentRequest message in accordance with 5.3.7.4;
rrc_ptr->send_con_restablish_request(reest_cause, reest_rnti, reest_source_pci);
} else {
} else if (rrc_ptr->t311.is_running()) {
// Upon selecting an inter-RAT cell
Info("Reestablishment Cell Selection criteria failed.\n");
rrc_ptr->leave_connected();
// Launch cell reselection
if (not rrc_ptr->cell_reselector.launch()) {
Error("Failed to initiate a Cell re-selection procedure...\n");
return proc_outcome_t::error;
}
state = state_t::cell_reselection;
return proc_outcome_t::yield;
}
return proc_outcome_t::success;
}

@ -240,25 +240,28 @@ public:
void in_sync() override {}
void out_of_sync() override {}
void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn, int pci) override
void new_cell_meas(std::vector<phy_meas_t>& meas) override
{
if (!cells.count(pci)) {
cells[pci].rsrp_min = rsrp;
cells[pci].rsrp_max = rsrp;
cells[pci].rsrp_avg = rsrp;
cells[pci].rsrq_min = rsrq;
cells[pci].rsrq_max = rsrq;
cells[pci].rsrq_avg = rsrq;
cells[pci].count = 1;
} else {
cells[pci].rsrp_min = SRSLTE_MIN(cells[pci].rsrp_min, rsrp);
cells[pci].rsrp_max = SRSLTE_MAX(cells[pci].rsrp_max, rsrp);
cells[pci].rsrp_avg = (rsrp + cells[pci].rsrp_avg * cells[pci].count) / (cells[pci].count + 1);
cells[pci].rsrq_min = SRSLTE_MIN(cells[pci].rsrq_min, rsrq);
cells[pci].rsrq_max = SRSLTE_MAX(cells[pci].rsrq_max, rsrq);
cells[pci].rsrq_avg = (rsrq + cells[pci].rsrq_avg * cells[pci].count) / (cells[pci].count + 1);
cells[pci].count++;
for (auto& m : meas) {
uint32_t pci = m.pci;
if (!cells.count(pci)) {
cells[pci].rsrp_min = m.rsrp;
cells[pci].rsrp_max = m.rsrp;
cells[pci].rsrp_avg = m.rsrp;
cells[pci].rsrq_min = m.rsrq;
cells[pci].rsrq_max = m.rsrq;
cells[pci].rsrq_avg = m.rsrq;
cells[pci].count = 1;
} else {
cells[pci].rsrp_min = SRSLTE_MIN(cells[pci].rsrp_min, m.rsrp);
cells[pci].rsrp_max = SRSLTE_MAX(cells[pci].rsrp_max, m.rsrp);
cells[pci].rsrp_avg = (m.rsrp + cells[pci].rsrp_avg * cells[pci].count) / (cells[pci].count + 1);
cells[pci].rsrq_min = SRSLTE_MIN(cells[pci].rsrq_min, m.rsrq);
cells[pci].rsrq_max = SRSLTE_MAX(cells[pci].rsrq_max, m.rsrq);
cells[pci].rsrq_avg = (m.rsrq + cells[pci].rsrq_avg * cells[pci].count) / (cells[pci].count + 1);
cells[pci].count++;
}
}
}
@ -393,7 +396,6 @@ int main(int argc, char** argv)
phy_args.estimator_fil_auto = false;
phy_args.estimator_fil_order = 4;
phy_args.estimator_fil_stddev = 1.0f;
phy_args.sic_pss_enabled = false;
phy_args.interpolate_subframe_enabled = false;
phy_args.nof_rx_ant = 1;
phy_args.cfo_is_doppler = true;
@ -450,6 +452,8 @@ int main(int argc, char** argv)
intra_measure.init(&common, &rrc, &logger);
intra_measure.set_primary_cell(serving_cell_id, cell_base);
std::set<uint32_t> pcis_to_meas = {};
if (earfcn_dl >= 0) {
// Create radio log
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
if (cell_list.empty()) {
intra_measure.add_cell(cell.id);
pcis_to_meas.insert(cell.id);
}
}
}
@ -499,7 +503,7 @@ int main(int argc, char** argv)
if (cell_list == "all") {
// Add all possible cells
for (int i = 0; i < 504; i++) {
intra_measure.add_cell(i);
pcis_to_meas.insert(i);
}
} else if (cell_list == "none") {
// Do nothing
@ -516,10 +520,13 @@ int main(int argc, char** argv)
while (ss.good()) {
std::string substr;
getline(ss, substr, ',');
intra_measure.add_cell((uint32_t)strtoul(substr.c_str(), nullptr, 10));
pcis_to_meas.insert((uint32_t)strtoul(substr.c_str(), nullptr, 10));
}
}
// pass cells to measure to intra_measure object
intra_measure.set_cells_to_meas(pcis_to_meas);
// Run loop
for (uint32_t sf_idx = 0; sf_idx < duration_execution_s * 1000; sf_idx++) {
srslte_dl_sf_cfg_t sf_cfg_dl = {};

@ -85,10 +85,13 @@ private:
void in_sync() override { notify_in_sync(); }
void out_of_sync() override { notify_out_of_sync(); }
void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn, int pci) override
void new_cell_meas(std::vector<phy_meas_t>& meas) override
{
notify_new_phy_meas();
log_h.info("New measurement earfcn=%d; pci=%d; rsrp=%+.1fdBm; rsrq=%+.1fdB;\n", earfcn, pci, rsrp, rsrq);
for (auto& m : meas) {
notify_new_phy_meas();
log_h.info(
"New measurement earfcn=%d; pci=%d; rsrp=%+.1fdBm; rsrq=%+.1fdB;\n", m.earfcn, m.pci, m.rsrp, m.rsrq);
}
}
uint16_t get_dl_sched_rnti(uint32_t tti) override { return rnti; }
uint16_t get_ul_sched_rnti(uint32_t tti) override { return rnti; }
@ -491,7 +494,7 @@ int main(int argc, char** argv)
auto cell_search_res = phy_test->get_phy_interface_rrc()->cell_search(&phy_cell);
TESTASSERT(cell_search_res.found == srsue::phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND);
TESTASSERT(phy_test->get_stack()->wait_in_sync(default_timeout));
TESTASSERT(memcmp(&phy_cell.cell, &cell, sizeof(srslte_cell_t)) == 0);
TESTASSERT(phy_cell.pci == cell.id);
// 2. Cell select
phy_test->get_phy_interface_rrc()->cell_select(&phy_cell);

@ -78,7 +78,7 @@ public:
void set_config_mbsfn_mcch(const srslte::mcch_msg_t& mcch){};
// Measurements interface
void meas_reset();
void meas_stop();
int meas_start(uint32_t earfcn, int pci = -1);
int meas_stop(uint32_t earfcn, int pci = -1);

@ -118,7 +118,7 @@ void lte_ttcn3_phy::set_config(srslte::phy_cfg_t& config, uint32_t cc_idx, uint3
}
// Measurements interface
void lte_ttcn3_phy::meas_reset(){};
void lte_ttcn3_phy::meas_stop(){};
int lte_ttcn3_phy::meas_start(uint32_t earfcn, int pci)
{

@ -31,6 +31,10 @@ add_executable(rrc_reconfig_test rrc_reconfig_test.cc)
target_link_libraries(rrc_reconfig_test srsue_upper srslte_upper srslte_phy rrc_asn1)
add_test(rrc_reconfig_test rrc_reconfig_test)
add_executable(rrc_meas_test rrc_meas_test.cc)
target_link_libraries(rrc_meas_test srsue_rrc srsue_upper srslte_upper srslte_phy rrc_asn1)
add_test(rrc_meas_test rrc_meas_test)
add_executable(nas_test nas_test.cc)
target_link_libraries(nas_test srsue_upper srslte_upper srslte_phy rrc_asn1)
add_test(nas_test nas_test)

@ -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::rrc;
void nas_test()
int nas_test()
{
srslte::log_filter log1("NAS");
log1.set_level(srslte::LOG_LEVEL_DEBUG);
@ -93,9 +93,10 @@ void nas_test()
default:
break;
}
return 0;
}
int basic_test()
int meas_obj_test()
{
srslte::log_filter log1("RRC");
log1.set_level(srslte::LOG_LEVEL_DEBUG);
@ -154,6 +155,6 @@ int basic_test()
int main(int argc, char** argv)
{
TESTASSERT(basic_test() == 0);
nas_test();
TESTASSERT(meas_obj_test() == 0);
TESTASSERT(nas_test() == 0);
}

@ -298,10 +298,6 @@ enable = false
#
# interpolate_subframe_enabled: Interpolates in the time domain the channel estimates within 1 subframe. Default is to average.
#
# sic_pss_enabled: Applies Successive Interference Cancellation to PSS signals when searching for neighbour cells.
# Must be disabled if cells have identical channel and timing, for instance if generated from
# the same source.
#
# pdsch_csi_enabled: Stores the Channel State Information and uses it for weightening the softbits. It is only
# used in TM1. It is True by default.
#
@ -333,7 +329,6 @@ enable = false
#estimator_fil_order = 4
#snr_to_cqi_offset = 0.0
#interpolate_subframe_enabled = false
#sic_pss_enabled = true
#pregenerate_signals = false
#pdsch_csi_enabled = true
#pdsch_8bit_decoder = false

Loading…
Cancel
Save