mirror of https://github.com/PentHertz/srsLTE.git
srsUE takes and reports intra-frequency measurements correctly
This commit is contained in:
parent
641eceb328
commit
12d8b373c7
|
@ -61,7 +61,6 @@ set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "")
|
||||||
option(ENABLE_SRSUE "Build srsUE application" ON)
|
option(ENABLE_SRSUE "Build srsUE application" ON)
|
||||||
option(ENABLE_SRSENB "Build srsENB 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_GUI "Enable GUI (using srsGUI)" ON)
|
||||||
option(ENABLE_BLADERF "Enable BladeRF" ON)
|
option(ENABLE_BLADERF "Enable BladeRF" ON)
|
||||||
|
|
||||||
|
@ -189,21 +188,6 @@ if(ENABLE_GUI)
|
||||||
endif(SRSGUI_FOUND)
|
endif(SRSGUI_FOUND)
|
||||||
endif(ENABLE_GUI)
|
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
|
# Install Dirs
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
|
@ -1218,6 +1218,9 @@ typedef enum{
|
||||||
}LIBLTE_RRC_REPORT_AMOUNT_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",
|
static const char liblte_rrc_report_amount_text[LIBLTE_RRC_REPORT_AMOUNT_N_ITEMS][20] = { "r1", "r2", "r4", "r8",
|
||||||
"r16", "r32", "r64", "INFINITY"};
|
"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{
|
typedef enum{
|
||||||
LIBLTE_RRC_THRESHOLD_UTRA_TYPE_RSCP = 0,
|
LIBLTE_RRC_THRESHOLD_UTRA_TYPE_RSCP = 0,
|
||||||
LIBLTE_RRC_THRESHOLD_UTRA_TYPE_ECNO,
|
LIBLTE_RRC_THRESHOLD_UTRA_TYPE_ECNO,
|
||||||
|
|
|
@ -78,22 +78,30 @@ class periodic_thread : public thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void start_periodic(int period_us_, int priority = -1) {
|
void start_periodic(int period_us_, int priority = -1) {
|
||||||
|
run_enable = true;
|
||||||
period_us = period_us_;
|
period_us = period_us_;
|
||||||
start(priority);
|
start(priority);
|
||||||
}
|
}
|
||||||
|
void stop() {
|
||||||
|
run_enable = false;
|
||||||
|
wait_thread_finish();
|
||||||
|
}
|
||||||
protected:
|
protected:
|
||||||
virtual void run_period() = 0;
|
virtual void run_period() = 0;
|
||||||
private:
|
private:
|
||||||
int wakeups_missed;
|
int wakeups_missed;
|
||||||
int timer_fd;
|
int timer_fd;
|
||||||
int period_us;
|
int period_us;
|
||||||
|
bool run_enable;
|
||||||
void run_thread() {
|
void run_thread() {
|
||||||
if (make_periodic()) {
|
if (make_periodic()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while(1) {
|
while(run_enable) {
|
||||||
run_period();
|
run_period();
|
||||||
wait_period();
|
if (run_enable) {
|
||||||
|
wait_period();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int make_periodic() {
|
int make_periodic() {
|
||||||
|
|
|
@ -134,6 +134,7 @@ class rrc_interface_mac : public rrc_interface_mac_common
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void release_pucch_srs() = 0;
|
virtual void release_pucch_srs() = 0;
|
||||||
|
virtual void run_tti(uint32_t tti) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// RRC interface for PHY
|
// RRC interface for PHY
|
||||||
|
@ -144,6 +145,7 @@ public:
|
||||||
virtual void out_of_sync() = 0;
|
virtual void out_of_sync() = 0;
|
||||||
virtual void earfcn_end() = 0;
|
virtual void earfcn_end() = 0;
|
||||||
virtual void cell_found(uint32_t earfcn, srslte_cell_t phy_cell, float rsrp) = 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
|
// RRC interface for NAS
|
||||||
|
@ -521,7 +523,7 @@ public:
|
||||||
bool enable_64qam;
|
bool enable_64qam;
|
||||||
} phy_cfg_t;
|
} 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 get_config(phy_cfg_t *phy_cfg) = 0;
|
||||||
virtual void set_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;
|
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_tdd(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd) = 0;
|
||||||
virtual void set_config_64qam_en(bool enable) = 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 */
|
/* Cell search and selection procedures */
|
||||||
virtual void cell_search_start() = 0;
|
virtual void cell_search_start() = 0;
|
||||||
virtual void cell_search_stop() = 0;
|
virtual void cell_search_stop() = 0;
|
||||||
|
|
|
@ -301,6 +301,7 @@ SRSLTE_API int srslte_str2mimotype(char *mimo_type_str,
|
||||||
|
|
||||||
SRSLTE_API char *srslte_mimotype2str(srslte_mimo_type_t mimo_type);
|
SRSLTE_API char *srslte_mimotype2str(srslte_mimo_type_t mimo_type);
|
||||||
|
|
||||||
|
/* Returns the interval tti1-tti2 mod 10240 */
|
||||||
SRSLTE_API uint32_t srslte_tti_interval(uint32_t tti1,
|
SRSLTE_API uint32_t srslte_tti_interval(uint32_t tti1,
|
||||||
uint32_t tti2);
|
uint32_t tti2);
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,9 @@ typedef struct {
|
||||||
SRSLTE_API int srslte_ringbuffer_init(srslte_ringbuffer_t *q,
|
SRSLTE_API int srslte_ringbuffer_init(srslte_ringbuffer_t *q,
|
||||||
int capacity);
|
int capacity);
|
||||||
|
|
||||||
SRSLTE_API void srslte_ringbuffer_free(srslte_ringbuffer_t *q,
|
SRSLTE_API void srslte_ringbuffer_free(srslte_ringbuffer_t *q);
|
||||||
int capacity);
|
|
||||||
|
SRSLTE_API void srslte_ringbuffer_reset(srslte_ringbuffer_t *q);
|
||||||
|
|
||||||
SRSLTE_API int srslte_ringbuffer_status(srslte_ringbuffer_t *q);
|
SRSLTE_API int srslte_ringbuffer_status(srslte_ringbuffer_t *q);
|
||||||
|
|
||||||
|
|
|
@ -57,9 +57,5 @@ set(srslte_srcs $<TARGET_OBJECTS:srslte_agc>
|
||||||
add_library(srslte_phy STATIC ${srslte_srcs})
|
add_library(srslte_phy STATIC ${srslte_srcs})
|
||||||
target_link_libraries(srslte_phy ${FFT_LIBRARIES})
|
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)
|
target_link_libraries(srslte_phy pthread m)
|
||||||
install(TARGETS srslte_phy DESTINATION ${LIBRARY_DIR})
|
install(TARGETS srslte_phy DESTINATION ${LIBRARY_DIR})
|
||||||
|
|
|
@ -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;
|
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
||||||
|
|
||||||
if (q != NULL &&
|
if (q != NULL &&
|
||||||
frame_size <= 307200 &&
|
|
||||||
fft_size_isvalid(fft_size))
|
fft_size_isvalid(fft_size))
|
||||||
{
|
{
|
||||||
ret = SRSLTE_ERROR;
|
ret = SRSLTE_ERROR;
|
||||||
|
|
|
@ -11,10 +11,9 @@ int srslte_ringbuffer_init(srslte_ringbuffer_t *q, int capacity)
|
||||||
if (!q->buffer) {
|
if (!q->buffer) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
q->capacity = capacity;
|
q->capacity = capacity;
|
||||||
q->count = 0;
|
srslte_ringbuffer_reset(q);
|
||||||
q->wpm = 0;
|
|
||||||
q->rpm = 0;
|
|
||||||
|
|
||||||
pthread_mutex_init(&q->mutex, NULL);
|
pthread_mutex_init(&q->mutex, NULL);
|
||||||
pthread_cond_init(&q->cvar, NULL);
|
pthread_cond_init(&q->cvar, NULL);
|
||||||
|
@ -22,7 +21,7 @@ int srslte_ringbuffer_init(srslte_ringbuffer_t *q, int capacity)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void srslte_ringbuffer_free(srslte_ringbuffer_t *q, int capacity)
|
void srslte_ringbuffer_free(srslte_ringbuffer_t *q)
|
||||||
{
|
{
|
||||||
if (q) {
|
if (q) {
|
||||||
if (q->buffer) {
|
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)
|
int srslte_ringbuffer_status(srslte_ringbuffer_t *q)
|
||||||
{
|
{
|
||||||
return q->count;
|
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;
|
uint8_t *ptr = (uint8_t*) p;
|
||||||
int w_bytes = nof_bytes;
|
int w_bytes = nof_bytes;
|
||||||
pthread_mutex_lock(&q->mutex);
|
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;
|
w_bytes = q->capacity - q->count;
|
||||||
fprintf(stderr, "Buffer overrun: lost %d bytes\n", nof_bytes - w_bytes);
|
fprintf(stderr, "Buffer overrun: lost %d bytes\n", nof_bytes - w_bytes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,8 @@ namespace srsue {
|
||||||
float avg_noise;
|
float avg_noise;
|
||||||
float avg_rsrp;
|
float avg_rsrp;
|
||||||
|
|
||||||
|
uint32_t serving_cell_report_period;
|
||||||
|
|
||||||
phch_common(uint32_t max_mutex = 3);
|
phch_common(uint32_t max_mutex = 3);
|
||||||
void init(phy_interface_rrc::phy_cfg_t *config,
|
void init(phy_interface_rrc::phy_cfg_t *config,
|
||||||
phy_args_t *args,
|
phy_args_t *args,
|
||||||
|
|
|
@ -27,10 +27,13 @@
|
||||||
#ifndef UEPHYRECV_H
|
#ifndef UEPHYRECV_H
|
||||||
#define UEPHYRECV_H
|
#define UEPHYRECV_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "srslte/srslte.h"
|
#include "srslte/srslte.h"
|
||||||
#include "srslte/common/log.h"
|
#include "srslte/common/log.h"
|
||||||
#include "srslte/common/threads.h"
|
#include "srslte/common/threads.h"
|
||||||
#include "srslte/common/thread_pool.h"
|
#include "srslte/common/thread_pool.h"
|
||||||
|
#include "srslte/common/tti_sync_cv.h"
|
||||||
#include "srslte/radio/radio_multi.h"
|
#include "srslte/radio/radio_multi.h"
|
||||||
#include "phy/prach.h"
|
#include "phy/prach.h"
|
||||||
#include "phy/phch_worker.h"
|
#include "phy/phch_worker.h"
|
||||||
|
@ -41,6 +44,7 @@ namespace srsue {
|
||||||
|
|
||||||
typedef _Complex float cf_t;
|
typedef _Complex float cf_t;
|
||||||
|
|
||||||
|
|
||||||
class phch_recv : public thread
|
class phch_recv : public thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -61,14 +65,17 @@ public:
|
||||||
void cell_search_next(bool reset = false);
|
void cell_search_next(bool reset = false);
|
||||||
bool cell_select(uint32_t earfcn, srslte_cell_t cell);
|
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();
|
uint32_t get_current_tti();
|
||||||
|
|
||||||
bool status_is_sync();
|
bool status_is_sync();
|
||||||
|
|
||||||
void set_time_adv_sec(float time_adv_sec);
|
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;
|
const static int MUTEX_X_WORKER = 4;
|
||||||
|
|
||||||
|
@ -125,13 +132,13 @@ private:
|
||||||
// Class to synchronize system frame number
|
// Class to synchronize system frame number
|
||||||
class sfn_sync {
|
class sfn_sync {
|
||||||
public:
|
public:
|
||||||
typedef enum {IDLE, SFN_FOUND, ERROR, TIMEOUT} ret_code;
|
typedef enum {IDLE, SFN_FOUND, SFX0_FOUND, ERROR, TIMEOUT} ret_code;
|
||||||
|
|
||||||
~sfn_sync();
|
~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 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();
|
void reset();
|
||||||
bool set_cell(srslte_cell_t cell);
|
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:
|
private:
|
||||||
srslte::log *log_h;
|
srslte::log *log_h;
|
||||||
|
@ -145,74 +152,106 @@ private:
|
||||||
|
|
||||||
// Class to perform cell measurements
|
// Class to perform cell measurements
|
||||||
class measure {
|
class measure {
|
||||||
|
|
||||||
|
// TODO: This class could early stop once the variance between the last N measurements is below 3GPP requirements
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef enum {IDLE, MEASURE_OK, ERROR} ret_code;
|
typedef enum {IDLE, MEASURE_OK, ERROR} ret_code;
|
||||||
|
|
||||||
~measure();
|
~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);
|
uint32_t nof_rx_antennas, uint32_t nof_subframes = RSRP_MEASURE_NOF_FRAMES);
|
||||||
void reset();
|
void reset();
|
||||||
void set_cell(srslte_cell_t cell);
|
void set_cell(srslte_cell_t cell);
|
||||||
ret_code run_subframe(uint32_t sf_idx);
|
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 rsrp();
|
||||||
float rsrq();
|
float rsrq();
|
||||||
float snr();
|
float snr();
|
||||||
float cfo();
|
|
||||||
private:
|
private:
|
||||||
srslte::log *log_h;
|
srslte::log *log_h;
|
||||||
srslte_ue_dl_t ue_dl;
|
srslte_ue_dl_t ue_dl;
|
||||||
srslte_ue_sync_t *ue_sync;
|
|
||||||
cf_t *buffer[SRSLTE_MAX_PORTS];
|
cf_t *buffer[SRSLTE_MAX_PORTS];
|
||||||
uint32_t cnt;
|
uint32_t cnt;
|
||||||
uint32_t nof_subframes;
|
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;
|
const static int RSRP_MEASURE_NOF_FRAMES = 5;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Class to receive secondary cell
|
// Class to receive secondary cell
|
||||||
class scell_recv : public thread {
|
class scell_recv {
|
||||||
public:
|
public:
|
||||||
void init(phch_recv *parent, srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t prio, int cpu_affinity = -1);
|
const static int MAX_CELLS = 8;
|
||||||
void stop();
|
typedef struct {
|
||||||
|
uint32_t pci;
|
||||||
|
float rsrp;
|
||||||
|
float rsrq;
|
||||||
|
uint32_t offset;
|
||||||
|
} cell_info_t;
|
||||||
|
void init(srslte::log *log_h);
|
||||||
void reset();
|
void reset();
|
||||||
int recv(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time);
|
int find_cells(cf_t *input_buffer, srslte_cell_t current_cell, uint32_t nof_sf, cell_info_t found_cells[MAX_CELLS]);
|
||||||
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);
|
|
||||||
private:
|
private:
|
||||||
void run_thread();
|
|
||||||
|
|
||||||
enum {
|
const static int DEFAULT_MEASUREMENT_LEN = 10;
|
||||||
IDLE = 0,
|
|
||||||
SCELL_SELECT,
|
|
||||||
SCELL_MEASURE,
|
|
||||||
SCELL_CAMPING
|
|
||||||
} scell_state;
|
|
||||||
|
|
||||||
srslte::log *log_h;
|
|
||||||
phch_recv *p;
|
|
||||||
bool running;
|
|
||||||
srslte_ringbuffer_t ring_buffer[SRSLTE_MAX_PORTS];
|
|
||||||
cf_t *sf_buffer[SRSLTE_MAX_PORTS];
|
cf_t *sf_buffer[SRSLTE_MAX_PORTS];
|
||||||
srslte_ue_sync_t ue_sync;
|
srslte::log *log_h;
|
||||||
srslte_cell_t cell;
|
srslte_sync_t sync_find;
|
||||||
uint32_t nof_rx_antennas;
|
|
||||||
uint32_t current_sflen;
|
|
||||||
|
|
||||||
measure measure_p;
|
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
|
// Objects for internal use
|
||||||
scell_recv scell;
|
|
||||||
measure measure_p;
|
measure measure_p;
|
||||||
search search_p;
|
search search_p;
|
||||||
sfn_sync sfn_p;
|
sfn_sync sfn_p;
|
||||||
|
intra_measure intra_freq_meas;
|
||||||
|
|
||||||
uint32_t current_sflen;
|
uint32_t current_sflen;
|
||||||
int next_offset;
|
int next_offset;
|
||||||
|
|
|
@ -78,7 +78,6 @@ public:
|
||||||
void set_earfcn(std::vector<uint32_t> earfcns);
|
void set_earfcn(std::vector<uint32_t> earfcns);
|
||||||
void force_freq(float dl_freq, float ul_freq);
|
void force_freq(float dl_freq, float ul_freq);
|
||||||
|
|
||||||
void scell_enable(bool enable);
|
|
||||||
|
|
||||||
/********** RRC INTERFACE ********************/
|
/********** RRC INTERFACE ********************/
|
||||||
void reset();
|
void reset();
|
||||||
|
@ -89,6 +88,10 @@ public:
|
||||||
void cell_search_next();
|
void cell_search_next();
|
||||||
bool cell_select(uint32_t earfcn, srslte_cell_t phy_cell);
|
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 ********************/
|
/********** MAC INTERFACE ********************/
|
||||||
/* Functions to synchronize with a cell */
|
/* Functions to synchronize with a cell */
|
||||||
bool sync_status(); // this is also RRC interface
|
bool sync_status(); // this is also RRC interface
|
||||||
|
@ -133,7 +136,7 @@ public:
|
||||||
float get_pathloss_db();
|
float get_pathloss_db();
|
||||||
|
|
||||||
uint32_t get_current_tti();
|
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();
|
void start_plot();
|
||||||
|
|
||||||
|
|
|
@ -80,8 +80,6 @@ public:
|
||||||
|
|
||||||
void pregenerate_signals(bool enable);
|
void pregenerate_signals(bool enable);
|
||||||
|
|
||||||
void test_scell();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~ue();
|
virtual ~ue();
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "srslte/common/threads.h"
|
#include "srslte/common/threads.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
using srslte::byte_buffer_t;
|
using srslte::byte_buffer_t;
|
||||||
|
|
||||||
|
@ -80,6 +81,46 @@ public:
|
||||||
|
|
||||||
void liblte_rrc_log(char *str);
|
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:
|
private:
|
||||||
srslte::byte_buffer_pool *pool;
|
srslte::byte_buffer_pool *pool;
|
||||||
srslte::log *rrc_log;
|
srslte::log *rrc_log;
|
||||||
|
@ -90,7 +131,9 @@ private:
|
||||||
nas_interface_rrc *nas;
|
nas_interface_rrc *nas;
|
||||||
usim_interface_rrc *usim;
|
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;
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
|
@ -166,42 +209,6 @@ private:
|
||||||
bool thread_running;
|
bool thread_running;
|
||||||
void run_thread();
|
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
|
// Radio bearers
|
||||||
typedef enum{
|
typedef enum{
|
||||||
RB_ID_SRB0 = 0,
|
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
|
// RLC interface
|
||||||
void max_retx_attempted();
|
void max_retx_attempted();
|
||||||
|
|
||||||
|
|
|
@ -180,6 +180,8 @@ void mac::run_thread() {
|
||||||
ra_procedure.start_mac_order();
|
ra_procedure.start_mac_order();
|
||||||
}
|
}
|
||||||
ra_procedure.step(tti);
|
ra_procedure.step(tti);
|
||||||
|
|
||||||
|
rrc_h->run_tti(tti);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -398,7 +398,6 @@ int main(int argc, char *argv[])
|
||||||
pthread_t input;
|
pthread_t input;
|
||||||
pthread_create(&input, NULL, &input_loop, &args);
|
pthread_create(&input, NULL, &input_loop, &args);
|
||||||
|
|
||||||
bool scell_done = false;
|
|
||||||
bool plot_started = false;
|
bool plot_started = false;
|
||||||
bool signals_pregenerated = false;
|
bool signals_pregenerated = false;
|
||||||
|
|
||||||
|
@ -408,10 +407,6 @@ int main(int argc, char *argv[])
|
||||||
ue->pregenerate_signals(true);
|
ue->pregenerate_signals(true);
|
||||||
signals_pregenerated = true;
|
signals_pregenerated = true;
|
||||||
}
|
}
|
||||||
if (!scell_done) {
|
|
||||||
((srsue::ue*) ue)->test_scell();
|
|
||||||
scell_done = true;
|
|
||||||
}
|
|
||||||
if (!plot_started && args.gui.enable) {
|
if (!plot_started && args.gui.enable) {
|
||||||
ue->start_plot();
|
ue->start_plot();
|
||||||
plot_started = true;
|
plot_started = true;
|
||||||
|
|
|
@ -59,6 +59,9 @@ phch_common::phch_common(uint32_t max_mutex_) : tx_mutex(max_mutex_)
|
||||||
rx_gain_offset = 0;
|
rx_gain_offset = 0;
|
||||||
sr_last_tx_tti = -1;
|
sr_last_tx_tti = -1;
|
||||||
cur_pusch_power = 0;
|
cur_pusch_power = 0;
|
||||||
|
|
||||||
|
serving_cell_report_period = 20;
|
||||||
|
|
||||||
bzero(zeros, 50000*sizeof(cf_t));
|
bzero(zeros, 50000*sizeof(cf_t));
|
||||||
|
|
||||||
// FIXME: This is an ungly fix to avoid the TX filters to empty
|
// FIXME: This is an ungly fix to avoid the TX filters to empty
|
||||||
|
|
|
@ -25,11 +25,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <srslte/srslte.h>
|
#include <algorithm>
|
||||||
#include "srslte/srslte.h"
|
#include "srslte/srslte.h"
|
||||||
#include "srslte/common/log.h"
|
#include "srslte/common/log.h"
|
||||||
#include "phy/phch_worker.h"
|
#include "phy/phch_worker.h"
|
||||||
#include "phy/phch_common.h"
|
|
||||||
#include "phy/phch_recv.h"
|
#include "phy/phch_recv.h"
|
||||||
|
|
||||||
#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||||
|
@ -41,11 +40,6 @@ namespace srsue {
|
||||||
|
|
||||||
int radio_recv_callback(void *obj, cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time) {
|
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);
|
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) {
|
double callback_set_rx_gain(void *h, double 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);
|
sfn_p.init(&ue_sync, sf_buffer, log_h);
|
||||||
|
|
||||||
// Initialize measurement class for the primary cell
|
// 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
|
// Start intra-frequency measurement
|
||||||
scell.init(this, log_h, nof_rx_antennas_, prio-1, sync_cpu_affinity);
|
intra_freq_meas.init(worker_com, rrc, log_h);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
|
@ -120,7 +114,7 @@ phch_recv::~phch_recv() {
|
||||||
|
|
||||||
void phch_recv::stop()
|
void phch_recv::stop()
|
||||||
{
|
{
|
||||||
|
intra_freq_meas.stop();
|
||||||
running = false;
|
running = false;
|
||||||
wait_thread_finish();
|
wait_thread_finish();
|
||||||
}
|
}
|
||||||
|
@ -223,6 +217,7 @@ bool phch_recv::set_cell() {
|
||||||
measure_p.set_cell(cell);
|
measure_p.set_cell(cell);
|
||||||
sfn_p.set_cell(cell);
|
sfn_p.set_cell(cell);
|
||||||
worker_com->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++) {
|
for (uint32_t i = 0; i < workers_pool->get_nof_workers(); i++) {
|
||||||
if (!((phch_worker *) workers_pool->get_worker(i))->set_cell(cell)) {
|
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;
|
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_) {
|
if (cell_) {
|
||||||
memcpy(cell_, &cell, sizeof(srslte_cell_t));
|
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)
|
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;
|
next_offset = nsamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset <= 0) {
|
|
||||||
scell.write(data, nsamples, rx_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
log_h->debug("SYNC: received %d samples from radio\n", nsamples);
|
log_h->debug("SYNC: received %d samples from radio\n", nsamples);
|
||||||
|
|
||||||
return 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;
|
break;
|
||||||
case CELL_MEASURE:
|
case CELL_MEASURE:
|
||||||
switch(measure_p.run_subframe(sf_idx))
|
switch(measure_p.run_subframe_sync(&ue_sync, sf_idx))
|
||||||
{
|
{
|
||||||
case measure::MEASURE_OK:
|
case measure::MEASURE_OK:
|
||||||
log_h->info("SYNC: Measured OK. Camping on cell PCI=%d...\n", cell.id);
|
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);
|
worker_com->cur_radio_power = SRSLTE_MIN(SRSLTE_PC_MAX, worker_com->pathloss+worker_com->p0_preamble);
|
||||||
}
|
}
|
||||||
workers_pool->start_worker(worker);
|
workers_pool->start_worker(worker);
|
||||||
|
|
||||||
|
intra_freq_meas.write(tti, buffer[0], SRSLTE_SF_LEN_PRB(cell.nof_prb));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
log_h->error("SYNC: Sync error. Sending out-of-sync to RRC\n");
|
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;
|
return CELL_NOT_FOUND;
|
||||||
}
|
}
|
||||||
// Save result
|
// Save result
|
||||||
cell->id = found_cells[max_peak_cell].cell_id;
|
cell->id = found_cells[max_peak_cell].cell_id;
|
||||||
cell->cp = found_cells[max_peak_cell].cp;
|
cell->cp = found_cells[max_peak_cell].cp;
|
||||||
float cfo = found_cells[max_peak_cell].cfo;
|
float cfo = found_cells[max_peak_cell].cfo;
|
||||||
|
|
||||||
printf("\n");
|
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];
|
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");
|
Error("SYNC: Initiating UE MIB decoder\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -885,7 +869,7 @@ void phch_recv::sfn_sync::reset()
|
||||||
cnt = 0;
|
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];
|
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 (ret == 1) {
|
||||||
if (srslte_ue_sync_get_sfidx(ue_sync) == 0) {
|
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;
|
int sfn_offset = 0;
|
||||||
Info("SYNC: Trying to decode MIB... SNR=%.1f dB\n", 10*log10(srslte_chest_dl_get_snr(&ue_mib.chest)));
|
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) {
|
if (n < 0) {
|
||||||
Error("SYNC: Error decoding MIB while synchronising SFN");
|
Error("SYNC: Error decoding MIB while synchronising SFN");
|
||||||
return ERROR;
|
return ERROR;
|
||||||
|
@ -944,16 +937,15 @@ phch_recv::sfn_sync::ret_code phch_recv::sfn_sync::run_subframe(srslte_cell_t *c
|
||||||
/*********
|
/*********
|
||||||
* Measurement class
|
* 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->log_h = log_h;
|
||||||
this->nof_subframes = nof_subrames;
|
this->nof_subframes = nof_subframes;
|
||||||
this->ue_sync = ue_sync;
|
|
||||||
for (int i=0;i<SRSLTE_MAX_PORTS;i++) {
|
for (int i=0;i<SRSLTE_MAX_PORTS;i++) {
|
||||||
this->buffer[i] = buffer[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");
|
Error("SYNC: Initiating ue_dl_measure\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -969,11 +961,11 @@ void phch_recv::measure::reset() {
|
||||||
mean_rsrp = 0;
|
mean_rsrp = 0;
|
||||||
mean_rsrq = 0;
|
mean_rsrq = 0;
|
||||||
mean_snr = 0;
|
mean_snr = 0;
|
||||||
mean_cfo = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void phch_recv::measure::set_cell(srslte_cell_t cell)
|
void phch_recv::measure::set_cell(srslte_cell_t cell)
|
||||||
{
|
{
|
||||||
|
current_prb = cell.nof_prb;
|
||||||
if (srslte_ue_dl_set_cell(&ue_dl, cell)) {
|
if (srslte_ue_dl_set_cell(&ue_dl, cell)) {
|
||||||
Error("SYNC: Setting cell: initiating ue_dl_measure\n");
|
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() {
|
float phch_recv::measure::rsrp() {
|
||||||
return 10*log10(mean_rsrp/1000);
|
return mean_rsrp;
|
||||||
}
|
}
|
||||||
|
|
||||||
float phch_recv::measure::rsrq() {
|
float phch_recv::measure::rsrq() {
|
||||||
return 10*log10(mean_rsrq);
|
return mean_rsrq;
|
||||||
}
|
}
|
||||||
|
|
||||||
float phch_recv::measure::snr() {
|
float phch_recv::measure::snr() {
|
||||||
return 10*log10(mean_snr);
|
return mean_snr;
|
||||||
}
|
}
|
||||||
|
|
||||||
float phch_recv::measure::cfo() {
|
phch_recv::measure::ret_code phch_recv::measure::run_subframe_sync(srslte_ue_sync_t *ue_sync, uint32_t sf_idx)
|
||||||
return mean_cfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
phch_recv::measure::ret_code phch_recv::measure::run_subframe(uint32_t sf_idx)
|
|
||||||
{
|
{
|
||||||
int sync_res = srslte_ue_sync_zerocopy_multi(ue_sync, buffer);
|
int sync_res = srslte_ue_sync_zerocopy_multi(ue_sync, buffer);
|
||||||
if (sync_res == 1) {
|
if (sync_res == 1) {
|
||||||
uint32_t cfi = 0;
|
return run_subframe(sf_idx);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log_h->error("SYNC: Measuring RSRP: Sync error\n");
|
log_h->error("SYNC: Measuring RSRP: Sync error\n");
|
||||||
return ERROR;
|
return ERROR;
|
||||||
|
@ -1033,7 +997,51 @@ phch_recv::measure::ret_code phch_recv::measure::run_subframe(uint32_t sf_idx)
|
||||||
return IDLE;
|
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
|
* 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->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
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
srslte_sync_cp_en(&sync_find, false);
|
||||||
for (uint32_t i = 0; i < nof_rx_antennas; i++) {
|
srslte_sync_set_cfo_ema_alpha(&sync_find, 0.8);
|
||||||
sf_buffer[i] = (cf_t *) srslte_vec_malloc(sizeof(cf_t) * 3 * SRSLTE_SF_LEN_PRB(100));
|
srslte_sync_set_threshold(&sync_find, 2.0);
|
||||||
}
|
|
||||||
|
|
||||||
measure_p.init(&ue_sync, sf_buffer, log_h, nof_rx_antennas);
|
|
||||||
sfn_p.init(&ue_sync, sf_buffer, log_h);
|
|
||||||
|
|
||||||
reset();
|
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()
|
void phch_recv::scell_recv::reset()
|
||||||
{
|
{
|
||||||
tti = 0;
|
|
||||||
measure_p.reset();
|
measure_p.reset();
|
||||||
sfn_p.reset();
|
|
||||||
scell_state = IDLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void phch_recv::scell_recv::set_cell(srslte_cell_t scell) {
|
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])
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
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, ¤t_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 {
|
} 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;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void phch_recv::scell_recv::write(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time)
|
int phch_recv::meas_stop(uint32_t earfcn, int pci) {
|
||||||
{
|
if (earfcn == current_earfcn) {
|
||||||
if (is_enabled()) {
|
intra_freq_meas.rem_cell(pci);
|
||||||
for (uint32_t i = 0; i < nof_rx_antennas; i++) {
|
return 0;
|
||||||
srslte_ringbuffer_write(&ring_buffer[i], data[i], sizeof(cf_t) * nsamples);
|
} 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) {
|
while(running) {
|
||||||
switch(scell_state) {
|
if (running) {
|
||||||
case IDLE:
|
tti_sync.wait();
|
||||||
usleep(1000);
|
}
|
||||||
break;
|
|
||||||
case SCELL_SELECT:
|
if (running) {
|
||||||
|
Info("INTRA: Running intra-frequency measurement for %d cells\n", active_pci.size());
|
||||||
switch (sfn_p.run_subframe(&cell, &tti))
|
|
||||||
{
|
// Read 5 ms data from buffer
|
||||||
case sfn_sync::SFN_FOUND:
|
srslte_ringbuffer_read(&ring_buffer, search_buffer, CAPTURE_LEN_SF*current_sflen*sizeof(cf_t));
|
||||||
log_h->info("SCELL: SFN Sync OK. Camping on cell PCI=%d...\n", cell.id);
|
int found_cells = scell.find_cells(search_buffer, primary_cell, CAPTURE_LEN_SF, info);
|
||||||
sfn_p.reset();
|
receiving = false;
|
||||||
scell_state = SCELL_MEASURE;
|
srslte_ringbuffer_reset(&ring_buffer);
|
||||||
break;
|
|
||||||
case sfn_sync::TIMEOUT:
|
for (int i=0;i<found_cells;i++) {
|
||||||
log_h->info("SCELL: SFN sync timeout\n");
|
info[i].rsrp -= common->rx_gain_offset;
|
||||||
break;
|
rrc->new_phy_meas(info[i].rsrp, info[i].rsrq, measure_tti, current_earfcn, info[i].pci);
|
||||||
case sfn_sync::IDLE:
|
}
|
||||||
break;
|
// Look for other cells not found automatically
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
// Increase TTI counter
|
|
||||||
tti = (tti+1) % 10240;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1210,8 +1210,8 @@ void phch_worker::update_measurements()
|
||||||
{
|
{
|
||||||
float snr_ema_coeff = phy->args->snr_ema_coeff;
|
float snr_ema_coeff = phy->args->snr_ema_coeff;
|
||||||
if (chest_done) {
|
if (chest_done) {
|
||||||
/* Compute ADC/RX gain offset every 20 ms */
|
/* Compute ADC/RX gain offset every ~10s */
|
||||||
if ((tti%20) == 0 || phy->rx_gain_offset == 0) {
|
if (tti== 0 || phy->rx_gain_offset == 0) {
|
||||||
float rx_gain_offset = 0;
|
float rx_gain_offset = 0;
|
||||||
if (phy->get_radio()->has_rssi() && phy->args->rssi_sensor_enabled) {
|
if (phy->get_radio()->has_rssi() && phy->args->rssi_sensor_enabled) {
|
||||||
float rssi_all_signal = srslte_chest_dl_get_rssi(&ue_dl.chest);
|
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();
|
rx_gain_offset = phy->get_radio()->get_rx_gain();
|
||||||
}
|
}
|
||||||
if (phy->rx_gain_offset) {
|
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 {
|
} else {
|
||||||
phy->rx_gain_offset = rx_gain_offset;
|
phy->rx_gain_offset = rx_gain_offset;
|
||||||
}
|
}
|
||||||
|
@ -1246,14 +1246,15 @@ void phch_worker::update_measurements()
|
||||||
float rsrp = 10*log10(srslte_chest_dl_get_rsrp(&ue_dl.chest)) + 30 - phy->rx_gain_offset;
|
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;
|
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 (isnormal(rsrp)) {
|
||||||
if (!phy->avg_rsrp_db) {
|
if (!phy->avg_rsrp_db) {
|
||||||
phy->avg_rsrp_db = rsrp;
|
phy->avg_rsrp_db = rsrp;
|
||||||
} else {
|
} else {
|
||||||
uint32_t k = 4; // Set by RRC reconfiguration message
|
phy->avg_rsrp_db = SRSLTE_VEC_EMA(phy->avg_rsrp_db, rsrp, 0.8);
|
||||||
float coeff = pow(0.5,(float) k/4);
|
}
|
||||||
phy->avg_rsrp_db = SRSLTE_VEC_EMA(phy->avg_rsrp_db, rsrp, coeff);
|
if ((tti%phy->serving_cell_report_period) == 0) {
|
||||||
|
phy->rrc->new_phy_meas(phy->avg_rsrp_db, phy->avg_rsrq_db, tti);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Compute PL
|
// Compute PL
|
||||||
|
|
|
@ -243,15 +243,22 @@ void phy::cell_search_next()
|
||||||
sf_recv.cell_search_next();
|
sf_recv.cell_search_next();
|
||||||
}
|
}
|
||||||
|
|
||||||
void phy::scell_enable(bool enable)
|
|
||||||
{
|
|
||||||
sf_recv.scell_enable(enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
void phy::sync_reset() {
|
void phy::sync_reset() {
|
||||||
sf_recv.reset_sync();
|
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)
|
bool phy::cell_select(uint32_t earfcn, srslte_cell_t phy_cell)
|
||||||
{
|
{
|
||||||
return sf_recv.cell_select(earfcn, 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);
|
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)
|
void phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm)
|
||||||
|
|
|
@ -215,11 +215,6 @@ void ue::pregenerate_signals(bool enable)
|
||||||
phy.enable_pregen_signals(enable);
|
phy.enable_pregen_signals(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ue::test_scell()
|
|
||||||
{
|
|
||||||
phy.scell_enable(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
void ue::stop()
|
void ue::stop()
|
||||||
{
|
{
|
||||||
if(started)
|
if(started)
|
||||||
|
|
|
@ -109,6 +109,8 @@ void rrc::init(phy_interface_rrc *phy_,
|
||||||
set_rrc_default();
|
set_rrc_default();
|
||||||
set_phy_default();
|
set_phy_default();
|
||||||
set_mac_default();
|
set_mac_default();
|
||||||
|
|
||||||
|
measurements.init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rrc::stop() {
|
void rrc::stop() {
|
||||||
|
@ -116,6 +118,10 @@ void rrc::stop() {
|
||||||
wait_thread_finish();
|
wait_thread_finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rrc::run_tti(uint32_t tti) {
|
||||||
|
measurements.run_tti(tti);
|
||||||
|
}
|
||||||
|
|
||||||
rrc_state_t rrc::get_state() {
|
rrc_state_t rrc::get_state() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -418,6 +424,10 @@ void rrc::select_next_cell_in_plmn() {
|
||||||
rrc_log->info("No more known cells...\n");
|
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) {
|
void rrc::cell_found(uint32_t earfcn, srslte_cell_t phy_cell, float rsrp) {
|
||||||
|
|
||||||
// find if cell_id-earfcn combination already exists
|
// 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) {
|
} else if (timeout_id == t301) {
|
||||||
rrc_log->info("Timer T301 expired: Going to RRC IDLE\n");
|
rrc_log->info("Timer T301 expired: Going to RRC IDLE\n");
|
||||||
state = RRC_STATE_LEAVE_CONNECTED;
|
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);
|
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() {
|
void rrc::send_con_restablish_complete() {
|
||||||
rrc_log->debug("Preparing RRC Connection Reestablishment Complete\n");
|
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
|
// Prepare ConnectionSetupComplete packet
|
||||||
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_REEST_COMPLETE;
|
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;
|
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
|
send_ul_dcch_msg();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) {
|
void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) {
|
||||||
rrc_log->debug("Preparing RRC Connection Setup Complete\n");
|
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
|
// Prepare ConnectionSetupComplete packet
|
||||||
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_SETUP_COMPLETE;
|
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;
|
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);
|
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;
|
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
|
send_ul_dcch_msg();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rrc::send_ul_info_transfer(uint32_t lcid, byte_buffer_t *sdu) {
|
void rrc::send_ul_info_transfer(uint32_t lcid, byte_buffer_t *sdu) {
|
||||||
rrc_log->debug("Preparing RX Info Transfer\n");
|
rrc_log->debug("Preparing RX Info Transfer\n");
|
||||||
LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg;
|
|
||||||
|
|
||||||
// Prepare RX INFO packet
|
// Prepare RX INFO packet
|
||||||
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER;
|
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;
|
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);
|
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;
|
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
|
send_ul_dcch_msg(sdu);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rrc::send_security_mode_complete(uint32_t lcid, byte_buffer_t *pdu) {
|
void rrc::send_security_mode_complete(uint32_t lcid, byte_buffer_t *pdu) {
|
||||||
rrc_log->debug("Preparing Security Mode Complete\n");
|
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_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE;
|
||||||
ul_dcch_msg.msg.security_mode_complete.rrc_transaction_id = transaction_id;
|
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
|
send_ul_dcch_msg(pdu);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rrc::send_rrc_con_reconfig_complete(uint32_t lcid, byte_buffer_t *pdu) {
|
void rrc::send_rrc_con_reconfig_complete(uint32_t lcid, byte_buffer_t *pdu) {
|
||||||
rrc_log->debug("Preparing RRC Connection Reconfig Complete\n");
|
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_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE;
|
||||||
ul_dcch_msg.msg.rrc_con_reconfig_complete.rrc_transaction_id = transaction_id;
|
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
|
send_ul_dcch_msg();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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");
|
printf("received con reconfig no rr confg present\n");
|
||||||
}
|
}
|
||||||
if (reconfig->meas_cnfg_present) {
|
if (reconfig->meas_cnfg_present) {
|
||||||
//TODO: handle meas_cnfg
|
measurements.parse_meas_config(&reconfig->meas_cnfg);
|
||||||
}
|
}
|
||||||
if (reconfig->mob_ctrl_info_present) {
|
if (reconfig->mob_ctrl_info_present) {
|
||||||
//TODO: handle mob_ctrl_info
|
//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;
|
byte_buffer_t *nas_sdu;
|
||||||
for (i = 0; i < reconfig->N_ded_info_nas; i++) {
|
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);
|
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_sdu->N_bytes = reconfig->ded_info_nas_list[i].N_bytes;
|
||||||
nas->write_pdu(lcid, nas_sdu);
|
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) {
|
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());
|
rrc_log->info_hex(sdu->msg, sdu->N_bytes, "RX %s SDU", get_rb_name(lcid).c_str());
|
||||||
switch (state) {
|
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(¤t_cell, ¤t_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
|
} // namespace srsue
|
||||||
|
|
Loading…
Reference in New Issue