diff --git a/CMakeLists.txt b/CMakeLists.txt index efaa1973a..73b38ceb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,6 @@ set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") option(ENABLE_SRSUE "Build srsUE application" ON) option(ENABLE_SRSENB "Build srsENB application" ON) -option(ENABLE_VOLK "Enable use of VOLK SIMD library" OFF) option(ENABLE_GUI "Enable GUI (using srsGUI)" ON) option(ENABLE_BLADERF "Enable BladeRF" ON) @@ -189,21 +188,6 @@ if(ENABLE_GUI) endif(SRSGUI_FOUND) endif(ENABLE_GUI) -# VOLK -include(CheckFunctionExistsMath) -if(ENABLE_VOLK) - find_package(Volk) - if(VOLK_FOUND) - include_directories(${VOLK_INCLUDE_DIRS}) - link_directories(${VOLK_LIBRARY_DIRS}) - message(STATUS "Compiling with VOLK SIMD library.") - else(VOLK_FOUND) - message(STATUS "VOLK SIMD library NOT found. Using generic implementation.") - endif(VOLK_FOUND) -else(ENABLE_VOLK) - message(STATUS "VOLK library disabled") -endif(ENABLE_VOLK) - ######################################################################## # Install Dirs ######################################################################## diff --git a/lib/include/srslte/asn1/liblte_rrc.h b/lib/include/srslte/asn1/liblte_rrc.h index c5c3b60e2..46f725ec3 100644 --- a/lib/include/srslte/asn1/liblte_rrc.h +++ b/lib/include/srslte/asn1/liblte_rrc.h @@ -1218,6 +1218,9 @@ typedef enum{ }LIBLTE_RRC_REPORT_AMOUNT_ENUM; static const char liblte_rrc_report_amount_text[LIBLTE_RRC_REPORT_AMOUNT_N_ITEMS][20] = { "r1", "r2", "r4", "r8", "r16", "r32", "r64", "INFINITY"}; + +static const int8 liblte_rrc_report_amount_num[LIBLTE_RRC_REPORT_AMOUNT_N_ITEMS] = {1, 2, 4, 8, 16, 32, 64, -1}; + typedef enum{ LIBLTE_RRC_THRESHOLD_UTRA_TYPE_RSCP = 0, LIBLTE_RRC_THRESHOLD_UTRA_TYPE_ECNO, diff --git a/lib/include/srslte/common/threads.h b/lib/include/srslte/common/threads.h index a8807ba9b..077dda72b 100644 --- a/lib/include/srslte/common/threads.h +++ b/lib/include/srslte/common/threads.h @@ -78,22 +78,30 @@ class periodic_thread : public thread { public: void start_periodic(int period_us_, int priority = -1) { + run_enable = true; period_us = period_us_; start(priority); } + void stop() { + run_enable = false; + wait_thread_finish(); + } protected: virtual void run_period() = 0; private: int wakeups_missed; int timer_fd; - int period_us; + int period_us; + bool run_enable; void run_thread() { if (make_periodic()) { return; } - while(1) { + while(run_enable) { run_period(); - wait_period(); + if (run_enable) { + wait_period(); + } } } int make_periodic() { diff --git a/lib/include/srslte/interfaces/ue_interfaces.h b/lib/include/srslte/interfaces/ue_interfaces.h index 88dfd285c..a4ecf1345 100644 --- a/lib/include/srslte/interfaces/ue_interfaces.h +++ b/lib/include/srslte/interfaces/ue_interfaces.h @@ -134,6 +134,7 @@ class rrc_interface_mac : public rrc_interface_mac_common { public: virtual void release_pucch_srs() = 0; + virtual void run_tti(uint32_t tti) = 0; }; // RRC interface for PHY @@ -144,6 +145,7 @@ public: virtual void out_of_sync() = 0; virtual void earfcn_end() = 0; virtual void cell_found(uint32_t earfcn, srslte_cell_t phy_cell, float rsrp) = 0; + virtual void new_phy_meas(float rsrp, float rsrq, uint32_t tti, uint32_t earfcn = 0, uint32_t pci = 0) = 0; }; // RRC interface for NAS @@ -521,7 +523,7 @@ public: bool enable_64qam; } phy_cfg_t; - virtual void get_current_cell(srslte_cell_t *cell) = 0; + virtual void get_current_cell(srslte_cell_t *cell, uint32_t *current_earfcn = NULL) = 0; virtual void get_config(phy_cfg_t *phy_cfg) = 0; virtual void set_config(phy_cfg_t *phy_cfg) = 0; virtual void set_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *dedicated) = 0; @@ -529,6 +531,11 @@ public: virtual void set_config_tdd(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd) = 0; virtual void set_config_64qam_en(bool enable) = 0; + /* Measurements interface */ + virtual void meas_reset() = 0; + virtual int meas_start(uint32_t earfcn, int pci = -1) = 0; + virtual int meas_stop(uint32_t earfcn, int pci = -1) = 0; + /* Cell search and selection procedures */ virtual void cell_search_start() = 0; virtual void cell_search_stop() = 0; diff --git a/lib/include/srslte/phy/common/phy_common.h b/lib/include/srslte/phy/common/phy_common.h index 4a1eeb27d..c55713cfc 100644 --- a/lib/include/srslte/phy/common/phy_common.h +++ b/lib/include/srslte/phy/common/phy_common.h @@ -301,7 +301,8 @@ SRSLTE_API int srslte_str2mimotype(char *mimo_type_str, SRSLTE_API char *srslte_mimotype2str(srslte_mimo_type_t mimo_type); -SRSLTE_API uint32_t srslte_tti_interval(uint32_t tti1, +/* Returns the interval tti1-tti2 mod 10240 */ +SRSLTE_API uint32_t srslte_tti_interval(uint32_t tti1, uint32_t tti2); #endif diff --git a/lib/include/srslte/phy/utils/ringbuffer.h b/lib/include/srslte/phy/utils/ringbuffer.h index acd2ae5e6..e590131ff 100644 --- a/lib/include/srslte/phy/utils/ringbuffer.h +++ b/lib/include/srslte/phy/utils/ringbuffer.h @@ -20,8 +20,9 @@ typedef struct { SRSLTE_API int srslte_ringbuffer_init(srslte_ringbuffer_t *q, int capacity); -SRSLTE_API void srslte_ringbuffer_free(srslte_ringbuffer_t *q, - int capacity); +SRSLTE_API void srslte_ringbuffer_free(srslte_ringbuffer_t *q); + +SRSLTE_API void srslte_ringbuffer_reset(srslte_ringbuffer_t *q); SRSLTE_API int srslte_ringbuffer_status(srslte_ringbuffer_t *q); diff --git a/lib/src/phy/CMakeLists.txt b/lib/src/phy/CMakeLists.txt index 572ff19c3..b52af7b1d 100644 --- a/lib/src/phy/CMakeLists.txt +++ b/lib/src/phy/CMakeLists.txt @@ -57,9 +57,5 @@ set(srslte_srcs $ add_library(srslte_phy STATIC ${srslte_srcs}) target_link_libraries(srslte_phy ${FFT_LIBRARIES}) -if(VOLK_FOUND) - target_link_libraries(srslte_phy ${VOLK_LIBRARIES}) -endif(VOLK_FOUND) - target_link_libraries(srslte_phy pthread m) install(TARGETS srslte_phy DESTINATION ${LIBRARY_DIR}) diff --git a/lib/src/phy/sync/sync.c b/lib/src/phy/sync/sync.c index ee2ba76d1..0fb1b819d 100644 --- a/lib/src/phy/sync/sync.c +++ b/lib/src/phy/sync/sync.c @@ -62,7 +62,6 @@ int srslte_sync_init_decim(srslte_sync_t *q, uint32_t frame_size, uint32_t max_o int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q != NULL && - frame_size <= 307200 && fft_size_isvalid(fft_size)) { ret = SRSLTE_ERROR; diff --git a/lib/src/phy/utils/ringbuffer.c b/lib/src/phy/utils/ringbuffer.c index f7a10e2c5..400f80360 100644 --- a/lib/src/phy/utils/ringbuffer.c +++ b/lib/src/phy/utils/ringbuffer.c @@ -11,10 +11,9 @@ int srslte_ringbuffer_init(srslte_ringbuffer_t *q, int capacity) if (!q->buffer) { return -1; } - q->capacity = capacity; - q->count = 0; - q->wpm = 0; - q->rpm = 0; + + q->capacity = capacity; + srslte_ringbuffer_reset(q); pthread_mutex_init(&q->mutex, NULL); pthread_cond_init(&q->cvar, NULL); @@ -22,7 +21,7 @@ int srslte_ringbuffer_init(srslte_ringbuffer_t *q, int capacity) return 0; } -void srslte_ringbuffer_free(srslte_ringbuffer_t *q, int capacity) +void srslte_ringbuffer_free(srslte_ringbuffer_t *q) { if (q) { if (q->buffer) { @@ -34,6 +33,15 @@ void srslte_ringbuffer_free(srslte_ringbuffer_t *q, int capacity) } } +void srslte_ringbuffer_reset(srslte_ringbuffer_t *q) +{ + pthread_mutex_lock(&q->mutex); + q->count = 0; + q->wpm = 0; + q->rpm = 0; + pthread_mutex_unlock(&q->mutex); +} + int srslte_ringbuffer_status(srslte_ringbuffer_t *q) { return q->count; @@ -44,7 +52,7 @@ int srslte_ringbuffer_write(srslte_ringbuffer_t *q, void *p, int nof_bytes) uint8_t *ptr = (uint8_t*) p; int w_bytes = nof_bytes; pthread_mutex_lock(&q->mutex); - if (q->count + w_bytes >= q->capacity) { + if (q->count + w_bytes > q->capacity) { w_bytes = q->capacity - q->count; fprintf(stderr, "Buffer overrun: lost %d bytes\n", nof_bytes - w_bytes); } diff --git a/srsue/hdr/phy/phch_common.h b/srsue/hdr/phy/phch_common.h index 39e9e9685..f3c36d994 100644 --- a/srsue/hdr/phy/phch_common.h +++ b/srsue/hdr/phy/phch_common.h @@ -65,7 +65,9 @@ namespace srsue { float avg_snr_db; float avg_noise; float avg_rsrp; - + + uint32_t serving_cell_report_period; + phch_common(uint32_t max_mutex = 3); void init(phy_interface_rrc::phy_cfg_t *config, phy_args_t *args, diff --git a/srsue/hdr/phy/phch_recv.h b/srsue/hdr/phy/phch_recv.h index bb23bb5b5..e35610c0b 100644 --- a/srsue/hdr/phy/phch_recv.h +++ b/srsue/hdr/phy/phch_recv.h @@ -27,10 +27,13 @@ #ifndef UEPHYRECV_H #define UEPHYRECV_H +#include + #include "srslte/srslte.h" #include "srslte/common/log.h" #include "srslte/common/threads.h" #include "srslte/common/thread_pool.h" +#include "srslte/common/tti_sync_cv.h" #include "srslte/radio/radio_multi.h" #include "phy/prach.h" #include "phy/phch_worker.h" @@ -39,7 +42,8 @@ namespace srsue { -typedef _Complex float cf_t; +typedef _Complex float cf_t; + class phch_recv : public thread { @@ -61,14 +65,17 @@ public: void cell_search_next(bool reset = false); bool cell_select(uint32_t earfcn, srslte_cell_t cell); + void meas_reset(); + int meas_start(uint32_t earfcn, int pci); + int meas_stop(uint32_t earfcn, int pci); + uint32_t get_current_tti(); bool status_is_sync(); void set_time_adv_sec(float time_adv_sec); - void get_current_cell(srslte_cell_t *cell); + void get_current_cell(srslte_cell_t *cell, uint32_t *earfcn = NULL); - void scell_enable(bool enable); const static int MUTEX_X_WORKER = 4; @@ -125,13 +132,13 @@ private: // Class to synchronize system frame number class sfn_sync { public: - typedef enum {IDLE, SFN_FOUND, ERROR, TIMEOUT} ret_code; + typedef enum {IDLE, SFN_FOUND, SFX0_FOUND, ERROR, TIMEOUT} ret_code; ~sfn_sync(); void init(srslte_ue_sync_t *ue_sync, cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t timeout = SYNC_SFN_TIMEOUT); void reset(); bool set_cell(srslte_cell_t cell); - ret_code run_subframe(srslte_cell_t *cell, uint32_t *tti_cnt); + ret_code run_subframe(srslte_cell_t *cell, uint32_t *tti_cnt, bool sfidx_only = false); private: srslte::log *log_h; @@ -145,74 +152,106 @@ private: // Class to perform cell measurements class measure { + + // TODO: This class could early stop once the variance between the last N measurements is below 3GPP requirements + public: typedef enum {IDLE, MEASURE_OK, ERROR} ret_code; ~measure(); - void init(srslte_ue_sync_t *ue_sync, cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, + void init(cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t nof_subframes = RSRP_MEASURE_NOF_FRAMES); void reset(); void set_cell(srslte_cell_t cell); ret_code run_subframe(uint32_t sf_idx); + ret_code run_subframe_sync(srslte_ue_sync_t *ue_sync, uint32_t sf_idx); + ret_code run_multiple_subframes(cf_t *buffer, uint32_t sf_idx, uint32_t nof_sf); float rsrp(); float rsrq(); float snr(); - float cfo(); private: srslte::log *log_h; srslte_ue_dl_t ue_dl; - srslte_ue_sync_t *ue_sync; cf_t *buffer[SRSLTE_MAX_PORTS]; uint32_t cnt; uint32_t nof_subframes; - float mean_rsrp, mean_rsrq, mean_snr, mean_cfo; + uint32_t current_prb; + float mean_rsrp, mean_rsrq, mean_snr; const static int RSRP_MEASURE_NOF_FRAMES = 5; }; - // Class to receive secondary cell - class scell_recv : public thread { + class scell_recv { public: - void init(phch_recv *parent, srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t prio, int cpu_affinity = -1); - void stop(); + const static int MAX_CELLS = 8; + typedef struct { + uint32_t pci; + float rsrp; + float rsrq; + uint32_t offset; + } cell_info_t; + void init(srslte::log *log_h); void reset(); - int recv(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time); - void write(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time); - bool is_enabled(); - void set_cell(srslte_cell_t scell); + int find_cells(cf_t *input_buffer, srslte_cell_t current_cell, uint32_t nof_sf, cell_info_t found_cells[MAX_CELLS]); private: - void run_thread(); - enum { - IDLE = 0, - SCELL_SELECT, - SCELL_MEASURE, - SCELL_CAMPING - } scell_state; + const static int DEFAULT_MEASUREMENT_LEN = 10; - srslte::log *log_h; - phch_recv *p; - bool running; - srslte_ringbuffer_t ring_buffer[SRSLTE_MAX_PORTS]; cf_t *sf_buffer[SRSLTE_MAX_PORTS]; - srslte_ue_sync_t ue_sync; - srslte_cell_t cell; - uint32_t nof_rx_antennas; - uint32_t current_sflen; + srslte::log *log_h; + srslte_sync_t sync_find; measure measure_p; - sfn_sync sfn_p; - uint32_t tti; }; + /* TODO: Intra-freq measurements can be improved by capturing 200 ms length signal and run cell search + + * measurements offline using sync object and finding multiple cells for each N_id_2 + */ + + // Class to perform intra-frequency measurements + class intra_measure : public thread { + public: + void init(phch_common *common, rrc_interface_phy *rrc, srslte::log *log_h); + void stop(); + void add_cell(int pci); + void rem_cell(int pci); + void set_primay_cell(uint32_t earfcn, srslte_cell_t cell); + void clear_cells(); + void write(uint32_t tti, cf_t *data, uint32_t nsamples); + private: + void run_thread(); + const static int CAPTURE_LEN_SF = 15; + const static int INTRA_FREQ_MEAS_PERIOD_MS = 200; + scell_recv scell; + rrc_interface_phy *rrc; + srslte::log *log_h; + phch_common *common; + uint32_t current_earfcn; + uint32_t current_sflen; + srslte_cell_t primary_cell; + std::vector active_pci; + + srslte::tti_sync_cv tti_sync; + + cf_t *search_buffer; + + scell_recv::cell_info_t info[scell_recv::MAX_CELLS]; + + bool running; + bool receive_enabled; + bool receiving; + uint32_t measure_tti; + uint32_t receive_cnt; + srslte_ringbuffer_t ring_buffer; + }; // Objects for internal use - scell_recv scell; measure measure_p; search search_p; sfn_sync sfn_p; + intra_measure intra_freq_meas; uint32_t current_sflen; int next_offset; diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index c75fbb90d..6a0e8891e 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -40,8 +40,8 @@ #include "srslte/interfaces/ue_interfaces.h" namespace srsue { - -typedef _Complex float cf_t; + +typedef _Complex float cf_t; class phy : public phy_interface_mac @@ -78,7 +78,6 @@ public: void set_earfcn(std::vector earfcns); void force_freq(float dl_freq, float ul_freq); - void scell_enable(bool enable); /********** RRC INTERFACE ********************/ void reset(); @@ -89,6 +88,10 @@ public: void cell_search_next(); bool cell_select(uint32_t earfcn, srslte_cell_t phy_cell); + void meas_reset(); + int meas_start(uint32_t earfcn, int pci); + int meas_stop(uint32_t earfcn, int pci); + /********** MAC INTERFACE ********************/ /* Functions to synchronize with a cell */ bool sync_status(); // this is also RRC interface @@ -133,7 +136,7 @@ public: float get_pathloss_db(); uint32_t get_current_tti(); - void get_current_cell(srslte_cell_t *cell); + void get_current_cell(srslte_cell_t *cell, uint32_t *current_earfcn = NULL); void start_plot(); diff --git a/srsue/hdr/ue.h b/srsue/hdr/ue.h index fa461d1c9..bfb38f14d 100644 --- a/srsue/hdr/ue.h +++ b/srsue/hdr/ue.h @@ -80,8 +80,6 @@ public: void pregenerate_signals(bool enable); - void test_scell(); - private: virtual ~ue(); diff --git a/srsue/hdr/upper/rrc.h b/srsue/hdr/upper/rrc.h index 3e2fb70dd..5ec484251 100644 --- a/srsue/hdr/upper/rrc.h +++ b/srsue/hdr/upper/rrc.h @@ -38,6 +38,7 @@ #include "srslte/common/threads.h" #include +#include using srslte::byte_buffer_t; @@ -80,6 +81,46 @@ public: void liblte_rrc_log(char *str); + + // NAS interface + void write_sdu(uint32_t lcid, byte_buffer_t *sdu); + + uint16_t get_mcc(); + + uint16_t get_mnc(); + + void enable_capabilities(); + void plmn_search(); + void plmn_select(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id); + + // PHY interface + void in_sync(); + void out_of_sync(); + void earfcn_end(); + void cell_found(uint32_t earfcn, srslte_cell_t phy_cell, float rsrp); + void new_phy_meas(float rsrp, float rsrq, uint32_t tti, uint32_t earfcn, uint32_t pci); + + // MAC interface + void release_pucch_srs(); + void run_tti(uint32_t tti); + + void ra_problem(); + + // GW interface + bool is_connected(); + + bool have_drb(); + + // PDCP interface + void write_pdu(uint32_t lcid, byte_buffer_t *pdu); + + void write_pdu_bcch_bch(byte_buffer_t *pdu); + + void write_pdu_bcch_dlsch(byte_buffer_t *pdu); + + void write_pdu_pcch(byte_buffer_t *pdu); + + private: srslte::byte_buffer_pool *pool; srslte::log *rrc_log; @@ -90,7 +131,9 @@ private: nas_interface_rrc *nas; usim_interface_rrc *usim; - srslte::bit_buffer_t bit_buf; + void send_ul_dcch_msg(byte_buffer_t *pdu = NULL); + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + srslte::bit_buffer_t bit_buf; pthread_mutex_t mutex; @@ -166,42 +209,6 @@ private: bool thread_running; void run_thread(); - // NAS interface - void write_sdu(uint32_t lcid, byte_buffer_t *sdu); - - uint16_t get_mcc(); - - uint16_t get_mnc(); - - void enable_capabilities(); - void plmn_search(); - void plmn_select(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id); - - // PHY interface - void in_sync(); - void out_of_sync(); - void earfcn_end(); - void cell_found(uint32_t earfcn, srslte_cell_t phy_cell, float rsrp); - - // MAC interface - void release_pucch_srs(); - - void ra_problem(); - - // GW interface - bool is_connected(); - - bool have_drb(); - - // PDCP interface - void write_pdu(uint32_t lcid, byte_buffer_t *pdu); - - void write_pdu_bcch_bch(byte_buffer_t *pdu); - - void write_pdu_bcch_dlsch(byte_buffer_t *pdu); - - void write_pdu_pcch(byte_buffer_t *pdu); - // Radio bearers typedef enum{ RB_ID_SRB0 = 0, @@ -226,6 +233,97 @@ private: } } + // Measurements sub-class + class rrc_meas { + public: + void init(rrc *parent); + void reset(); + void parse_meas_config(LIBLTE_RRC_MEAS_CONFIG_STRUCT *meas_config); + void new_phy_meas(uint32_t earfcn, uint32_t pci, float rsrp, float rsrq, uint32_t tti); + void run_tti(uint32_t tti); + bool timer_expired(uint32_t timer_id); + private: + + const static int NOF_MEASUREMENTS = 3; + + typedef enum {RSRP = 0, RSRQ = 1, BOTH = 2} quantity_t; + + typedef struct { + uint32_t pci; + float q_offset; + } meas_cell_t; + + typedef struct { + uint32_t earfcn; + float q_offset; + std::map 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 cell_values; // Value for each PCI in this object + } meas_t; + + std::map objects; + std::map reports_cfg; + std::map active; + + rrc *parent; + srslte::log *log_h; + phy_interface_rrc *phy; + srslte::mac_interface_timers *mac_timers; + + uint32_t filter_k_rsrp, filter_k_rsrq; + float filter_a[NOF_MEASUREMENTS]; + + meas_value_t pcell_measurement; + + bool s_measure_enabled; + float s_measure_value; + + void stop_reports_object(uint32_t object_id); + void remove_meas_object(uint32_t object_id); + void remove_meas_report(uint32_t report_id); + void remove_meas_id(uint32_t meas_id); + void calculate_triggers(uint32_t tti); + void update_phy(); + void L3_filter(meas_value_t *value, float rsrp[NOF_MEASUREMENTS]); + bool find_earfcn_cell(uint32_t earfcn, uint32_t pci, meas_obj_t **object, int *cell_idx); + float range_to_value(quantity_t quant, uint8_t range); + uint8_t value_to_range(quantity_t quant, float value); + bool process_event(LIBLTE_RRC_EVENT_EUTRA_STRUCT *event, uint32_t tti, + bool enter_condition, bool exit_condition, + meas_t *m, meas_value_t *cell); + + void generate_report(uint32_t meas_id); + }; + + rrc_meas measurements; + + // RLC interface void max_retx_attempted(); diff --git a/srsue/src/mac/mac.cc b/srsue/src/mac/mac.cc index f8d10bb34..66adb5b0b 100644 --- a/srsue/src/mac/mac.cc +++ b/srsue/src/mac/mac.cc @@ -180,6 +180,8 @@ void mac::run_thread() { ra_procedure.start_mac_order(); } ra_procedure.step(tti); + + rrc_h->run_tti(tti); } } diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 92745edb8..0cd31a3d6 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -398,7 +398,6 @@ int main(int argc, char *argv[]) pthread_t input; pthread_create(&input, NULL, &input_loop, &args); - bool scell_done = false; bool plot_started = false; bool signals_pregenerated = false; @@ -408,10 +407,6 @@ int main(int argc, char *argv[]) ue->pregenerate_signals(true); signals_pregenerated = true; } - if (!scell_done) { - ((srsue::ue*) ue)->test_scell(); - scell_done = true; - } if (!plot_started && args.gui.enable) { ue->start_plot(); plot_started = true; diff --git a/srsue/src/phy/phch_common.cc b/srsue/src/phy/phch_common.cc index d956ddd15..416f146ac 100644 --- a/srsue/src/phy/phch_common.cc +++ b/srsue/src/phy/phch_common.cc @@ -59,6 +59,9 @@ phch_common::phch_common(uint32_t max_mutex_) : tx_mutex(max_mutex_) rx_gain_offset = 0; sr_last_tx_tti = -1; cur_pusch_power = 0; + + serving_cell_report_period = 20; + bzero(zeros, 50000*sizeof(cf_t)); // FIXME: This is an ungly fix to avoid the TX filters to empty diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 91d2cd482..126c4a2f6 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -25,11 +25,10 @@ */ #include -#include +#include #include "srslte/srslte.h" #include "srslte/common/log.h" #include "phy/phch_worker.h" -#include "phy/phch_common.h" #include "phy/phch_recv.h" #define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) @@ -41,13 +40,8 @@ namespace srsue { int radio_recv_callback(void *obj, cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time) { return ((phch_recv*) obj)->radio_recv_fnc(data, nsamples, rx_time); - } -int scell_recv_callback(void *obj, cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time) { - return ((phch_recv*) obj)->scell_recv_fnc(data, nsamples, rx_time); -} - double callback_set_rx_gain(void *h, double gain) { srslte::radio_multi *radio_handler = (srslte::radio_multi *) h; return radio_handler->set_rx_gain_th(gain); @@ -94,10 +88,10 @@ void phch_recv::init(srslte::radio_multi *_radio_handler, mac_interface_phy *_ma sfn_p.init(&ue_sync, sf_buffer, log_h); // Initialize measurement class for the primary cell - measure_p.init(&ue_sync, sf_buffer, log_h, nof_rx_antennas); + measure_p.init(sf_buffer, log_h, nof_rx_antennas); - // Start scell - scell.init(this, log_h, nof_rx_antennas_, prio-1, sync_cpu_affinity); + // Start intra-frequency measurement + intra_freq_meas.init(worker_com, rrc, log_h); reset(); @@ -120,7 +114,7 @@ phch_recv::~phch_recv() { void phch_recv::stop() { - + intra_freq_meas.stop(); running = false; wait_thread_finish(); } @@ -223,6 +217,7 @@ bool phch_recv::set_cell() { measure_p.set_cell(cell); sfn_p.set_cell(cell); worker_com->set_cell(cell); + intra_freq_meas.set_primay_cell(current_earfcn, cell); for (uint32_t i = 0; i < workers_pool->get_nof_workers(); i++) { if (!((phch_worker *) workers_pool->get_worker(i))->set_cell(cell)) { @@ -464,10 +459,13 @@ bool phch_recv::status_is_sync() { return phy_state == CELL_CAMP; } -void phch_recv::get_current_cell(srslte_cell_t *cell_) { +void phch_recv::get_current_cell(srslte_cell_t *cell_, uint32_t *earfcn) { if (cell_) { memcpy(cell_, &cell, sizeof(srslte_cell_t)); } + if (earfcn) { + *earfcn = current_earfcn; + } } int phch_recv::radio_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time) @@ -480,10 +478,6 @@ int phch_recv::radio_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, s next_offset = nsamples; } - if (offset <= 0) { - scell.write(data, nsamples, rx_time); - } - log_h->debug("SYNC: received %d samples from radio\n", nsamples); return nsamples; @@ -492,19 +486,6 @@ int phch_recv::radio_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, s } } -int phch_recv::scell_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time) -{ - return scell.recv(data, nsamples, rx_time); -} - -void phch_recv::scell_enable(bool enable) -{ - srslte_cell_t target_cell; - memcpy(&target_cell, &cell, sizeof(srslte_cell_t)); - target_cell.id++; - scell.set_cell(target_cell); -} - @@ -587,7 +568,7 @@ void phch_recv::run_thread() } break; case CELL_MEASURE: - switch(measure_p.run_subframe(sf_idx)) + switch(measure_p.run_subframe_sync(&ue_sync, sf_idx)) { case measure::MEASURE_OK: log_h->info("SYNC: Measured OK. Camping on cell PCI=%d...\n", cell.id); @@ -643,6 +624,9 @@ void phch_recv::run_thread() worker_com->cur_radio_power = SRSLTE_MIN(SRSLTE_PC_MAX, worker_com->pathloss+worker_com->p0_preamble); } workers_pool->start_worker(worker); + + intra_freq_meas.write(tti, buffer[0], SRSLTE_SF_LEN_PRB(cell.nof_prb)); + break; case 0: log_h->error("SYNC: Sync error. Sending out-of-sync to RRC\n"); @@ -789,8 +773,8 @@ phch_recv::search::ret_code phch_recv::search::run(srslte_cell_t *cell) return CELL_NOT_FOUND; } // Save result - cell->id = found_cells[max_peak_cell].cell_id; - cell->cp = found_cells[max_peak_cell].cp; + cell->id = found_cells[max_peak_cell].cell_id; + cell->cp = found_cells[max_peak_cell].cp; float cfo = found_cells[max_peak_cell].cfo; printf("\n"); @@ -864,7 +848,7 @@ void phch_recv::sfn_sync::init(srslte_ue_sync_t *ue_sync, cf_t *buffer[SRSLTE_MA this->buffer[i] = buffer[i]; } - if (srslte_ue_mib_init(&ue_mib, SRSLTE_MAX_PRB)) { + if (srslte_ue_mib_init(&ue_mib, this->buffer, SRSLTE_MAX_PRB)) { Error("SYNC: Initiating UE MIB decoder\n"); } } @@ -885,7 +869,7 @@ void phch_recv::sfn_sync::reset() cnt = 0; } -phch_recv::sfn_sync::ret_code phch_recv::sfn_sync::run_subframe(srslte_cell_t *cell, uint32_t *tti_cnt) +phch_recv::sfn_sync::ret_code phch_recv::sfn_sync::run_subframe(srslte_cell_t *cell, uint32_t *tti_cnt, bool sfidx_only) { uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN]; @@ -899,10 +883,19 @@ phch_recv::sfn_sync::ret_code phch_recv::sfn_sync::run_subframe(srslte_cell_t *c if (ret == 1) { if (srslte_ue_sync_get_sfidx(ue_sync) == 0) { + + // Skip MIB decoding if we are only interested in subframe 0 + if (sfidx_only) { + if (tti_cnt) { + *tti_cnt = 0; + } + return SFX0_FOUND; + } + int sfn_offset = 0; Info("SYNC: Trying to decode MIB... SNR=%.1f dB\n", 10*log10(srslte_chest_dl_get_snr(&ue_mib.chest))); - int n = srslte_ue_mib_decode(&ue_mib, buffer[0], bch_payload, NULL, &sfn_offset); + int n = srslte_ue_mib_decode(&ue_mib, bch_payload, NULL, &sfn_offset); if (n < 0) { Error("SYNC: Error decoding MIB while synchronising SFN"); return ERROR; @@ -944,16 +937,15 @@ phch_recv::sfn_sync::ret_code phch_recv::sfn_sync::run_subframe(srslte_cell_t *c /********* * Measurement class */ -void phch_recv::measure::init(srslte_ue_sync_t *ue_sync, cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t nof_subrames) +void phch_recv::measure::init(cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t nof_subframes) { this->log_h = log_h; - this->nof_subframes = nof_subrames; - this->ue_sync = ue_sync; + this->nof_subframes = nof_subframes; for (int i=0;ibuffer[i] = buffer[i]; } - if (srslte_ue_dl_init(&ue_dl, SRSLTE_MAX_PRB, nof_rx_antennas)) { + if (srslte_ue_dl_init(&ue_dl, this->buffer, SRSLTE_MAX_PRB, nof_rx_antennas)) { Error("SYNC: Initiating ue_dl_measure\n"); return; } @@ -969,11 +961,11 @@ void phch_recv::measure::reset() { mean_rsrp = 0; mean_rsrq = 0; mean_snr = 0; - mean_cfo = 0; } void phch_recv::measure::set_cell(srslte_cell_t cell) { + current_prb = cell.nof_prb; if (srslte_ue_dl_set_cell(&ue_dl, cell)) { Error("SYNC: Setting cell: initiating ue_dl_measure\n"); } @@ -981,50 +973,22 @@ void phch_recv::measure::set_cell(srslte_cell_t cell) } float phch_recv::measure::rsrp() { - return 10*log10(mean_rsrp/1000); + return mean_rsrp; } float phch_recv::measure::rsrq() { - return 10*log10(mean_rsrq); + return mean_rsrq; } float phch_recv::measure::snr() { - return 10*log10(mean_snr); + return mean_snr; } -float phch_recv::measure::cfo() { - return mean_cfo; -} - -phch_recv::measure::ret_code phch_recv::measure::run_subframe(uint32_t sf_idx) +phch_recv::measure::ret_code phch_recv::measure::run_subframe_sync(srslte_ue_sync_t *ue_sync, uint32_t sf_idx) { int sync_res = srslte_ue_sync_zerocopy_multi(ue_sync, buffer); if (sync_res == 1) { - uint32_t cfi = 0; - - if (srslte_ue_dl_decode_fft_estimate(&ue_dl, buffer, sf_idx, &cfi)) { - log_h->error("SYNC: Measuring RSRP: Estimating channel\n"); - return ERROR; - } - - float rsrp = srslte_chest_dl_get_rsrp(&ue_dl.chest); - float rsrq = srslte_chest_dl_get_rsrq(&ue_dl.chest); - float snr = srslte_chest_dl_get_snr(&ue_dl.chest); - float cfo = srslte_ue_sync_get_cfo(ue_sync); - - mean_rsrp = SRSLTE_VEC_CMA(rsrp, mean_rsrp, cnt); - mean_rsrq = SRSLTE_VEC_CMA(rsrq, mean_rsrq, cnt); - mean_snr = SRSLTE_VEC_CMA(snr, mean_snr, cnt); - mean_cfo = SRSLTE_VEC_CMA(cfo, mean_cfo, cnt); - cnt++; - - log_h->info("SYNC: Measuring RSRP %d/%d, sf_idx=%d, RSRP=%.1f dBm, SNR=%.1f dB\n", - cnt, RSRP_MEASURE_NOF_FRAMES, sf_idx, - 10*log10(rsrp/1000), 10*log10(snr)); - - if (cnt >= nof_subframes) { - return MEASURE_OK; - } + return run_subframe(sf_idx); } else { log_h->error("SYNC: Measuring RSRP: Sync error\n"); return ERROR; @@ -1033,7 +997,51 @@ phch_recv::measure::ret_code phch_recv::measure::run_subframe(uint32_t sf_idx) return IDLE; } +phch_recv::measure::ret_code phch_recv::measure::run_multiple_subframes(cf_t *input_buffer, + uint32_t sf_idx, + uint32_t nof_sf) +{ + uint32_t sf_len = SRSLTE_SF_LEN_PRB(current_prb); + ret_code ret = IDLE; + + for (uint32_t i=0;ierror("SYNC: Measuring RSRP: Estimating channel\n"); + return ERROR; + } + + float rsrp = 10*log10(srslte_chest_dl_get_rsrp(&ue_dl.chest)) + 30; + float rsrq = 10*log10(srslte_chest_dl_get_rsrq(&ue_dl.chest)); + float snr = 10*log10(srslte_chest_dl_get_snr(&ue_dl.chest)); + + mean_rsrp = SRSLTE_VEC_CMA(rsrp, mean_rsrp, cnt); + mean_rsrq = SRSLTE_VEC_CMA(rsrq, mean_rsrq, cnt); + mean_snr = SRSLTE_VEC_CMA(snr, mean_snr, cnt); + cnt++; + + log_h->info("SYNC: Measuring RSRP %d/%d, sf_idx=%d, RSRP=%.1f dBm, SNR=%.1f dB\n", + cnt, nof_subframes, sf_idx, + rsrp, snr); + + if (cnt >= nof_subframes) { + return MEASURE_OK; + } else { + return IDLE; + } +} @@ -1044,164 +1052,246 @@ phch_recv::measure::ret_code phch_recv::measure::run_subframe(uint32_t sf_idx) * Secondary cell receiver */ -void phch_recv::scell_recv::init(phch_recv *parent, srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t prio, int cpu_affinity) +void phch_recv::scell_recv::init(srslte::log *log_h) { - this->p = parent; this->log_h = log_h; - this->nof_rx_antennas = nof_rx_antennas; - // Create the ringbuffer for secondary cell reception - for (int i=0;i current_sflen) { - read_samples = current_sflen; - } - if (nsamples < 10) { - read_samples = 0; - } - int n = 0; - for (uint32_t i=0;idebug("SCELL: tti=%d, read %d/%d samples from buffer, buffer size=%d\n", - tti, read_samples,nsamples, srslte_ringbuffer_status(&ring_buffer[0])); - return nsamples; + +/********** + * PHY measurements + * + */ + +void phch_recv::meas_reset() { + // Stop all measurements + intra_freq_meas.clear_cells(); +} + +int phch_recv::meas_start(uint32_t earfcn, int pci) { + if (earfcn == current_earfcn) { + intra_freq_meas.add_cell(pci); + return 0; } else { - Error("SCELL: Reception not enabled\n"); + Warning("INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested measurement for %d)\n", + current_earfcn, earfcn); return -1; } } -void phch_recv::scell_recv::write(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time) -{ - if (is_enabled()) { - for (uint32_t i = 0; i < nof_rx_antennas; i++) { - srslte_ringbuffer_write(&ring_buffer[i], data[i], sizeof(cf_t) * nsamples); - } +int phch_recv::meas_stop(uint32_t earfcn, int pci) { + if (earfcn == current_earfcn) { + intra_freq_meas.rem_cell(pci); + return 0; + } else { + Warning("INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested stop measurement for %d)\n", + current_earfcn, earfcn); + } + return -1; +} + +void phch_recv::intra_measure::init(phch_common *common, rrc_interface_phy *rrc, srslte::log *log_h) { + this->rrc = rrc; + this->log_h = log_h; + this->common = common; + receive_enabled = false; + + // Start scell + scell.init(log_h); + + search_buffer = (cf_t*) srslte_vec_malloc(CAPTURE_LEN_SF*SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB)*sizeof(cf_t)); + + if (srslte_ringbuffer_init(&ring_buffer, sizeof(cf_t)*50*SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB))) { + return; + } + + running = true; + start(); +} + +void phch_recv::intra_measure::stop() { + running = false; + tti_sync.increase(); + wait_thread_finish(); +} + +void phch_recv::intra_measure::set_primay_cell(uint32_t earfcn, srslte_cell_t cell) { + this->current_earfcn = earfcn; + current_sflen = SRSLTE_SF_LEN_PRB(cell.nof_prb); + memcpy(&this->primary_cell, &cell, sizeof(srslte_cell_t)); +} + +void phch_recv::intra_measure::clear_cells() { + active_pci.clear(); + receive_enabled = false; +} + +void phch_recv::intra_measure::add_cell(int pci) { + if (std::find(active_pci.begin(), active_pci.end(), pci) == active_pci.end()) { + active_pci.push_back(pci); + receive_enabled = true; + Info("INTRA: Starting intra-frequency measurement for pci=%d\n", pci); + } else { + Warning("INTRA: Requested to start already existing intra-frequency measurement for PCI=%d\n", pci); } } -void phch_recv::scell_recv::run_thread() +void phch_recv::intra_measure::rem_cell(int pci) { + std::vector::iterator newEnd = std::remove(active_pci.begin(), active_pci.end(), pci); + + if (newEnd != active_pci.end()) { + active_pci.erase(newEnd, active_pci.end()); + if (active_pci.size() == 0) { + receive_enabled = false; + } + Info("INTRA: Stopping intra-frequency measurement for pci=%d. Number of cells: %d\n", pci, active_pci.size()); + } else { + Warning("INTRA: Requested to stop non-existing intra-frequency measurement for PCI=%d\n", pci); + } +} + +void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples) { + if (receive_enabled) { + if ((tti%INTRA_FREQ_MEAS_PERIOD_MS) == 0) { + receiving = true; + receive_cnt = 0; + measure_tti = tti; + } + if (receiving == true) { + if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { + receiving = false; + srslte_ringbuffer_reset(&ring_buffer); + } else { + receive_cnt++; + if (receive_cnt == CAPTURE_LEN_SF) { + tti_sync.increase(); + } + } + } + + } +} + +void phch_recv::intra_measure::run_thread() { while(running) { - switch(scell_state) { - case IDLE: - usleep(1000); - break; - case SCELL_SELECT: - - switch (sfn_p.run_subframe(&cell, &tti)) - { - case sfn_sync::SFN_FOUND: - log_h->info("SCELL: SFN Sync OK. Camping on cell PCI=%d...\n", cell.id); - sfn_p.reset(); - scell_state = SCELL_MEASURE; - break; - case sfn_sync::TIMEOUT: - log_h->info("SCELL: SFN sync timeout\n"); - break; - case sfn_sync::IDLE: - break; - default: - p->radio_error(); - break; - } - - break; - case SCELL_MEASURE: - switch (measure_p.run_subframe(tti%10)) { - case measure::MEASURE_OK: - log_h->info("SCELL: Measured OK TTI=%5d, RSRP=%.1f dBm, RSRQ=%.1f dB, SNR=%3.1f dB, CFO=%.1f KHz, Buff=%d\n", - tti, measure_p.rsrp(), measure_p.rsrq(), measure_p.snr(), measure_p.cfo()/1000, - srslte_ringbuffer_status(&ring_buffer[0])); - measure_p.reset(); - break; - case measure::IDLE: - break; - default: - p->radio_error(); - break; - } - break; + if (running) { + tti_sync.wait(); + } + + if (running) { + Info("INTRA: Running intra-frequency measurement for %d cells\n", active_pci.size()); + + // Read 5 ms data from buffer + srslte_ringbuffer_read(&ring_buffer, search_buffer, CAPTURE_LEN_SF*current_sflen*sizeof(cf_t)); + int found_cells = scell.find_cells(search_buffer, primary_cell, CAPTURE_LEN_SF, info); + receiving = false; + srslte_ringbuffer_reset(&ring_buffer); + + for (int i=0;irx_gain_offset; + rrc->new_phy_meas(info[i].rsrp, info[i].rsrq, measure_tti, current_earfcn, info[i].pci); + } + // Look for other cells not found automatically } - // Increase TTI counter - tti = (tti+1) % 10240; } } diff --git a/srsue/src/phy/phch_worker.cc b/srsue/src/phy/phch_worker.cc index cf0eab94c..5d93d94a4 100644 --- a/srsue/src/phy/phch_worker.cc +++ b/srsue/src/phy/phch_worker.cc @@ -1210,8 +1210,8 @@ void phch_worker::update_measurements() { float snr_ema_coeff = phy->args->snr_ema_coeff; if (chest_done) { - /* Compute ADC/RX gain offset every 20 ms */ - if ((tti%20) == 0 || phy->rx_gain_offset == 0) { + /* Compute ADC/RX gain offset every ~10s */ + if (tti== 0 || phy->rx_gain_offset == 0) { float rx_gain_offset = 0; if (phy->get_radio()->has_rssi() && phy->args->rssi_sensor_enabled) { float rssi_all_signal = srslte_chest_dl_get_rssi(&ue_dl.chest); @@ -1224,7 +1224,7 @@ void phch_worker::update_measurements() rx_gain_offset = phy->get_radio()->get_rx_gain(); } if (phy->rx_gain_offset) { - phy->rx_gain_offset = SRSLTE_VEC_EMA(phy->rx_gain_offset, rx_gain_offset, 0.1); + phy->rx_gain_offset = SRSLTE_VEC_EMA(phy->rx_gain_offset, rx_gain_offset, 0.5); } else { phy->rx_gain_offset = rx_gain_offset; } @@ -1246,15 +1246,16 @@ void phch_worker::update_measurements() float rsrp = 10*log10(srslte_chest_dl_get_rsrp(&ue_dl.chest)) + 30 - phy->rx_gain_offset; float rssi = 10*log10(srslte_chest_dl_get_rssi(&ue_dl.chest)) + 30 - phy->rx_gain_offset; - // TODO: Send UE measurements to RRC where filtering is done. Now do filtering here + // Serving cell measurements are averaged over DEFAULT_MEAS_PERIOD_MS then sent to RRC if (isnormal(rsrp)) { if (!phy->avg_rsrp_db) { phy->avg_rsrp_db = rsrp; } else { - uint32_t k = 4; // Set by RRC reconfiguration message - float coeff = pow(0.5,(float) k/4); - phy->avg_rsrp_db = SRSLTE_VEC_EMA(phy->avg_rsrp_db, rsrp, coeff); + phy->avg_rsrp_db = SRSLTE_VEC_EMA(phy->avg_rsrp_db, rsrp, 0.8); } + if ((tti%phy->serving_cell_report_period) == 0) { + phy->rrc->new_phy_meas(phy->avg_rsrp_db, phy->avg_rsrq_db, tti); + } } // Compute PL float tx_crs_power = phy->config->common.pdsch_cnfg.rs_power; diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 2c140c191..7d3b8f311 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -243,15 +243,22 @@ void phy::cell_search_next() sf_recv.cell_search_next(); } -void phy::scell_enable(bool enable) -{ - sf_recv.scell_enable(enable); -} - void phy::sync_reset() { sf_recv.reset_sync(); } +void phy::meas_reset() { + sf_recv.meas_reset(); +} + +int phy::meas_start(uint32_t earfcn, int pci) { + return sf_recv.meas_start(earfcn, pci); +} + +int phy::meas_stop(uint32_t earfcn, int pci) { + return sf_recv.meas_stop(earfcn, pci); +} + bool phy::cell_select(uint32_t earfcn, srslte_cell_t phy_cell) { return sf_recv.cell_select(earfcn, phy_cell); @@ -288,9 +295,9 @@ void phy::pdcch_ul_search_reset() workers_common.set_ul_rnti(SRSLTE_RNTI_USER, 0); } -void phy::get_current_cell(srslte_cell_t *cell) +void phy::get_current_cell(srslte_cell_t *cell, uint32_t *current_earfcn) { - sf_recv.get_current_cell(cell); + sf_recv.get_current_cell(cell, current_earfcn); } void phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm) diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index c97958316..a99281e6f 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -215,11 +215,6 @@ void ue::pregenerate_signals(bool enable) phy.enable_pregen_signals(enable); } -void ue::test_scell() -{ - phy.scell_enable(true); -}; - void ue::stop() { if(started) diff --git a/srsue/src/upper/rrc.cc b/srsue/src/upper/rrc.cc index 10373dcf2..434f931f3 100644 --- a/srsue/src/upper/rrc.cc +++ b/srsue/src/upper/rrc.cc @@ -109,6 +109,8 @@ void rrc::init(phy_interface_rrc *phy_, set_rrc_default(); set_phy_default(); set_mac_default(); + + measurements.init(this); } void rrc::stop() { @@ -116,6 +118,10 @@ void rrc::stop() { wait_thread_finish(); } +void rrc::run_tti(uint32_t tti) { + measurements.run_tti(tti); +} + rrc_state_t rrc::get_state() { return state; } @@ -418,6 +424,10 @@ void rrc::select_next_cell_in_plmn() { rrc_log->info("No more known cells...\n"); } +void rrc::new_phy_meas(float rsrp, float rsrq, uint32_t tti, uint32_t earfcn, uint32_t pci) { + measurements.new_phy_meas(earfcn, pci, rsrp, rsrq, tti); +} + void rrc::cell_found(uint32_t earfcn, srslte_cell_t phy_cell, float rsrp) { // find if cell_id-earfcn combination already exists @@ -562,7 +572,8 @@ void rrc::timer_expired(uint32_t timeout_id) { } else if (timeout_id == t301) { rrc_log->info("Timer T301 expired: Going to RRC IDLE\n"); state = RRC_STATE_LEAVE_CONNECTED; - } else { + // fw to measurement + } else if (!measurements.timer_expired(timeout_id)) { rrc_log->error("Timeout from unknown timer id %d\n", timeout_id); } } @@ -717,32 +728,22 @@ void rrc::con_restablish_cell_reselected() void rrc::send_con_restablish_complete() { rrc_log->debug("Preparing RRC Connection Reestablishment Complete\n"); - LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + + rrc_log->console("RRC Connected\n"); + state = RRC_STATE_CONNECTED; // Prepare ConnectionSetupComplete packet ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_REEST_COMPLETE; ul_dcch_msg.msg.rrc_con_reest_complete.rrc_transaction_id = transaction_id; - liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf); - // Byte align and pack the message bits for PDCP - if ((bit_buf.N_bits % 8) != 0) { - for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++) - bit_buf.msg[bit_buf.N_bits + i] = 0; - bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); - } - byte_buffer_t *pdcp_buf = pool_allocate;; - srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits); - pdcp_buf->N_bytes = bit_buf.N_bits / 8; - - state = RRC_STATE_CONNECTED; - rrc_log->console("RRC Connected\n"); - rrc_log->info("Sending RRC Connection Reestablishment Complete\n"); - pdcp->write_sdu(RB_ID_SRB1, pdcp_buf); + send_ul_dcch_msg(); } void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) { rrc_log->debug("Preparing RRC Connection Setup Complete\n"); - LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + + state = RRC_STATE_CONNECTED; + rrc_log->console("RRC Connected\n"); // Prepare ConnectionSetupComplete packet ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_SETUP_COMPLETE; @@ -751,96 +752,38 @@ void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) { ul_dcch_msg.msg.rrc_con_setup_complete.selected_plmn_id = 1; memcpy(ul_dcch_msg.msg.rrc_con_setup_complete.dedicated_info_nas.msg, nas_msg->msg, nas_msg->N_bytes); ul_dcch_msg.msg.rrc_con_setup_complete.dedicated_info_nas.N_bytes = nas_msg->N_bytes; - liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf); - // Byte align and pack the message bits for PDCP - if ((bit_buf.N_bits % 8) != 0) { - for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++) - bit_buf.msg[bit_buf.N_bits + i] = 0; - bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); - } - byte_buffer_t *pdcp_buf = pool_allocate;; - srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits); - pdcp_buf->N_bytes = bit_buf.N_bits / 8; - pdcp_buf->set_timestamp(); - - state = RRC_STATE_CONNECTED; - rrc_log->console("RRC Connected\n"); - rrc_log->info("Sending RRC Connection Setup Complete\n"); - pdcp->write_sdu(RB_ID_SRB1, pdcp_buf); + send_ul_dcch_msg(); } void rrc::send_ul_info_transfer(uint32_t lcid, byte_buffer_t *sdu) { rrc_log->debug("Preparing RX Info Transfer\n"); - LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; // Prepare RX INFO packet ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER; ul_dcch_msg.msg.ul_info_transfer.dedicated_info_type = LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_NAS; memcpy(ul_dcch_msg.msg.ul_info_transfer.dedicated_info.msg, sdu->msg, sdu->N_bytes); ul_dcch_msg.msg.ul_info_transfer.dedicated_info.N_bytes = sdu->N_bytes; - liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf); - // Reset and reuse sdu buffer - byte_buffer_t *pdu = sdu; - pdu->reset(); - - // Byte align and pack the message bits for PDCP - if ((bit_buf.N_bits % 8) != 0) { - for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++) - bit_buf.msg[bit_buf.N_bits + i] = 0; - bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); - } - srslte_bit_pack_vector(bit_buf.msg, pdu->msg, bit_buf.N_bits); - pdu->N_bytes = bit_buf.N_bits / 8; - pdu->set_timestamp(); - pdu->set_timestamp(); - - rrc_log->info("Sending RX Info Transfer\n"); - pdcp->write_sdu(lcid, pdu); + send_ul_dcch_msg(sdu); } void rrc::send_security_mode_complete(uint32_t lcid, byte_buffer_t *pdu) { rrc_log->debug("Preparing Security Mode Complete\n"); - LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE; ul_dcch_msg.msg.security_mode_complete.rrc_transaction_id = transaction_id; - liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf); - // Byte align and pack the message bits for PDCP - if ((bit_buf.N_bits % 8) != 0) { - for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++) - bit_buf.msg[bit_buf.N_bits + i] = 0; - bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); - } - srslte_bit_pack_vector(bit_buf.msg, pdu->msg, bit_buf.N_bits); - pdu->N_bytes = bit_buf.N_bits / 8; - pdu->set_timestamp(); - - rrc_log->info("Sending Security Mode Complete\n"); - pdcp->write_sdu(lcid, pdu); + send_ul_dcch_msg(pdu); } void rrc::send_rrc_con_reconfig_complete(uint32_t lcid, byte_buffer_t *pdu) { rrc_log->debug("Preparing RRC Connection Reconfig Complete\n"); - LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE; ul_dcch_msg.msg.rrc_con_reconfig_complete.rrc_transaction_id = transaction_id; - liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf); - // Byte align and pack the message bits for PDCP - if ((bit_buf.N_bits % 8) != 0) { - for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++) - bit_buf.msg[bit_buf.N_bits + i] = 0; - bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); - } - srslte_bit_pack_vector(bit_buf.msg, pdu->msg, bit_buf.N_bits); - pdu->N_bytes = bit_buf.N_bits / 8; - pdu->set_timestamp(); - - rrc_log->info("Sending RRC Connection Reconfig Complete\n"); - pdcp->write_sdu(lcid, pdu); + send_ul_dcch_msg(); } @@ -854,7 +797,7 @@ void rrc::handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGU printf("received con reconfig no rr confg present\n"); } if (reconfig->meas_cnfg_present) { - //TODO: handle meas_cnfg + measurements.parse_meas_config(&reconfig->meas_cnfg); } if (reconfig->mob_ctrl_info_present) { //TODO: handle mob_ctrl_info @@ -864,7 +807,7 @@ void rrc::handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGU byte_buffer_t *nas_sdu; for (i = 0; i < reconfig->N_ded_info_nas; i++) { - nas_sdu = pool_allocate;; + nas_sdu = pool_allocate; memcpy(nas_sdu->msg, &reconfig->ded_info_nas_list[i].msg, reconfig->ded_info_nas_list[i].N_bytes); nas_sdu->N_bytes = reconfig->ded_info_nas_list[i].N_bytes; nas->write_pdu(lcid, nas_sdu); @@ -1051,6 +994,33 @@ void rrc::write_pdu_pcch(byte_buffer_t *pdu) { * * *******************************************************************************/ +void rrc::send_ul_dcch_msg(byte_buffer_t *pdu) +{ + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf); + + // Byte align and pack the message bits for PDCP + if ((bit_buf.N_bits % 8) != 0) { + for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++) + bit_buf.msg[bit_buf.N_bits + i] = 0; + bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); + } + // Reset and reuse sdu buffer if provided + byte_buffer_t *pdcp_buf = pdu; + + if (pdcp_buf) { + pdcp_buf->reset(); + } else { + pdcp_buf = pool_allocate; + } + + srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits); + pdcp_buf->N_bytes = bit_buf.N_bits / 8; + pdcp_buf->set_timestamp(); + + rrc_log->info("Sending %s\n", liblte_rrc_ul_dcch_msg_type_text[ul_dcch_msg.msg_type]); + pdcp->write_sdu(RB_ID_SRB1, pdcp_buf); +} + void rrc::write_sdu(uint32_t lcid, byte_buffer_t *sdu) { rrc_log->info_hex(sdu->msg, sdu->N_bytes, "RX %s SDU", get_rb_name(lcid).c_str()); switch (state) { @@ -1764,4 +1734,582 @@ void rrc::set_rrc_default() { } + + + + + + + + + + + + + + + + + +/************************************************************************ + * + * + * RRC Measurements + * + * + ************************************************************************/ + +void rrc::rrc_meas::init(rrc *parent) { + this->parent = parent; + this->log_h = parent->rrc_log; + this->phy = parent->phy; + this->mac_timers = parent->mac_timers; + s_measure_enabled = false; + reset(); +} + +void rrc::rrc_meas::reset() +{ + bzero(&pcell_measurement, sizeof(meas_value_t)); + filter_k_rsrp = liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_FC4]; + filter_k_rsrq = liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_FC4]; + objects.clear(); + active.clear(); + reports_cfg.clear(); + phy->meas_reset(); +} + +/* L3 filtering 5.5.3.2 */ +void rrc::rrc_meas::L3_filter(meas_value_t *value, float values[NOF_MEASUREMENTS]) +{ + for (int i=0;ims[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::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::iterator obj = objects.begin(); obj != objects.end(); ++obj) { + if (obj->second.earfcn == earfcn) { + if (object) { + *object = &obj->second; + } + for (std::map::iterator c = obj->second.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::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::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::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::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::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::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::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;iN_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;imeas_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;iN_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;imeas_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;jblack_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;jN_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;iN_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;irep_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::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::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::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