srsUE takes and reports intra-frequency measurements correctly

This commit is contained in:
Ismael Gomez 2017-11-20 10:02:39 +01:00
parent 641eceb328
commit 12d8b373c7
22 changed files with 1209 additions and 421 deletions

View File

@ -61,7 +61,6 @@ set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "")
option(ENABLE_SRSUE "Build srsUE application" ON)
option(ENABLE_SRSENB "Build srsENB application" ON)
option(ENABLE_VOLK "Enable use of VOLK SIMD library" OFF)
option(ENABLE_GUI "Enable GUI (using srsGUI)" ON)
option(ENABLE_BLADERF "Enable BladeRF" ON)
@ -189,21 +188,6 @@ if(ENABLE_GUI)
endif(SRSGUI_FOUND)
endif(ENABLE_GUI)
# VOLK
include(CheckFunctionExistsMath)
if(ENABLE_VOLK)
find_package(Volk)
if(VOLK_FOUND)
include_directories(${VOLK_INCLUDE_DIRS})
link_directories(${VOLK_LIBRARY_DIRS})
message(STATUS "Compiling with VOLK SIMD library.")
else(VOLK_FOUND)
message(STATUS "VOLK SIMD library NOT found. Using generic implementation.")
endif(VOLK_FOUND)
else(ENABLE_VOLK)
message(STATUS "VOLK library disabled")
endif(ENABLE_VOLK)
########################################################################
# Install Dirs
########################################################################

View File

@ -1218,6 +1218,9 @@ typedef enum{
}LIBLTE_RRC_REPORT_AMOUNT_ENUM;
static const char liblte_rrc_report_amount_text[LIBLTE_RRC_REPORT_AMOUNT_N_ITEMS][20] = { "r1", "r2", "r4", "r8",
"r16", "r32", "r64", "INFINITY"};
static const int8 liblte_rrc_report_amount_num[LIBLTE_RRC_REPORT_AMOUNT_N_ITEMS] = {1, 2, 4, 8, 16, 32, 64, -1};
typedef enum{
LIBLTE_RRC_THRESHOLD_UTRA_TYPE_RSCP = 0,
LIBLTE_RRC_THRESHOLD_UTRA_TYPE_ECNO,

View File

@ -78,22 +78,30 @@ class periodic_thread : public thread
{
public:
void start_periodic(int period_us_, int priority = -1) {
run_enable = true;
period_us = period_us_;
start(priority);
}
void stop() {
run_enable = false;
wait_thread_finish();
}
protected:
virtual void run_period() = 0;
private:
int wakeups_missed;
int timer_fd;
int period_us;
int period_us;
bool run_enable;
void run_thread() {
if (make_periodic()) {
return;
}
while(1) {
while(run_enable) {
run_period();
wait_period();
if (run_enable) {
wait_period();
}
}
}
int make_periodic() {

View File

@ -134,6 +134,7 @@ class rrc_interface_mac : public rrc_interface_mac_common
{
public:
virtual void release_pucch_srs() = 0;
virtual void run_tti(uint32_t tti) = 0;
};
// RRC interface for PHY
@ -144,6 +145,7 @@ public:
virtual void out_of_sync() = 0;
virtual void earfcn_end() = 0;
virtual void cell_found(uint32_t earfcn, srslte_cell_t phy_cell, float rsrp) = 0;
virtual void new_phy_meas(float rsrp, float rsrq, uint32_t tti, uint32_t earfcn = 0, uint32_t pci = 0) = 0;
};
// RRC interface for NAS
@ -521,7 +523,7 @@ public:
bool enable_64qam;
} phy_cfg_t;
virtual void get_current_cell(srslte_cell_t *cell) = 0;
virtual void get_current_cell(srslte_cell_t *cell, uint32_t *current_earfcn = NULL) = 0;
virtual void get_config(phy_cfg_t *phy_cfg) = 0;
virtual void set_config(phy_cfg_t *phy_cfg) = 0;
virtual void set_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *dedicated) = 0;
@ -529,6 +531,11 @@ public:
virtual void set_config_tdd(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd) = 0;
virtual void set_config_64qam_en(bool enable) = 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;
/* Cell search and selection procedures */
virtual void cell_search_start() = 0;
virtual void cell_search_stop() = 0;

View File

@ -301,7 +301,8 @@ SRSLTE_API int srslte_str2mimotype(char *mimo_type_str,
SRSLTE_API char *srslte_mimotype2str(srslte_mimo_type_t mimo_type);
SRSLTE_API uint32_t srslte_tti_interval(uint32_t tti1,
/* Returns the interval tti1-tti2 mod 10240 */
SRSLTE_API uint32_t srslte_tti_interval(uint32_t tti1,
uint32_t tti2);
#endif

View File

@ -20,8 +20,9 @@ typedef struct {
SRSLTE_API int srslte_ringbuffer_init(srslte_ringbuffer_t *q,
int capacity);
SRSLTE_API void srslte_ringbuffer_free(srslte_ringbuffer_t *q,
int capacity);
SRSLTE_API void srslte_ringbuffer_free(srslte_ringbuffer_t *q);
SRSLTE_API void srslte_ringbuffer_reset(srslte_ringbuffer_t *q);
SRSLTE_API int srslte_ringbuffer_status(srslte_ringbuffer_t *q);

View File

@ -57,9 +57,5 @@ set(srslte_srcs $<TARGET_OBJECTS:srslte_agc>
add_library(srslte_phy STATIC ${srslte_srcs})
target_link_libraries(srslte_phy ${FFT_LIBRARIES})
if(VOLK_FOUND)
target_link_libraries(srslte_phy ${VOLK_LIBRARIES})
endif(VOLK_FOUND)
target_link_libraries(srslte_phy pthread m)
install(TARGETS srslte_phy DESTINATION ${LIBRARY_DIR})

View File

@ -62,7 +62,6 @@ int srslte_sync_init_decim(srslte_sync_t *q, uint32_t frame_size, uint32_t max_o
int ret = SRSLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
frame_size <= 307200 &&
fft_size_isvalid(fft_size))
{
ret = SRSLTE_ERROR;

View File

@ -11,10 +11,9 @@ int srslte_ringbuffer_init(srslte_ringbuffer_t *q, int capacity)
if (!q->buffer) {
return -1;
}
q->capacity = capacity;
q->count = 0;
q->wpm = 0;
q->rpm = 0;
q->capacity = capacity;
srslte_ringbuffer_reset(q);
pthread_mutex_init(&q->mutex, NULL);
pthread_cond_init(&q->cvar, NULL);
@ -22,7 +21,7 @@ int srslte_ringbuffer_init(srslte_ringbuffer_t *q, int capacity)
return 0;
}
void srslte_ringbuffer_free(srslte_ringbuffer_t *q, int capacity)
void srslte_ringbuffer_free(srslte_ringbuffer_t *q)
{
if (q) {
if (q->buffer) {
@ -34,6 +33,15 @@ void srslte_ringbuffer_free(srslte_ringbuffer_t *q, int capacity)
}
}
void srslte_ringbuffer_reset(srslte_ringbuffer_t *q)
{
pthread_mutex_lock(&q->mutex);
q->count = 0;
q->wpm = 0;
q->rpm = 0;
pthread_mutex_unlock(&q->mutex);
}
int srslte_ringbuffer_status(srslte_ringbuffer_t *q)
{
return q->count;
@ -44,7 +52,7 @@ int srslte_ringbuffer_write(srslte_ringbuffer_t *q, void *p, int nof_bytes)
uint8_t *ptr = (uint8_t*) p;
int w_bytes = nof_bytes;
pthread_mutex_lock(&q->mutex);
if (q->count + w_bytes >= q->capacity) {
if (q->count + w_bytes > q->capacity) {
w_bytes = q->capacity - q->count;
fprintf(stderr, "Buffer overrun: lost %d bytes\n", nof_bytes - w_bytes);
}

View File

@ -65,7 +65,9 @@ namespace srsue {
float avg_snr_db;
float avg_noise;
float avg_rsrp;
uint32_t serving_cell_report_period;
phch_common(uint32_t max_mutex = 3);
void init(phy_interface_rrc::phy_cfg_t *config,
phy_args_t *args,

View File

@ -27,10 +27,13 @@
#ifndef UEPHYRECV_H
#define UEPHYRECV_H
#include <map>
#include "srslte/srslte.h"
#include "srslte/common/log.h"
#include "srslte/common/threads.h"
#include "srslte/common/thread_pool.h"
#include "srslte/common/tti_sync_cv.h"
#include "srslte/radio/radio_multi.h"
#include "phy/prach.h"
#include "phy/phch_worker.h"
@ -39,7 +42,8 @@
namespace srsue {
typedef _Complex float cf_t;
typedef _Complex float cf_t;
class phch_recv : public thread
{
@ -61,14 +65,17 @@ public:
void cell_search_next(bool reset = false);
bool cell_select(uint32_t earfcn, srslte_cell_t cell);
void meas_reset();
int meas_start(uint32_t earfcn, int pci);
int meas_stop(uint32_t earfcn, int pci);
uint32_t get_current_tti();
bool status_is_sync();
void set_time_adv_sec(float time_adv_sec);
void get_current_cell(srslte_cell_t *cell);
void get_current_cell(srslte_cell_t *cell, uint32_t *earfcn = NULL);
void scell_enable(bool enable);
const static int MUTEX_X_WORKER = 4;
@ -125,13 +132,13 @@ private:
// Class to synchronize system frame number
class sfn_sync {
public:
typedef enum {IDLE, SFN_FOUND, ERROR, TIMEOUT} ret_code;
typedef enum {IDLE, SFN_FOUND, SFX0_FOUND, ERROR, TIMEOUT} ret_code;
~sfn_sync();
void init(srslte_ue_sync_t *ue_sync, cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t timeout = SYNC_SFN_TIMEOUT);
void reset();
bool set_cell(srslte_cell_t cell);
ret_code run_subframe(srslte_cell_t *cell, uint32_t *tti_cnt);
ret_code run_subframe(srslte_cell_t *cell, uint32_t *tti_cnt, bool sfidx_only = false);
private:
srslte::log *log_h;
@ -145,74 +152,106 @@ private:
// 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(srslte_ue_sync_t *ue_sync, cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h,
void init(cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h,
uint32_t nof_rx_antennas, 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_subframe_sync(srslte_ue_sync_t *ue_sync, uint32_t sf_idx);
ret_code run_multiple_subframes(cf_t *buffer, uint32_t sf_idx, uint32_t nof_sf);
float rsrp();
float rsrq();
float snr();
float cfo();
private:
srslte::log *log_h;
srslte_ue_dl_t ue_dl;
srslte_ue_sync_t *ue_sync;
cf_t *buffer[SRSLTE_MAX_PORTS];
uint32_t cnt;
uint32_t nof_subframes;
float mean_rsrp, mean_rsrq, mean_snr, mean_cfo;
uint32_t current_prb;
float mean_rsrp, mean_rsrq, mean_snr;
const static int RSRP_MEASURE_NOF_FRAMES = 5;
};
// Class to receive secondary cell
class scell_recv : public thread {
class scell_recv {
public:
void init(phch_recv *parent, srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t prio, int cpu_affinity = -1);
void stop();
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);
void reset();
int recv(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time);
void write(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time);
bool is_enabled();
void set_cell(srslte_cell_t scell);
int find_cells(cf_t *input_buffer, srslte_cell_t current_cell, uint32_t nof_sf, cell_info_t found_cells[MAX_CELLS]);
private:
void run_thread();
enum {
IDLE = 0,
SCELL_SELECT,
SCELL_MEASURE,
SCELL_CAMPING
} scell_state;
const static int DEFAULT_MEASUREMENT_LEN = 10;
srslte::log *log_h;
phch_recv *p;
bool running;
srslte_ringbuffer_t ring_buffer[SRSLTE_MAX_PORTS];
cf_t *sf_buffer[SRSLTE_MAX_PORTS];
srslte_ue_sync_t ue_sync;
srslte_cell_t cell;
uint32_t nof_rx_antennas;
uint32_t current_sflen;
srslte::log *log_h;
srslte_sync_t sync_find;
measure measure_p;
sfn_sync sfn_p;
uint32_t tti;
};
/* TODO: Intra-freq measurements can be improved by capturing 200 ms length signal and run cell search +
* measurements offline using sync object and finding multiple cells for each N_id_2
*/
// Class to perform intra-frequency measurements
class intra_measure : public thread {
public:
void init(phch_common *common, rrc_interface_phy *rrc, srslte::log *log_h);
void stop();
void add_cell(int pci);
void rem_cell(int pci);
void set_primay_cell(uint32_t earfcn, srslte_cell_t cell);
void clear_cells();
void write(uint32_t tti, cf_t *data, uint32_t nsamples);
private:
void run_thread();
const static int CAPTURE_LEN_SF = 15;
const static int INTRA_FREQ_MEAS_PERIOD_MS = 200;
scell_recv scell;
rrc_interface_phy *rrc;
srslte::log *log_h;
phch_common *common;
uint32_t current_earfcn;
uint32_t current_sflen;
srslte_cell_t primary_cell;
std::vector<int> active_pci;
srslte::tti_sync_cv tti_sync;
cf_t *search_buffer;
scell_recv::cell_info_t info[scell_recv::MAX_CELLS];
bool running;
bool receive_enabled;
bool receiving;
uint32_t measure_tti;
uint32_t receive_cnt;
srslte_ringbuffer_t ring_buffer;
};
// Objects for internal use
scell_recv scell;
measure measure_p;
search search_p;
sfn_sync sfn_p;
intra_measure intra_freq_meas;
uint32_t current_sflen;
int next_offset;

View File

@ -40,8 +40,8 @@
#include "srslte/interfaces/ue_interfaces.h"
namespace srsue {
typedef _Complex float cf_t;
typedef _Complex float cf_t;
class phy
: public phy_interface_mac
@ -78,7 +78,6 @@ public:
void set_earfcn(std::vector<uint32_t> earfcns);
void force_freq(float dl_freq, float ul_freq);
void scell_enable(bool enable);
/********** RRC INTERFACE ********************/
void reset();
@ -89,6 +88,10 @@ public:
void cell_search_next();
bool cell_select(uint32_t earfcn, srslte_cell_t phy_cell);
void meas_reset();
int meas_start(uint32_t earfcn, int pci);
int meas_stop(uint32_t earfcn, int pci);
/********** MAC INTERFACE ********************/
/* Functions to synchronize with a cell */
bool sync_status(); // this is also RRC interface
@ -133,7 +136,7 @@ public:
float get_pathloss_db();
uint32_t get_current_tti();
void get_current_cell(srslte_cell_t *cell);
void get_current_cell(srslte_cell_t *cell, uint32_t *current_earfcn = NULL);
void start_plot();

View File

@ -80,8 +80,6 @@ public:
void pregenerate_signals(bool enable);
void test_scell();
private:
virtual ~ue();

View File

@ -38,6 +38,7 @@
#include "srslte/common/threads.h"
#include <map>
#include <queue>
using srslte::byte_buffer_t;
@ -80,6 +81,46 @@ public:
void liblte_rrc_log(char *str);
// NAS interface
void write_sdu(uint32_t lcid, byte_buffer_t *sdu);
uint16_t get_mcc();
uint16_t get_mnc();
void enable_capabilities();
void plmn_search();
void plmn_select(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id);
// PHY interface
void in_sync();
void out_of_sync();
void earfcn_end();
void cell_found(uint32_t earfcn, srslte_cell_t phy_cell, float rsrp);
void new_phy_meas(float rsrp, float rsrq, uint32_t tti, uint32_t earfcn, uint32_t pci);
// MAC interface
void release_pucch_srs();
void run_tti(uint32_t tti);
void ra_problem();
// GW interface
bool is_connected();
bool have_drb();
// PDCP interface
void write_pdu(uint32_t lcid, byte_buffer_t *pdu);
void write_pdu_bcch_bch(byte_buffer_t *pdu);
void write_pdu_bcch_dlsch(byte_buffer_t *pdu);
void write_pdu_pcch(byte_buffer_t *pdu);
private:
srslte::byte_buffer_pool *pool;
srslte::log *rrc_log;
@ -90,7 +131,9 @@ private:
nas_interface_rrc *nas;
usim_interface_rrc *usim;
srslte::bit_buffer_t bit_buf;
void send_ul_dcch_msg(byte_buffer_t *pdu = NULL);
LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg;
srslte::bit_buffer_t bit_buf;
pthread_mutex_t mutex;
@ -166,42 +209,6 @@ private:
bool thread_running;
void run_thread();
// NAS interface
void write_sdu(uint32_t lcid, byte_buffer_t *sdu);
uint16_t get_mcc();
uint16_t get_mnc();
void enable_capabilities();
void plmn_search();
void plmn_select(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id);
// PHY interface
void in_sync();
void out_of_sync();
void earfcn_end();
void cell_found(uint32_t earfcn, srslte_cell_t phy_cell, float rsrp);
// MAC interface
void release_pucch_srs();
void ra_problem();
// GW interface
bool is_connected();
bool have_drb();
// PDCP interface
void write_pdu(uint32_t lcid, byte_buffer_t *pdu);
void write_pdu_bcch_bch(byte_buffer_t *pdu);
void write_pdu_bcch_dlsch(byte_buffer_t *pdu);
void write_pdu_pcch(byte_buffer_t *pdu);
// Radio bearers
typedef enum{
RB_ID_SRB0 = 0,
@ -226,6 +233,97 @@ private:
}
}
// Measurements sub-class
class rrc_meas {
public:
void init(rrc *parent);
void reset();
void parse_meas_config(LIBLTE_RRC_MEAS_CONFIG_STRUCT *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);
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> cells;
} meas_obj_t;
typedef struct {
uint32_t interval;
uint32_t max_cell;
uint32_t amount;
quantity_t trigger_quantity;
quantity_t report_quantity;
LIBLTE_RRC_EVENT_EUTRA_STRUCT 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;
uint32_t 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;
srslte::log *log_h;
phy_interface_rrc *phy;
srslte::mac_interface_timers *mac_timers;
uint32_t filter_k_rsrp, filter_k_rsrq;
float filter_a[NOF_MEASUREMENTS];
meas_value_t pcell_measurement;
bool s_measure_enabled;
float s_measure_value;
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 meas_id);
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(LIBLTE_RRC_EVENT_EUTRA_STRUCT *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;
// RLC interface
void max_retx_attempted();

View File

@ -180,6 +180,8 @@ void mac::run_thread() {
ra_procedure.start_mac_order();
}
ra_procedure.step(tti);
rrc_h->run_tti(tti);
}
}

View File

@ -398,7 +398,6 @@ int main(int argc, char *argv[])
pthread_t input;
pthread_create(&input, NULL, &input_loop, &args);
bool scell_done = false;
bool plot_started = false;
bool signals_pregenerated = false;
@ -408,10 +407,6 @@ int main(int argc, char *argv[])
ue->pregenerate_signals(true);
signals_pregenerated = true;
}
if (!scell_done) {
((srsue::ue*) ue)->test_scell();
scell_done = true;
}
if (!plot_started && args.gui.enable) {
ue->start_plot();
plot_started = true;

View File

@ -59,6 +59,9 @@ phch_common::phch_common(uint32_t max_mutex_) : tx_mutex(max_mutex_)
rx_gain_offset = 0;
sr_last_tx_tti = -1;
cur_pusch_power = 0;
serving_cell_report_period = 20;
bzero(zeros, 50000*sizeof(cf_t));
// FIXME: This is an ungly fix to avoid the TX filters to empty

View File

@ -25,11 +25,10 @@
*/
#include <unistd.h>
#include <srslte/srslte.h>
#include <algorithm>
#include "srslte/srslte.h"
#include "srslte/common/log.h"
#include "phy/phch_worker.h"
#include "phy/phch_common.h"
#include "phy/phch_recv.h"
#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
@ -41,13 +40,8 @@ namespace srsue {
int radio_recv_callback(void *obj, cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time) {
return ((phch_recv*) obj)->radio_recv_fnc(data, nsamples, rx_time);
}
int scell_recv_callback(void *obj, cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time) {
return ((phch_recv*) obj)->scell_recv_fnc(data, nsamples, rx_time);
}
double callback_set_rx_gain(void *h, double gain) {
srslte::radio_multi *radio_handler = (srslte::radio_multi *) h;
return radio_handler->set_rx_gain_th(gain);
@ -94,10 +88,10 @@ void phch_recv::init(srslte::radio_multi *_radio_handler, mac_interface_phy *_ma
sfn_p.init(&ue_sync, sf_buffer, log_h);
// Initialize measurement class for the primary cell
measure_p.init(&ue_sync, sf_buffer, log_h, nof_rx_antennas);
measure_p.init(sf_buffer, log_h, nof_rx_antennas);
// Start scell
scell.init(this, log_h, nof_rx_antennas_, prio-1, sync_cpu_affinity);
// Start intra-frequency measurement
intra_freq_meas.init(worker_com, rrc, log_h);
reset();
@ -120,7 +114,7 @@ phch_recv::~phch_recv() {
void phch_recv::stop()
{
intra_freq_meas.stop();
running = false;
wait_thread_finish();
}
@ -223,6 +217,7 @@ bool phch_recv::set_cell() {
measure_p.set_cell(cell);
sfn_p.set_cell(cell);
worker_com->set_cell(cell);
intra_freq_meas.set_primay_cell(current_earfcn, cell);
for (uint32_t i = 0; i < workers_pool->get_nof_workers(); i++) {
if (!((phch_worker *) workers_pool->get_worker(i))->set_cell(cell)) {
@ -464,10 +459,13 @@ bool phch_recv::status_is_sync() {
return phy_state == CELL_CAMP;
}
void phch_recv::get_current_cell(srslte_cell_t *cell_) {
void phch_recv::get_current_cell(srslte_cell_t *cell_, uint32_t *earfcn) {
if (cell_) {
memcpy(cell_, &cell, sizeof(srslte_cell_t));
}
if (earfcn) {
*earfcn = current_earfcn;
}
}
int phch_recv::radio_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time)
@ -480,10 +478,6 @@ int phch_recv::radio_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, s
next_offset = nsamples;
}
if (offset <= 0) {
scell.write(data, nsamples, rx_time);
}
log_h->debug("SYNC: received %d samples from radio\n", nsamples);
return nsamples;
@ -492,19 +486,6 @@ int phch_recv::radio_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, s
}
}
int phch_recv::scell_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time)
{
return scell.recv(data, nsamples, rx_time);
}
void phch_recv::scell_enable(bool enable)
{
srslte_cell_t target_cell;
memcpy(&target_cell, &cell, sizeof(srslte_cell_t));
target_cell.id++;
scell.set_cell(target_cell);
}
@ -587,7 +568,7 @@ void phch_recv::run_thread()
}
break;
case CELL_MEASURE:
switch(measure_p.run_subframe(sf_idx))
switch(measure_p.run_subframe_sync(&ue_sync, sf_idx))
{
case measure::MEASURE_OK:
log_h->info("SYNC: Measured OK. Camping on cell PCI=%d...\n", cell.id);
@ -643,6 +624,9 @@ void phch_recv::run_thread()
worker_com->cur_radio_power = SRSLTE_MIN(SRSLTE_PC_MAX, worker_com->pathloss+worker_com->p0_preamble);
}
workers_pool->start_worker(worker);
intra_freq_meas.write(tti, buffer[0], SRSLTE_SF_LEN_PRB(cell.nof_prb));
break;
case 0:
log_h->error("SYNC: Sync error. Sending out-of-sync to RRC\n");
@ -789,8 +773,8 @@ phch_recv::search::ret_code phch_recv::search::run(srslte_cell_t *cell)
return CELL_NOT_FOUND;
}
// Save result
cell->id = found_cells[max_peak_cell].cell_id;
cell->cp = found_cells[max_peak_cell].cp;
cell->id = found_cells[max_peak_cell].cell_id;
cell->cp = found_cells[max_peak_cell].cp;
float cfo = found_cells[max_peak_cell].cfo;
printf("\n");
@ -864,7 +848,7 @@ void phch_recv::sfn_sync::init(srslte_ue_sync_t *ue_sync, cf_t *buffer[SRSLTE_MA
this->buffer[i] = buffer[i];
}
if (srslte_ue_mib_init(&ue_mib, SRSLTE_MAX_PRB)) {
if (srslte_ue_mib_init(&ue_mib, this->buffer, SRSLTE_MAX_PRB)) {
Error("SYNC: Initiating UE MIB decoder\n");
}
}
@ -885,7 +869,7 @@ void phch_recv::sfn_sync::reset()
cnt = 0;
}
phch_recv::sfn_sync::ret_code phch_recv::sfn_sync::run_subframe(srslte_cell_t *cell, uint32_t *tti_cnt)
phch_recv::sfn_sync::ret_code phch_recv::sfn_sync::run_subframe(srslte_cell_t *cell, uint32_t *tti_cnt, bool sfidx_only)
{
uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN];
@ -899,10 +883,19 @@ phch_recv::sfn_sync::ret_code phch_recv::sfn_sync::run_subframe(srslte_cell_t *c
if (ret == 1) {
if (srslte_ue_sync_get_sfidx(ue_sync) == 0) {
// Skip MIB decoding if we are only interested in subframe 0
if (sfidx_only) {
if (tti_cnt) {
*tti_cnt = 0;
}
return SFX0_FOUND;
}
int sfn_offset = 0;
Info("SYNC: Trying to decode MIB... SNR=%.1f dB\n", 10*log10(srslte_chest_dl_get_snr(&ue_mib.chest)));
int n = srslte_ue_mib_decode(&ue_mib, buffer[0], bch_payload, NULL, &sfn_offset);
int n = srslte_ue_mib_decode(&ue_mib, bch_payload, NULL, &sfn_offset);
if (n < 0) {
Error("SYNC: Error decoding MIB while synchronising SFN");
return ERROR;
@ -944,16 +937,15 @@ phch_recv::sfn_sync::ret_code phch_recv::sfn_sync::run_subframe(srslte_cell_t *c
/*********
* Measurement class
*/
void phch_recv::measure::init(srslte_ue_sync_t *ue_sync, cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t nof_subrames)
void phch_recv::measure::init(cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t nof_subframes)
{
this->log_h = log_h;
this->nof_subframes = nof_subrames;
this->ue_sync = ue_sync;
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, SRSLTE_MAX_PRB, nof_rx_antennas)) {
if (srslte_ue_dl_init(&ue_dl, this->buffer, SRSLTE_MAX_PRB, nof_rx_antennas)) {
Error("SYNC: Initiating ue_dl_measure\n");
return;
}
@ -969,11 +961,11 @@ void phch_recv::measure::reset() {
mean_rsrp = 0;
mean_rsrq = 0;
mean_snr = 0;
mean_cfo = 0;
}
void phch_recv::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");
}
@ -981,50 +973,22 @@ void phch_recv::measure::set_cell(srslte_cell_t cell)
}
float phch_recv::measure::rsrp() {
return 10*log10(mean_rsrp/1000);
return mean_rsrp;
}
float phch_recv::measure::rsrq() {
return 10*log10(mean_rsrq);
return mean_rsrq;
}
float phch_recv::measure::snr() {
return 10*log10(mean_snr);
return mean_snr;
}
float phch_recv::measure::cfo() {
return mean_cfo;
}
phch_recv::measure::ret_code phch_recv::measure::run_subframe(uint32_t sf_idx)
phch_recv::measure::ret_code phch_recv::measure::run_subframe_sync(srslte_ue_sync_t *ue_sync, uint32_t sf_idx)
{
int sync_res = srslte_ue_sync_zerocopy_multi(ue_sync, buffer);
if (sync_res == 1) {
uint32_t cfi = 0;
if (srslte_ue_dl_decode_fft_estimate(&ue_dl, buffer, sf_idx, &cfi)) {
log_h->error("SYNC: Measuring RSRP: Estimating channel\n");
return ERROR;
}
float rsrp = srslte_chest_dl_get_rsrp(&ue_dl.chest);
float rsrq = srslte_chest_dl_get_rsrq(&ue_dl.chest);
float snr = srslte_chest_dl_get_snr(&ue_dl.chest);
float cfo = srslte_ue_sync_get_cfo(ue_sync);
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_cfo = SRSLTE_VEC_CMA(cfo, mean_cfo, cnt);
cnt++;
log_h->info("SYNC: Measuring RSRP %d/%d, sf_idx=%d, RSRP=%.1f dBm, SNR=%.1f dB\n",
cnt, RSRP_MEASURE_NOF_FRAMES, sf_idx,
10*log10(rsrp/1000), 10*log10(snr));
if (cnt >= nof_subframes) {
return MEASURE_OK;
}
return run_subframe(sf_idx);
} else {
log_h->error("SYNC: Measuring RSRP: Sync error\n");
return ERROR;
@ -1033,7 +997,51 @@ phch_recv::measure::ret_code phch_recv::measure::run_subframe(uint32_t sf_idx)
return IDLE;
}
phch_recv::measure::ret_code phch_recv::measure::run_multiple_subframes(cf_t *input_buffer,
uint32_t sf_idx,
uint32_t nof_sf)
{
uint32_t sf_len = SRSLTE_SF_LEN_PRB(current_prb);
ret_code ret = IDLE;
for (uint32_t i=0;i<nof_sf;i++) {
memcpy(buffer[0], &input_buffer[i*sf_len], sizeof(cf_t)*sf_len);
ret = run_subframe((sf_idx+i)%10);
if (ret != IDLE) {
return ret;
}
}
return ret;
}
phch_recv::measure::ret_code phch_recv::measure::run_subframe(uint32_t sf_idx)
{
uint32_t cfi = 0;
if (srslte_ue_dl_decode_fft_estimate(&ue_dl, buffer, sf_idx, &cfi)) {
log_h->error("SYNC: Measuring RSRP: Estimating channel\n");
return ERROR;
}
float rsrp = 10*log10(srslte_chest_dl_get_rsrp(&ue_dl.chest)) + 30;
float rsrq = 10*log10(srslte_chest_dl_get_rsrq(&ue_dl.chest));
float snr = 10*log10(srslte_chest_dl_get_snr(&ue_dl.chest));
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);
cnt++;
log_h->info("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;
}
}
@ -1044,164 +1052,246 @@ phch_recv::measure::ret_code phch_recv::measure::run_subframe(uint32_t sf_idx)
* Secondary cell receiver
*/
void phch_recv::scell_recv::init(phch_recv *parent, srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t prio, int cpu_affinity)
void phch_recv::scell_recv::init(srslte::log *log_h)
{
this->p = parent;
this->log_h = log_h;
this->nof_rx_antennas = nof_rx_antennas;
// Create the ringbuffer for secondary cell reception
for (int i=0;i<SRSLTE_MAX_PORTS;i++) {
if (srslte_ringbuffer_init(&ring_buffer[i], 10*SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB))) {
Error("SCELL: Creating ringbuffer for SCell\n");
return;
}
}
// and a separate ue_sync instance
if (srslte_ue_sync_init_multi(&ue_sync, SRSLTE_MAX_PRB, false, scell_recv_callback, nof_rx_antennas, parent)) {
Error("SCELL: Initiating ue_sync\n");
uint32_t max_fft_sz = srslte_symbol_sz(100);
uint32_t max_sf_size = SRSLTE_SF_LEN(max_fft_sz);
sf_buffer[0] = (cf_t*) srslte_vec_malloc(sizeof(cf_t)*max_sf_size);
measure_p.init(sf_buffer, log_h, 1, DEFAULT_MEASUREMENT_LEN);
if(srslte_sync_init(&sync_find, 15*max_sf_size, 5*max_sf_size, max_fft_sz)) {
fprintf(stderr, "Error initiating sync_find\n");
return;
}
for (uint32_t i = 0; i < nof_rx_antennas; i++) {
sf_buffer[i] = (cf_t *) srslte_vec_malloc(sizeof(cf_t) * 3 * SRSLTE_SF_LEN_PRB(100));
}
measure_p.init(&ue_sync, sf_buffer, log_h, nof_rx_antennas);
sfn_p.init(&ue_sync, sf_buffer, log_h);
srslte_sync_cp_en(&sync_find, false);
srslte_sync_set_cfo_ema_alpha(&sync_find, 0.8);
srslte_sync_set_threshold(&sync_find, 2.0);
reset();
running = true;
if (cpu_affinity < 0) {
start(prio);
} else {
start_cpu(prio, cpu_affinity);
}
}
void phch_recv::scell_recv::stop()
{
running = false;
wait_thread_finish();
}
void phch_recv::scell_recv::reset()
{
tti = 0;
measure_p.reset();
sfn_p.reset();
scell_state = IDLE;
}
void phch_recv::scell_recv::set_cell(srslte_cell_t scell) {
printf("SCELL: set scell to select, id=%d, prb=%d\n", scell.id, scell.nof_prb);
memcpy(&cell, &scell, sizeof(srslte_cell_t));
current_sflen = SRSLTE_SF_LEN_PRB(cell.nof_prb);
srslte_ue_sync_set_cell(&ue_sync, scell);
measure_p.set_cell(scell);
sfn_p.set_cell(scell);
scell_state = SCELL_SELECT;
}
bool phch_recv::scell_recv::is_enabled()
int phch_recv::scell_recv::find_cells(cf_t *input_buffer, srslte_cell_t current_cell, uint32_t nof_sf, cell_info_t cells[MAX_CELLS])
{
return scell_state != IDLE;
uint32_t fft_sz = srslte_symbol_sz(current_cell.nof_prb);
uint32_t sf_len = SRSLTE_SF_LEN(fft_sz);
if (srslte_sync_resize(&sync_find, nof_sf*sf_len, 5*sf_len, fft_sz)) {
fprintf(stderr, "Error resizing sync\n");
return SRSLTE_ERROR;
}
int nof_cells = 0;
uint32_t peak_idx = 0;
uint32_t sf_idx = 0;
uint32_t cell_id = 0;
uint32_t rem_sf = 0;
int offset = 0;
srslte_cell_t found_cell;
memcpy(&found_cell, &current_cell, sizeof(srslte_cell_t));
for (uint32_t n_id_2=0;n_id_2<3;n_id_2++) {
if (current_cell.id%3 != n_id_2) {
srslte_sync_set_N_id_2(&sync_find, n_id_2);
switch(srslte_sync_find(&sync_find, input_buffer, 0, &peak_idx)) {
case SRSLTE_SYNC_ERROR:
return SRSLTE_ERROR;
fprintf(stderr, "Error finding correlation peak\n");
return SRSLTE_ERROR;
case SRSLTE_SYNC_FOUND:
sf_idx = srslte_sync_get_sf_idx(&sync_find);
cell_id = srslte_sync_get_cell_id(&sync_find);
Info("INTRA: found peak_idx=%d, n_id_2=%d, cell_id=%d, sf=%d\n",
peak_idx, n_id_2, cell_id, sf_idx);
found_cell.id = cell_id;
measure_p.set_cell(found_cell);
offset = peak_idx-sf_len/2;
if (offset < 0) {
offset += sf_len;
sf_idx ++;
}
rem_sf = (sf_len*nof_sf - offset)/sf_len;
switch(measure_p.run_multiple_subframes(&input_buffer[offset], sf_idx, rem_sf)) {
case measure::MEASURE_OK:
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 = offset;
nof_cells++;
break;
case measure::ERROR:
Error("Measuring neighbour cell\n");
return SRSLTE_ERROR;
default:
printf("torna idle\n");
break;
}
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;
}
}
}
return nof_cells;
}
int phch_recv::scell_recv::recv(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time)
{
if (is_enabled())
{
uint32_t read_samples = nsamples;
if (read_samples > current_sflen) {
read_samples = current_sflen;
}
if (nsamples < 10) {
read_samples = 0;
}
int n = 0;
for (uint32_t i=0;i<nof_rx_antennas;i++) {
n = srslte_ringbuffer_read(&ring_buffer[i], data[i], sizeof(cf_t)*read_samples);
if (n < 0) {
Error("SCELL: Receiving from SCell buffer\n");
return -1;
}
if ((uint32_t) n < read_samples*sizeof(cf_t)) {
Error("SCELL: SCell received %d<%d samples from port %d\n", n/sizeof(cf_t), read_samples, i);
return -1;
}
// Pad with zeros if requested more samples in order to avoid consuming the buffer
for (int j=read_samples;j<nsamples;j++) {
data[i][j] = 0;
}
}
log_h->debug("SCELL: tti=%d, read %d/%d samples from buffer, buffer size=%d\n",
tti, read_samples,nsamples, srslte_ringbuffer_status(&ring_buffer[0]));
return nsamples;
/**********
* PHY measurements
*
*/
void phch_recv::meas_reset() {
// Stop all measurements
intra_freq_meas.clear_cells();
}
int phch_recv::meas_start(uint32_t earfcn, int pci) {
if (earfcn == current_earfcn) {
intra_freq_meas.add_cell(pci);
return 0;
} else {
Error("SCELL: Reception not enabled\n");
Warning("INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested measurement for %d)\n",
current_earfcn, earfcn);
return -1;
}
}
void phch_recv::scell_recv::write(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time)
{
if (is_enabled()) {
for (uint32_t i = 0; i < nof_rx_antennas; i++) {
srslte_ringbuffer_write(&ring_buffer[i], data[i], sizeof(cf_t) * nsamples);
}
int phch_recv::meas_stop(uint32_t earfcn, int pci) {
if (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);
}
return -1;
}
void phch_recv::intra_measure::init(phch_common *common, rrc_interface_phy *rrc, srslte::log *log_h) {
this->rrc = rrc;
this->log_h = log_h;
this->common = common;
receive_enabled = false;
// Start scell
scell.init(log_h);
search_buffer = (cf_t*) srslte_vec_malloc(CAPTURE_LEN_SF*SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB)*sizeof(cf_t));
if (srslte_ringbuffer_init(&ring_buffer, sizeof(cf_t)*50*SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB))) {
return;
}
running = true;
start();
}
void phch_recv::intra_measure::stop() {
running = false;
tti_sync.increase();
wait_thread_finish();
}
void phch_recv::intra_measure::set_primay_cell(uint32_t earfcn, srslte_cell_t cell) {
this->current_earfcn = earfcn;
current_sflen = SRSLTE_SF_LEN_PRB(cell.nof_prb);
memcpy(&this->primary_cell, &cell, sizeof(srslte_cell_t));
}
void phch_recv::intra_measure::clear_cells() {
active_pci.clear();
receive_enabled = false;
}
void phch_recv::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 {
Warning("INTRA: Requested to start already existing intra-frequency measurement for PCI=%d\n", pci);
}
}
void phch_recv::scell_recv::run_thread()
void phch_recv::intra_measure::rem_cell(int pci) {
std::vector<int>::iterator 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.size() == 0) {
receive_enabled = false;
}
Info("INTRA: Stopping intra-frequency measurement for pci=%d. Number of cells: %d\n", pci, active_pci.size());
} else {
Warning("INTRA: Requested to stop non-existing intra-frequency measurement for PCI=%d\n", pci);
}
}
void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples) {
if (receive_enabled) {
if ((tti%INTRA_FREQ_MEAS_PERIOD_MS) == 0) {
receiving = true;
receive_cnt = 0;
measure_tti = tti;
}
if (receiving == true) {
if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) {
receiving = false;
srslte_ringbuffer_reset(&ring_buffer);
} else {
receive_cnt++;
if (receive_cnt == CAPTURE_LEN_SF) {
tti_sync.increase();
}
}
}
}
}
void phch_recv::intra_measure::run_thread()
{
while(running) {
switch(scell_state) {
case IDLE:
usleep(1000);
break;
case SCELL_SELECT:
switch (sfn_p.run_subframe(&cell, &tti))
{
case sfn_sync::SFN_FOUND:
log_h->info("SCELL: SFN Sync OK. Camping on cell PCI=%d...\n", cell.id);
sfn_p.reset();
scell_state = SCELL_MEASURE;
break;
case sfn_sync::TIMEOUT:
log_h->info("SCELL: SFN sync timeout\n");
break;
case sfn_sync::IDLE:
break;
default:
p->radio_error();
break;
}
break;
case SCELL_MEASURE:
switch (measure_p.run_subframe(tti%10)) {
case measure::MEASURE_OK:
log_h->info("SCELL: Measured OK TTI=%5d, RSRP=%.1f dBm, RSRQ=%.1f dB, SNR=%3.1f dB, CFO=%.1f KHz, Buff=%d\n",
tti, measure_p.rsrp(), measure_p.rsrq(), measure_p.snr(), measure_p.cfo()/1000,
srslte_ringbuffer_status(&ring_buffer[0]));
measure_p.reset();
break;
case measure::IDLE:
break;
default:
p->radio_error();
break;
}
break;
if (running) {
tti_sync.wait();
}
if (running) {
Info("INTRA: Running intra-frequency measurement for %d cells\n", active_pci.size());
// Read 5 ms data from buffer
srslte_ringbuffer_read(&ring_buffer, search_buffer, CAPTURE_LEN_SF*current_sflen*sizeof(cf_t));
int found_cells = scell.find_cells(search_buffer, primary_cell, CAPTURE_LEN_SF, info);
receiving = false;
srslte_ringbuffer_reset(&ring_buffer);
for (int i=0;i<found_cells;i++) {
info[i].rsrp -= common->rx_gain_offset;
rrc->new_phy_meas(info[i].rsrp, info[i].rsrq, measure_tti, current_earfcn, info[i].pci);
}
// Look for other cells not found automatically
}
// Increase TTI counter
tti = (tti+1) % 10240;
}
}

View File

@ -1210,8 +1210,8 @@ void phch_worker::update_measurements()
{
float snr_ema_coeff = phy->args->snr_ema_coeff;
if (chest_done) {
/* Compute ADC/RX gain offset every 20 ms */
if ((tti%20) == 0 || phy->rx_gain_offset == 0) {
/* Compute ADC/RX gain offset every ~10s */
if (tti== 0 || phy->rx_gain_offset == 0) {
float rx_gain_offset = 0;
if (phy->get_radio()->has_rssi() && phy->args->rssi_sensor_enabled) {
float rssi_all_signal = srslte_chest_dl_get_rssi(&ue_dl.chest);
@ -1224,7 +1224,7 @@ void phch_worker::update_measurements()
rx_gain_offset = phy->get_radio()->get_rx_gain();
}
if (phy->rx_gain_offset) {
phy->rx_gain_offset = SRSLTE_VEC_EMA(phy->rx_gain_offset, rx_gain_offset, 0.1);
phy->rx_gain_offset = SRSLTE_VEC_EMA(phy->rx_gain_offset, rx_gain_offset, 0.5);
} else {
phy->rx_gain_offset = rx_gain_offset;
}
@ -1246,15 +1246,16 @@ void phch_worker::update_measurements()
float rsrp = 10*log10(srslte_chest_dl_get_rsrp(&ue_dl.chest)) + 30 - phy->rx_gain_offset;
float rssi = 10*log10(srslte_chest_dl_get_rssi(&ue_dl.chest)) + 30 - phy->rx_gain_offset;
// TODO: Send UE measurements to RRC where filtering is done. Now do filtering here
// Serving cell measurements are averaged over DEFAULT_MEAS_PERIOD_MS then sent to RRC
if (isnormal(rsrp)) {
if (!phy->avg_rsrp_db) {
phy->avg_rsrp_db = rsrp;
} else {
uint32_t k = 4; // Set by RRC reconfiguration message
float coeff = pow(0.5,(float) k/4);
phy->avg_rsrp_db = SRSLTE_VEC_EMA(phy->avg_rsrp_db, rsrp, coeff);
phy->avg_rsrp_db = SRSLTE_VEC_EMA(phy->avg_rsrp_db, rsrp, 0.8);
}
if ((tti%phy->serving_cell_report_period) == 0) {
phy->rrc->new_phy_meas(phy->avg_rsrp_db, phy->avg_rsrq_db, tti);
}
}
// Compute PL
float tx_crs_power = phy->config->common.pdsch_cnfg.rs_power;

View File

@ -243,15 +243,22 @@ void phy::cell_search_next()
sf_recv.cell_search_next();
}
void phy::scell_enable(bool enable)
{
sf_recv.scell_enable(enable);
}
void phy::sync_reset() {
sf_recv.reset_sync();
}
void phy::meas_reset() {
sf_recv.meas_reset();
}
int phy::meas_start(uint32_t earfcn, int pci) {
return sf_recv.meas_start(earfcn, pci);
}
int phy::meas_stop(uint32_t earfcn, int pci) {
return sf_recv.meas_stop(earfcn, pci);
}
bool phy::cell_select(uint32_t earfcn, srslte_cell_t phy_cell)
{
return sf_recv.cell_select(earfcn, phy_cell);
@ -288,9 +295,9 @@ void phy::pdcch_ul_search_reset()
workers_common.set_ul_rnti(SRSLTE_RNTI_USER, 0);
}
void phy::get_current_cell(srslte_cell_t *cell)
void phy::get_current_cell(srslte_cell_t *cell, uint32_t *current_earfcn)
{
sf_recv.get_current_cell(cell);
sf_recv.get_current_cell(cell, current_earfcn);
}
void phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm)

View File

@ -215,11 +215,6 @@ void ue::pregenerate_signals(bool enable)
phy.enable_pregen_signals(enable);
}
void ue::test_scell()
{
phy.scell_enable(true);
};
void ue::stop()
{
if(started)

View File

@ -109,6 +109,8 @@ void rrc::init(phy_interface_rrc *phy_,
set_rrc_default();
set_phy_default();
set_mac_default();
measurements.init(this);
}
void rrc::stop() {
@ -116,6 +118,10 @@ void rrc::stop() {
wait_thread_finish();
}
void rrc::run_tti(uint32_t tti) {
measurements.run_tti(tti);
}
rrc_state_t rrc::get_state() {
return state;
}
@ -418,6 +424,10 @@ void rrc::select_next_cell_in_plmn() {
rrc_log->info("No more known cells...\n");
}
void rrc::new_phy_meas(float rsrp, float rsrq, uint32_t tti, uint32_t earfcn, uint32_t pci) {
measurements.new_phy_meas(earfcn, pci, rsrp, rsrq, tti);
}
void rrc::cell_found(uint32_t earfcn, srslte_cell_t phy_cell, float rsrp) {
// find if cell_id-earfcn combination already exists
@ -562,7 +572,8 @@ void rrc::timer_expired(uint32_t timeout_id) {
} else if (timeout_id == t301) {
rrc_log->info("Timer T301 expired: Going to RRC IDLE\n");
state = RRC_STATE_LEAVE_CONNECTED;
} else {
// fw to measurement
} else if (!measurements.timer_expired(timeout_id)) {
rrc_log->error("Timeout from unknown timer id %d\n", timeout_id);
}
}
@ -717,32 +728,22 @@ void rrc::con_restablish_cell_reselected()
void rrc::send_con_restablish_complete() {
rrc_log->debug("Preparing RRC Connection Reestablishment Complete\n");
LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg;
rrc_log->console("RRC Connected\n");
state = RRC_STATE_CONNECTED;
// Prepare ConnectionSetupComplete packet
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_REEST_COMPLETE;
ul_dcch_msg.msg.rrc_con_reest_complete.rrc_transaction_id = transaction_id;
liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf);
// Byte align and pack the message bits for PDCP
if ((bit_buf.N_bits % 8) != 0) {
for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++)
bit_buf.msg[bit_buf.N_bits + i] = 0;
bit_buf.N_bits += 8 - (bit_buf.N_bits % 8);
}
byte_buffer_t *pdcp_buf = pool_allocate;;
srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits);
pdcp_buf->N_bytes = bit_buf.N_bits / 8;
state = RRC_STATE_CONNECTED;
rrc_log->console("RRC Connected\n");
rrc_log->info("Sending RRC Connection Reestablishment Complete\n");
pdcp->write_sdu(RB_ID_SRB1, pdcp_buf);
send_ul_dcch_msg();
}
void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) {
rrc_log->debug("Preparing RRC Connection Setup Complete\n");
LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg;
state = RRC_STATE_CONNECTED;
rrc_log->console("RRC Connected\n");
// Prepare ConnectionSetupComplete packet
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_SETUP_COMPLETE;
@ -751,96 +752,38 @@ void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) {
ul_dcch_msg.msg.rrc_con_setup_complete.selected_plmn_id = 1;
memcpy(ul_dcch_msg.msg.rrc_con_setup_complete.dedicated_info_nas.msg, nas_msg->msg, nas_msg->N_bytes);
ul_dcch_msg.msg.rrc_con_setup_complete.dedicated_info_nas.N_bytes = nas_msg->N_bytes;
liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf);
// Byte align and pack the message bits for PDCP
if ((bit_buf.N_bits % 8) != 0) {
for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++)
bit_buf.msg[bit_buf.N_bits + i] = 0;
bit_buf.N_bits += 8 - (bit_buf.N_bits % 8);
}
byte_buffer_t *pdcp_buf = pool_allocate;;
srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits);
pdcp_buf->N_bytes = bit_buf.N_bits / 8;
pdcp_buf->set_timestamp();
state = RRC_STATE_CONNECTED;
rrc_log->console("RRC Connected\n");
rrc_log->info("Sending RRC Connection Setup Complete\n");
pdcp->write_sdu(RB_ID_SRB1, pdcp_buf);
send_ul_dcch_msg();
}
void rrc::send_ul_info_transfer(uint32_t lcid, byte_buffer_t *sdu) {
rrc_log->debug("Preparing RX Info Transfer\n");
LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg;
// Prepare RX INFO packet
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER;
ul_dcch_msg.msg.ul_info_transfer.dedicated_info_type = LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_NAS;
memcpy(ul_dcch_msg.msg.ul_info_transfer.dedicated_info.msg, sdu->msg, sdu->N_bytes);
ul_dcch_msg.msg.ul_info_transfer.dedicated_info.N_bytes = sdu->N_bytes;
liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf);
// Reset and reuse sdu buffer
byte_buffer_t *pdu = sdu;
pdu->reset();
// Byte align and pack the message bits for PDCP
if ((bit_buf.N_bits % 8) != 0) {
for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++)
bit_buf.msg[bit_buf.N_bits + i] = 0;
bit_buf.N_bits += 8 - (bit_buf.N_bits % 8);
}
srslte_bit_pack_vector(bit_buf.msg, pdu->msg, bit_buf.N_bits);
pdu->N_bytes = bit_buf.N_bits / 8;
pdu->set_timestamp();
pdu->set_timestamp();
rrc_log->info("Sending RX Info Transfer\n");
pdcp->write_sdu(lcid, pdu);
send_ul_dcch_msg(sdu);
}
void rrc::send_security_mode_complete(uint32_t lcid, byte_buffer_t *pdu) {
rrc_log->debug("Preparing Security Mode Complete\n");
LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg;
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE;
ul_dcch_msg.msg.security_mode_complete.rrc_transaction_id = transaction_id;
liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf);
// Byte align and pack the message bits for PDCP
if ((bit_buf.N_bits % 8) != 0) {
for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++)
bit_buf.msg[bit_buf.N_bits + i] = 0;
bit_buf.N_bits += 8 - (bit_buf.N_bits % 8);
}
srslte_bit_pack_vector(bit_buf.msg, pdu->msg, bit_buf.N_bits);
pdu->N_bytes = bit_buf.N_bits / 8;
pdu->set_timestamp();
rrc_log->info("Sending Security Mode Complete\n");
pdcp->write_sdu(lcid, pdu);
send_ul_dcch_msg(pdu);
}
void rrc::send_rrc_con_reconfig_complete(uint32_t lcid, byte_buffer_t *pdu) {
rrc_log->debug("Preparing RRC Connection Reconfig Complete\n");
LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg;
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE;
ul_dcch_msg.msg.rrc_con_reconfig_complete.rrc_transaction_id = transaction_id;
liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf);
// Byte align and pack the message bits for PDCP
if ((bit_buf.N_bits % 8) != 0) {
for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++)
bit_buf.msg[bit_buf.N_bits + i] = 0;
bit_buf.N_bits += 8 - (bit_buf.N_bits % 8);
}
srslte_bit_pack_vector(bit_buf.msg, pdu->msg, bit_buf.N_bits);
pdu->N_bytes = bit_buf.N_bits / 8;
pdu->set_timestamp();
rrc_log->info("Sending RRC Connection Reconfig Complete\n");
pdcp->write_sdu(lcid, pdu);
send_ul_dcch_msg();
}
@ -854,7 +797,7 @@ void rrc::handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGU
printf("received con reconfig no rr confg present\n");
}
if (reconfig->meas_cnfg_present) {
//TODO: handle meas_cnfg
measurements.parse_meas_config(&reconfig->meas_cnfg);
}
if (reconfig->mob_ctrl_info_present) {
//TODO: handle mob_ctrl_info
@ -864,7 +807,7 @@ void rrc::handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGU
byte_buffer_t *nas_sdu;
for (i = 0; i < reconfig->N_ded_info_nas; i++) {
nas_sdu = pool_allocate;;
nas_sdu = pool_allocate;
memcpy(nas_sdu->msg, &reconfig->ded_info_nas_list[i].msg, reconfig->ded_info_nas_list[i].N_bytes);
nas_sdu->N_bytes = reconfig->ded_info_nas_list[i].N_bytes;
nas->write_pdu(lcid, nas_sdu);
@ -1051,6 +994,33 @@ void rrc::write_pdu_pcch(byte_buffer_t *pdu) {
*
*
*******************************************************************************/
void rrc::send_ul_dcch_msg(byte_buffer_t *pdu)
{
liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf);
// Byte align and pack the message bits for PDCP
if ((bit_buf.N_bits % 8) != 0) {
for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++)
bit_buf.msg[bit_buf.N_bits + i] = 0;
bit_buf.N_bits += 8 - (bit_buf.N_bits % 8);
}
// Reset and reuse sdu buffer if provided
byte_buffer_t *pdcp_buf = pdu;
if (pdcp_buf) {
pdcp_buf->reset();
} else {
pdcp_buf = pool_allocate;
}
srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits);
pdcp_buf->N_bytes = bit_buf.N_bits / 8;
pdcp_buf->set_timestamp();
rrc_log->info("Sending %s\n", liblte_rrc_ul_dcch_msg_type_text[ul_dcch_msg.msg_type]);
pdcp->write_sdu(RB_ID_SRB1, pdcp_buf);
}
void rrc::write_sdu(uint32_t lcid, byte_buffer_t *sdu) {
rrc_log->info_hex(sdu->msg, sdu->N_bytes, "RX %s SDU", get_rb_name(lcid).c_str());
switch (state) {
@ -1764,4 +1734,582 @@ void rrc::set_rrc_default() {
}
/************************************************************************
*
*
* RRC Measurements
*
*
************************************************************************/
void rrc::rrc_meas::init(rrc *parent) {
this->parent = parent;
this->log_h = parent->rrc_log;
this->phy = parent->phy;
this->mac_timers = parent->mac_timers;
s_measure_enabled = false;
reset();
}
void rrc::rrc_meas::reset()
{
bzero(&pcell_measurement, sizeof(meas_value_t));
filter_k_rsrp = liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_FC4];
filter_k_rsrq = liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_FC4];
objects.clear();
active.clear();
reports_cfg.clear();
phy->meas_reset();
}
/* L3 filtering 5.5.3.2 */
void rrc::rrc_meas::L3_filter(meas_value_t *value, float values[NOF_MEASUREMENTS])
{
for (int i=0;i<NOF_MEASUREMENTS;i++) {
if (value->ms[i]) {
value->ms[i] = SRSLTE_VEC_EMA(values[i], value->ms[i], filter_a[i]);
} else {
value->ms[i] = values[i];
}
}
}
void rrc::rrc_meas::new_phy_meas(uint32_t earfcn, uint32_t pci, float rsrp, float rsrq, uint32_t tti)
{
log_h->info("MEAS: New measurement earfcn=%d, pci=%d, rsrp=%f, rsrq=%f, tti=%d\n", earfcn, pci, rsrp, rsrq, tti);
float values[NOF_MEASUREMENTS] = {rsrp, rsrq};
// This indicates serving cell
if (earfcn == 0) {
L3_filter(&pcell_measurement, values);
} else {
// Save PHY measurement for all active measurements whose earfcn/pci matches
for(std::map<uint32_t, meas_t>::iterator iter=active.begin(); iter!=active.end(); ++iter) {
meas_t *m = &iter->second;
if (objects[m->object_id].earfcn == earfcn) {
// If it's a newly discovered cell, add it to objects
if (!m->cell_values.count(pci)) {
uint32_t cell_idx = objects[m->object_id].cells.size();
objects[m->object_id].cells[cell_idx].pci = pci;
objects[m->object_id].cells[cell_idx].q_offset = 0;
}
// Update or add cell
L3_filter(&m->cell_values[pci], values);
return;
}
}
parent->rrc_log->warning("MEAS: Received measurement from unknown EARFCN=%d\n", earfcn);
}
}
void rrc::rrc_meas::run_tti(uint32_t tti) {
// Measurement Report Triggering Section 5.5.4
calculate_triggers(tti);
}
bool rrc::rrc_meas::find_earfcn_cell(uint32_t earfcn, uint32_t pci, meas_obj_t **object, int *cell_idx) {
if (object) {
*object = NULL;
}
for (std::map<uint32_t, meas_obj_t>::iterator obj = objects.begin(); obj != objects.end(); ++obj) {
if (obj->second.earfcn == earfcn) {
if (object) {
*object = &obj->second;
}
for (std::map<uint32_t, meas_cell_t>::iterator c = obj->second.cells.begin(); c != obj->second.cells.end(); ++c) {
if (c->second.pci == pci) {
if (cell_idx) {
*cell_idx = c->first;
return true;
}
}
}
// return true if cell idx not found but frequency is found
if (cell_idx) {
*cell_idx = -1;
}
return true;
}
}
return false;
}
/* Generate report procedure 5.5.5 */
void rrc::rrc_meas::generate_report(uint32_t meas_id)
{
parent->ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_MEASUREMENT_REPORT;
LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *report = &parent->ul_dcch_msg.msg.measurement_report;
bzero(report, sizeof(LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT));
meas_t *m = &active[meas_id];
report_cfg_t *cfg = &reports_cfg[m->report_id];
report->meas_id = meas_id;
report->pcell_rsrp_result = value_to_range(RSRP, pcell_measurement.ms[RSRP]);
report->pcell_rsrq_result = value_to_range(RSRQ, pcell_measurement.ms[RSRQ]);
log_h->console("MEAS: Generate report MeasId=%d, rsrp=%f rsrq=%f\n",
report->meas_id, pcell_measurement.ms[RSRP], pcell_measurement.ms[RSRQ]);
// TODO: report up to 8 best cells
for (std::map<uint32_t, meas_value_t>::iterator cell = m->cell_values.begin(); cell != m->cell_values.end(); ++cell)
{
if (cell->second.triggered && report->meas_result_neigh_cells.eutra.n_result < 8)
{
LIBLTE_RRC_MEAS_RESULT_EUTRA_STRUCT *rc = &report->meas_result_neigh_cells.eutra.result_eutra_list[report->meas_result_neigh_cells.eutra.n_result];
rc->phys_cell_id = cell->first;
rc->meas_result.have_rsrp = cfg->report_quantity==RSRP || cfg->report_quantity==BOTH;
rc->meas_result.have_rsrq = cfg->report_quantity==RSRQ || cfg->report_quantity==BOTH;
rc->meas_result.rsrp_result = value_to_range(RSRP, cell->second.ms[RSRP]);
rc->meas_result.rsrq_result = value_to_range(RSRQ, cell->second.ms[RSRQ]);
log_h->console("MEAS: Add neigh=%d, pci=%d, rsrp=%f, rsrq=%f\n",
report->meas_result_neigh_cells.eutra.n_result, rc->phys_cell_id,
cell->second.ms[RSRP], cell->second.ms[RSRQ]);
report->meas_result_neigh_cells.eutra.n_result++;
}
}
report->have_meas_result_neigh_cells = report->meas_result_neigh_cells.eutra.n_result > 0;
m->nof_reports_sent++;
mac_timers->timer_get(m->periodic_timer)->stop();
if (m->nof_reports_sent < cfg->amount) {
mac_timers->timer_get(m->periodic_timer)->reset();
mac_timers->timer_get(m->periodic_timer)->run();
} else {
if (cfg->trigger_type == report_cfg_t::PERIODIC) {
m->triggered = false;
}
}
// Send to lower layers
parent->send_ul_dcch_msg();
}
/* Handle entering/leaving event conditions 5.5.4.1 */
bool rrc::rrc_meas::process_event(LIBLTE_RRC_EVENT_EUTRA_STRUCT *event, uint32_t tti,
bool enter_condition, bool exit_condition,
meas_t *m, meas_value_t *cell)
{
bool generate_report = false;
if (enter_condition && (!m->triggered || !cell->triggered)) {
if (!cell->timer_enter_triggered) {
cell->timer_enter_triggered = true;
cell->enter_tti = tti;
} else if (srslte_tti_interval(tti, cell->enter_tti) >= event->time_to_trigger) {
m->triggered = true;
cell->triggered = true;
m->nof_reports_sent = 0;
generate_report = true;
}
} else if (exit_condition) {
if (!cell->timer_exit_triggered) {
cell->timer_exit_triggered = true;
cell->exit_tti = tti;
} else if (srslte_tti_interval(tti, cell->exit_tti) >= event->time_to_trigger) {
m->triggered = false;
cell->triggered = false;
mac_timers->timer_get(m->periodic_timer)->stop();
if (event) {
if (event->event_id == LIBLTE_RRC_EVENT_ID_EUTRA_A3 && event->event_a3.report_on_leave) {
generate_report = true;
}
}
}
}
if (!enter_condition) {
cell->timer_enter_triggered = false;
}
if (!enter_condition) {
cell->timer_exit_triggered = false;
}
return generate_report;
}
/* Calculate trigger conditions for each cell 5.5.4 */
void rrc::rrc_meas::calculate_triggers(uint32_t tti)
{
float Ofp = 0, Ocp = 0;
meas_obj_t *serving_object = NULL;
int serving_cell_idx = 0;
// Get serving cell
if (active.size()) {
uint32_t current_earfcn = 0;
srslte_cell_t current_cell;
phy->get_current_cell(&current_cell, &current_earfcn);
if (find_earfcn_cell(current_earfcn, current_cell.id, &serving_object, &serving_cell_idx)) {
Ofp = serving_object->q_offset;
if (serving_cell_idx >= 0) {
Ocp = serving_object->cells[serving_cell_idx].q_offset;
}
} else {
log_h->warning("Can't find current eafcn=%d, pci=%d in objects list. Using Ofp=0, Ocp=0\n", current_earfcn, current_cell.id);
}
}
for (std::map<uint32_t, meas_t>::iterator m = active.begin(); m != active.end(); ++m) {
report_cfg_t *cfg = &reports_cfg[m->second.report_id];
float hyst = 0.5*cfg->event.hysteresis;
float Mp = pcell_measurement.ms[cfg->trigger_quantity];
LIBLTE_RRC_EVENT_ID_EUTRA_ENUM event_id = cfg->event.event_id;
const char *event_str = liblte_rrc_event_id_eutra_text[event_id];
bool gen_report = false;
if (cfg->trigger_type == report_cfg_t::EVENT) {
// A1 & A2 are for serving cell only
if (event_id < LIBLTE_RRC_EVENT_ID_EUTRA_A3) {
bool enter_condition;
bool exit_condition;
if (event_id == LIBLTE_RRC_EVENT_ID_EUTRA_A1) {
enter_condition = Mp - hyst > range_to_value(cfg->trigger_quantity, cfg->event.event_a1.eutra.range);
exit_condition = Mp + hyst < range_to_value(cfg->trigger_quantity, cfg->event.event_a1.eutra.range);
} else {
enter_condition = Mp + hyst < range_to_value(cfg->trigger_quantity, cfg->event.event_a1.eutra.range);
exit_condition = Mp - hyst > range_to_value(cfg->trigger_quantity, cfg->event.event_a1.eutra.range);
}
gen_report |= process_event(&cfg->event, tti, enter_condition, exit_condition,
&m->second, &m->second.cell_values[serving_cell_idx]);
// Rest are evaluated for every cell in frequency
} else {
meas_obj_t *obj = &objects[m->second.object_id];
for (std::map<uint32_t, meas_cell_t>::iterator cell = obj->cells.begin(); cell != obj->cells.end(); ++cell) {
float Ofn = obj->q_offset;
float Ocn = cell->second.q_offset;
float Mn = m->second.cell_values[cell->second.pci].ms[cfg->trigger_quantity];
float Off=0, th=0, th1=0, th2=0;
bool enter_condition = false;
bool exit_condition = false;
switch (event_id) {
case LIBLTE_RRC_EVENT_ID_EUTRA_A3:
Off = 0.5*cfg->event.event_a3.offset;
enter_condition = Mn + Ofn + Ocn - hyst > Mp + Ofp + Ocp + Off;
exit_condition = Mn + Ofn + Ocn + hyst < Mp + Ofp + Ocp + Off;
break;
case LIBLTE_RRC_EVENT_ID_EUTRA_A4:
th = range_to_value(cfg->trigger_quantity, cfg->event.event_a4.eutra.range);
enter_condition = Mn + Ofn + Ocn - hyst > th;
exit_condition = Mn + Ofn + Ocn + hyst < th;
break;
case LIBLTE_RRC_EVENT_ID_EUTRA_A5:
th1 = range_to_value(cfg->trigger_quantity, cfg->event.event_a5.eutra1.range);
th2 = range_to_value(cfg->trigger_quantity, cfg->event.event_a5.eutra2.range);
enter_condition = (Mp + hyst < th1) && (Mn + Ofn + Ocn - hyst > th2);
exit_condition = (Mp - hyst > th1) && (Mn + Ofn + Ocn + hyst < th2);
break;
default:
log_h->error("Error event %s not implemented\n", event_str);
}
gen_report |= process_event(&cfg->event, tti, enter_condition, exit_condition,
&m->second, &m->second.cell_values[cell->second.pci]);
}
}
}
if (gen_report) {
generate_report(m->first);
}
}
}
// 5.5.4.1 expiry of periodical reporting timer
bool rrc::rrc_meas::timer_expired(uint32_t timer_id) {
for (std::map<uint32_t, meas_t>::iterator iter = active.begin(); iter != active.end(); ++iter) {
if (iter->second.periodic_timer == timer_id) {
generate_report(iter->first);
return true;
}
}
return false;
}
void rrc::rrc_meas::stop_reports_object(uint32_t object_id) {
for (std::map<uint32_t, meas_t>::iterator iter = active.begin(); iter != active.end(); ++iter) {
meas_t *m = &iter->second;
if (m->object_id == object_id) {
mac_timers->timer_get(m->periodic_timer)->stop();
m->triggered = false;
}
}
}
void rrc::rrc_meas::remove_meas_object(uint32_t object_id) {
// CHECKME: Is the delete from the map correct?
for(std::map<uint32_t, meas_t>::iterator iter=active.begin(); iter!=active.end(); ++iter) {
meas_t m = iter->second;
if (m.object_id == object_id) {
remove_meas_id(iter->first);
}
}
}
void rrc::rrc_meas::remove_meas_report(uint32_t report_id) {
// CHECKME: Is the delete from the map correct?
for(std::map<uint32_t, meas_t>::iterator iter=active.begin(); iter!=active.end(); ++iter) {
meas_t m = iter->second;
if (m.report_id == report_id) {
remove_meas_id(iter->first);
}
}
}
void rrc::rrc_meas::remove_meas_id(uint32_t meas_id) {
mac_timers->timer_get(active[meas_id].periodic_timer)->stop();
mac_timers->timer_release_id(active[meas_id].periodic_timer);
active.erase(meas_id);
log_h->info("MEAS: Removed measId=%d\n", meas_id);
}
/* Parses MeasConfig object from RRCConnectionReconfiguration message and applies configuration
* as per section 5.5.2
*/
void rrc::rrc_meas::parse_meas_config(LIBLTE_RRC_MEAS_CONFIG_STRUCT *cfg)
{
// Measurement identity removal 5.5.2.2
for (uint32_t i=0;i<cfg->N_meas_id_to_remove;i++) {
remove_meas_id(cfg->meas_id_to_remove_list[i]);
}
// Measurement identity addition/modification 5.5.2.3
if (cfg->meas_id_to_add_mod_list_present) {
for (uint32_t i=0;i<cfg->meas_id_to_add_mod_list.N_meas_id;i++) {
LIBLTE_RRC_MEAS_ID_TO_ADD_MOD_STRUCT *measId = &cfg->meas_id_to_add_mod_list.meas_id_list[i];
// Stop the timer if the entry exists or create the timer if not
if (active.count(measId->meas_id)) {
mac_timers->timer_get(active[measId->meas_id].periodic_timer)->stop();
} else {
active[measId->meas_id].periodic_timer = mac_timers->timer_get_unique_id();
}
active[measId->meas_id].object_id = measId->meas_obj_id;
active[measId->meas_id].report_id = measId->rep_cnfg_id;
log_h->info("MEAS: Added measId=%d, measObjectId=%d, reportConfigId=%d\n",
measId->meas_id, measId->meas_obj_id, measId->rep_cnfg_id);
}
}
// Measurement object removal 5.5.2.4
for (uint32_t i=0;i<cfg->N_meas_obj_to_remove;i++) {
objects.erase(cfg->meas_obj_to_remove_list[i]);
remove_meas_object(cfg->meas_obj_to_remove_list[i]);
log_h->info("MEAS: Removed measObjectId=%d\n", cfg->meas_obj_to_remove_list[i]);
}
// Measurement object addition/modification Section 5.5.2.5
if (cfg->meas_obj_to_add_mod_list_present) {
for (uint32_t i=0;i<cfg->meas_obj_to_add_mod_list.N_meas_obj;i++) {
if (cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_type == LIBLTE_RRC_MEAS_OBJECT_TYPE_EUTRA) {
LIBLTE_RRC_MEAS_OBJECT_EUTRA_STRUCT *src_obj = &cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_eutra;
// Access the object if exists or create it
meas_obj_t *dst_obj = &objects[cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_id];
dst_obj->earfcn = src_obj->carrier_freq;;
if (src_obj->offset_freq_not_default) {
dst_obj->q_offset = liblte_rrc_q_offset_range_num[src_obj->offset_freq];
} else {
dst_obj->q_offset = 0;
}
if (src_obj->black_cells_to_remove_list_present) {
for (uint32_t j=0;j<src_obj->black_cells_to_remove_list.N_cell_idx;j++) {
dst_obj->cells.erase(src_obj->black_cells_to_remove_list.cell_idx[j]);
}
}
for (uint32_t j=0;j<src_obj->N_cells_to_add_mod;j++) {
dst_obj->cells[src_obj->cells_to_add_mod_list[j].cell_idx].q_offset = liblte_rrc_q_offset_range_num[src_obj->cells_to_add_mod_list[j].cell_offset];
dst_obj->cells[src_obj->cells_to_add_mod_list[j].cell_idx].pci = src_obj->cells_to_add_mod_list[j].pci;
log_h->info("MEAS: Added measObjectId=%d, earfcn=%d, q_offset=%f, pci=%d, offset_cell=%f\n",
cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_id, dst_obj->earfcn, dst_obj->q_offset,
dst_obj->cells[src_obj->cells_to_add_mod_list[j].cell_idx].q_offset,
dst_obj->cells[src_obj->cells_to_add_mod_list[j].cell_idx].pci);
}
// Untrigger reports and stop timers
stop_reports_object(cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_id);
// TODO: Blackcells
// TODO: meassubframepattern
} else {
log_h->warning("MEAS: Unsupported MeasObject type %s\n",
liblte_rrc_meas_object_type_text[cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_type]);
}
}
}
// Reporting configuration removal 5.5.2.6
for (uint32_t i=0;i<cfg->N_rep_cnfg_to_remove;i++) {
reports_cfg.erase(cfg->rep_cnfg_to_remove_list[i]);
remove_meas_report(cfg->rep_cnfg_to_remove_list[i]);
log_h->info("MEAS: Removed reportConfigId=%d\n", cfg->rep_cnfg_to_remove_list[i]);
}
// Reporting configuration addition/modification 5.5.2.7
if (cfg->rep_cnfg_to_add_mod_list_present) {
for (uint32_t i=0;i<cfg->rep_cnfg_to_add_mod_list.N_rep_cnfg;i++) {
if (cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_type == LIBLTE_RRC_REPORT_CONFIG_TYPE_EUTRA) {
LIBLTE_RRC_REPORT_CONFIG_EUTRA_STRUCT *src_rep = &cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_eutra;
// Access the object if exists or create it
report_cfg_t *dst_rep = &reports_cfg[cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_id];
dst_rep->trigger_type = src_rep->trigger_type==LIBLTE_RRC_TRIGGER_TYPE_EUTRA_EVENT?report_cfg_t::EVENT:report_cfg_t::PERIODIC;
dst_rep->event = src_rep->event;
dst_rep->amount = liblte_rrc_report_amount_num[src_rep->report_amount];
dst_rep->interval = liblte_rrc_report_interval_num[src_rep->report_interval];
dst_rep->max_cell = src_rep->max_report_cells;
dst_rep->trigger_quantity = (quantity_t) src_rep->trigger_quantity;
dst_rep->report_quantity = src_rep->report_quantity==LIBLTE_RRC_REPORT_QUANTITY_SAME_AS_TRIGGER_QUANTITY?dst_rep->trigger_quantity:BOTH;
log_h->info("MEAS: Added reportConfigId=%d, event=%s, amount=%d, interval=%d\n",
cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_id,
liblte_rrc_event_id_eutra_text[dst_rep->event.event_id],
dst_rep->amount, dst_rep->interval);
// Reset reports counter
for(std::map<uint32_t, meas_t>::iterator iter=active.begin(); iter!=active.end(); ++iter) {
meas_t m = iter->second;
if (m.report_id == cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_id) {
iter->second.nof_reports_sent = 0;
m.triggered = false;
mac_timers->timer_get(m.periodic_timer)->stop();
}
}
} else {
log_h->warning("MEAS: Unsupported reportConfigType %s\n", liblte_rrc_report_config_type_text[cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_type]);
}
}
}
// Quantity configuration 5.5.2.8
if (cfg->quantity_cnfg_present && cfg->quantity_cnfg.qc_eutra_present) {
if (cfg->quantity_cnfg.qc_eutra.fc_rsrp_not_default) {
filter_k_rsrp = liblte_rrc_filter_coefficient_num[cfg->quantity_cnfg.qc_eutra.fc_rsrp];
} else {
filter_k_rsrp = liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_FC4];
}
if (cfg->quantity_cnfg.qc_eutra.fc_rsrq_not_default) {
filter_k_rsrq = liblte_rrc_filter_coefficient_num[cfg->quantity_cnfg.qc_eutra.fc_rsrq];
} else {
filter_k_rsrq = liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_FC4];
}
filter_a[RSRP] = pow(0.5, (float) filter_k_rsrp/4);
filter_a[RSRQ] = pow(0.5, (float) filter_k_rsrq/4);
log_h->info("MEAS: Quantity configuration k_rsrp=%d, k_rsrq=%d\n", filter_k_rsrp, filter_k_rsrq);
}
// S-Measure
if (cfg->s_meas_present) {
if (cfg->s_meas) {
s_measure_enabled = true;
s_measure_value = range_to_value(RSRP, cfg->s_meas);
} else {
s_measure_enabled = false;
}
}
update_phy();
}
/* Instruct PHY to start measurement */
void rrc::rrc_meas::update_phy()
{
phy->meas_reset();
if (pcell_measurement.ms[RSRP] < s_measure_value || !s_measure_enabled) {
for(std::map<uint32_t, meas_t>::iterator iter=active.begin(); iter!=active.end(); ++iter) {
meas_t m = iter->second;
meas_obj_t o = objects[m.object_id];
// Instruct PHY to look for neighbour cells on this frequency
phy->meas_start(o.earfcn);
for(std::map<uint32_t, meas_cell_t>::iterator iter=o.cells.begin(); iter!=o.cells.end(); ++iter) {
// Instruct PHY to look for cells IDs on this frequency
phy->meas_start(o.earfcn, iter->second.pci);
}
}
}
}
uint8_t rrc::rrc_meas::value_to_range(quantity_t quant, float value) {
uint8_t range = 0;
switch(quant) {
case RSRP:
if (value < -140) {
range = 0;
} else if (-140 <= value && value < -44) {
range = 1 + (uint8_t) (value + 140);
} else {
range = 97;
}
break;
case RSRQ:
if (value < -19.5) {
range = 0;
} else if (-19.5 <= value && value < -3) {
range = 1 + (uint8_t) (2*(value + 19.5));
} else {
range = 34;
}
break;
case BOTH:
printf("Error quantity both not supported in value_to_range\n");
break;
}
return range;
}
float rrc::rrc_meas::range_to_value(quantity_t quant, uint8_t range) {
float val = 0;
switch(quant) {
case RSRP:
val = -140+(float) range;
break;
case RSRQ:
val = -19.5+(float) range/2;
break;
case BOTH:
printf("Error quantity both not supported in range_to_value\n");
break;
}
return val;
}
} // namespace srsue