diff --git a/srsue/hdr/phy/search.h b/srsue/hdr/phy/search.h new file mode 100644 index 000000000..b9768d868 --- /dev/null +++ b/srsue/hdr/phy/search.h @@ -0,0 +1,64 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_SEARCH_H +#define SRSUE_SEARCH_H + +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/radio/radio.h" +#include "srslte/srslte.h" + +namespace srsue { + +class search_callback +{ +public: + virtual int radio_recv_fnc(srslte::rf_buffer_t&, uint32_t nsamples, srslte_timestamp_t* rx_time) = 0; + virtual void set_ue_sync_opts(srslte_ue_sync_t* q, float cfo) = 0; + virtual srslte::radio_interface_phy* get_radio() = 0; + virtual void set_rx_gain(float gain) = 0; +}; + +// Class to run cell search +class search +{ +public: + typedef enum { CELL_NOT_FOUND, CELL_FOUND, ERROR, TIMEOUT } ret_code; + + ~search(); + void init(srslte::rf_buffer_t& buffer_, srslte::log* log_h, uint32_t nof_rx_channels, search_callback* parent); + void reset(); + float get_last_cfo(); + void set_agc_enable(bool enable); + ret_code run(srslte_cell_t* cell, std::array& bch_payload); + +private: + search_callback* p = nullptr; + srslte::log* log_h = nullptr; + srslte::rf_buffer_t buffer = {}; + srslte_ue_cellsearch_t cs = {}; + srslte_ue_mib_sync_t ue_mib_sync = {}; + int force_N_id_2 = 0; +}; + +}; // namespace srsue + +#endif // SRSUE_SEARCH_H diff --git a/srsue/hdr/phy/sfn_sync.h b/srsue/hdr/phy/sfn_sync.h new file mode 100644 index 000000000..a40c2ad4a --- /dev/null +++ b/srsue/hdr/phy/sfn_sync.h @@ -0,0 +1,71 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_SFN_SYNC_H +#define SRSUE_SFN_SYNC_H + +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/radio/radio.h" +#include "srslte/srslte.h" + +namespace srsue { + +// Class to synchronize system frame number +class sfn_sync +{ +public: + typedef enum { IDLE, SFN_FOUND, SFX0_FOUND, SFN_NOFOUND, ERROR } ret_code; + sfn_sync() = default; + ~sfn_sync(); + void init(srslte_ue_sync_t* ue_sync, + const phy_args_t* phy_args_, + srslte::rf_buffer_t& buffer, + uint32_t buffer_max_samples_, + srslte::log* log_h, + uint32_t nof_subframes = SFN_SYNC_NOF_SUBFRAMES); + void reset(); + bool set_cell(srslte_cell_t cell); + ret_code run_subframe(srslte_cell_t* cell, + uint32_t* tti_cnt, + std::array& bch_payload, + bool sfidx_only = false); + ret_code decode_mib(srslte_cell_t* cell, + uint32_t* tti_cnt, + srslte::rf_buffer_t* ext_buffer, + std::array& bch_payload, + bool sfidx_only = false); + +private: + const static int SFN_SYNC_NOF_SUBFRAMES = 100; + + const phy_args_t* phy_args = nullptr; + uint32_t cnt = 0; + uint32_t timeout = 0; + srslte::log* log_h = nullptr; + srslte_ue_sync_t* ue_sync = nullptr; + srslte::rf_buffer_t mib_buffer = {}; + uint32_t buffer_max_samples = 0; + srslte_ue_mib_t ue_mib = {}; +}; + +}; // namespace srsue + +#endif // SRSUE_SFN_SYNC_H diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index 01b1369a8..2f878a55a 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -26,29 +26,31 @@ #include #include #include -#include #include "phy_common.h" #include "prach.h" +#include "scell/intra_measure.h" +#include "search.h" #include "sf_worker.h" +#include "sfn_sync.h" #include "srslte/common/log.h" #include "srslte/common/thread_pool.h" #include "srslte/common/threads.h" #include "srslte/common/tti_sync_cv.h" #include "srslte/interfaces/radio_interfaces.h" #include "srslte/interfaces/ue_interfaces.h" +#include "srslte/phy/channel/channel.h" #include "srslte/srslte.h" - -#include +#include "sync_state.h" namespace srsue { typedef _Complex float cf_t; -class sync : public srslte::thread, public chest_feedback_itf +class sync : public srslte::thread, public chest_feedback_itf, public search_callback { public: - sync() : thread("SYNC"), sf_buffer(sync_nof_rx_subframes){}; + sync() : thread("SYNC"), sf_buffer(sync_nof_rx_subframes), dummy_buffer(sync_nof_rx_subframes){}; ~sync(); void init(srslte::radio_interface_phy* radio_, @@ -86,86 +88,70 @@ public: void force_freq(float dl_freq, float ul_freq); // Other functions - void set_rx_gain(float gain); - int radio_recv_fnc(srslte::rf_buffer_t&, uint32_t nsamples, srslte_timestamp_t* rx_time); + void set_rx_gain(float gain) override; + int radio_recv_fnc(srslte::rf_buffer_t&, uint32_t nsamples, srslte_timestamp_t* rx_time) override; + + srslte::radio_interface_phy* get_radio() override { return radio_h; } private: - // Class to run cell search - class search - { - public: - typedef enum { CELL_NOT_FOUND, CELL_FOUND, ERROR, TIMEOUT } ret_code; + void reset(); + void radio_error(); + void set_ue_sync_opts(srslte_ue_sync_t* q, float cfo) override; - ~search(); - void init(srslte::rf_buffer_t& buffer_, srslte::log* log_h, uint32_t nof_rx_channels, sync* parent); - void reset(); - float get_last_cfo(); - void set_agc_enable(bool enable); - ret_code run(srslte_cell_t* cell, std::array& bch_payload); - - private: - sync* p = nullptr; - srslte::log* log_h = nullptr; - srslte::rf_buffer_t buffer = {}; - srslte_ue_cellsearch_t cs = {}; - srslte_ue_mib_sync_t ue_mib_sync = {}; - int force_N_id_2 = 0; - }; - - // Class to synchronize system frame number - class sfn_sync - { - public: - typedef enum { IDLE, SFN_FOUND, SFX0_FOUND, SFN_NOFOUND, ERROR } ret_code; - sfn_sync() = default; - ~sfn_sync(); - void init(srslte_ue_sync_t* ue_sync, - const phy_args_t* phy_args_, - srslte::rf_buffer_t& buffer, - uint32_t buffer_max_samples_, - srslte::log* log_h, - uint32_t nof_subframes = SFN_SYNC_NOF_SUBFRAMES); - void reset(); - bool set_cell(srslte_cell_t cell); - ret_code run_subframe(srslte_cell_t* cell, - uint32_t* tti_cnt, - std::array& bch_payload, - bool sfidx_only = false); - ret_code decode_mib(srslte_cell_t* cell, - uint32_t* tti_cnt, - srslte::rf_buffer_t* ext_buffer, - std::array& bch_payload, - bool sfidx_only = false); - - private: - const static int SFN_SYNC_NOF_SUBFRAMES = 100; - - const phy_args_t* phy_args = nullptr; - uint32_t cnt = 0; - uint32_t timeout = 0; - srslte::log* log_h = nullptr; - srslte_ue_sync_t* ue_sync = nullptr; - srslte::rf_buffer_t mib_buffer = {}; - uint32_t buffer_max_samples = 0; - srslte_ue_mib_t ue_mib = {}; - }; - - /* 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 + /** + * Search for a cell in the current frequency and go to IDLE. + * The function search_p.run() will not return until the search finishes */ + void run_cell_search_state(); - void reset(); - void radio_error(); - void set_ue_sync_opts(srslte_ue_sync_t* q, float cfo); + /** + * SFN synchronization using MIB. run_subframe() receives and processes 1 subframe + * and returns + */ + void run_sfn_sync_state(); + + /** + * Cell camping state. Calls the PHCH workers to process subframes and maintains cell synchronization + */ + void run_camping_state(); + + /** + * Receives and discards received samples. Does not maintain synchronization + */ + void run_idle_state(); + + /** + * MAIN THREAD + * + * The main thread process the SYNC state machine. Every state except IDLE must have exclusive access to + * all variables. If any change of cell configuration must be done, the thread must be in IDLE. + * + * On each state except campling, 1 function is called and the thread jumps to the next state based on the output. + * + * It has 3 states: Cell search, SFN synchronization, initial measurement and camping. + * - CELL_SEARCH: Initial Cell id and MIB acquisition. Uses 1.92 MHz sampling rate + * - CELL_SYNC: Full sampling rate, uses MIB to obtain SFN. When SFN is obtained, moves to CELL_CAMP + * - CELL_CAMP: Cell camping state. Calls the PHCH workers to process subframes and maintains cell + * synchronization. + * - IDLE: Receives and discards received samples. Does not maintain synchronization. + * + */ void run_thread() final; + + /** + * Helper method, executed when the UE is camping and in-sync + * @param worker Selected worker for the current TTI + * @param sync_buffer Sub-frame buffer for the current TTI + */ + void run_camping_in_sync_state(sf_worker* worker, srslte::rf_buffer_t& sync_buffer); float get_tx_cfo(); void set_sampling_rate(); bool set_frequency(); bool set_cell(float cfo); - bool running = false; - bool is_overflow = false; + bool running = false; + bool is_overflow = false; bool forced_rx_time_init = true; // Rx time sync after first receive from radio @@ -174,8 +160,6 @@ private: sfn_sync sfn_p; std::vector > intra_freq_meas; - uint32_t current_sflen = 0; - // Pointers to other classes stack_interface_phy_lte* stack = nullptr; srslte::log* log_h = nullptr; @@ -186,12 +170,19 @@ private: prach* prach_buffer = nullptr; srslte::channel_ptr channel_emulator = nullptr; + // PRACH state + uint32_t prach_nof_sf = 0; + uint32_t prach_sf_cnt = 0; + cf_t* prach_ptr = nullptr; + float prach_power = 0; + // Object for synchronization of the primary cell srslte_ue_sync_t ue_sync = {}; // Buffer for primary and secondary cell samples const static uint32_t sync_nof_rx_subframes = 5; srslte::rf_buffer_t sf_buffer = {}; + srslte::rf_buffer_t dummy_buffer; // Sync metrics sync_metrics_t metrics = {}; @@ -200,133 +191,6 @@ private: uint32_t out_of_sync_cnt = 0; uint32_t in_sync_cnt = 0; - // State machine for SYNC thread - class sync_state - { - public: - typedef enum { - IDLE = 0, - CELL_SEARCH, - SFN_SYNC, - CAMPING, - } state_t; - - /* Run_state is called by the main thread at the start of each loop. It updates the state - * and returns the current state - */ - state_t run_state() - { - std::lock_guard lock(inside); - cur_state = next_state; - if (state_setting) { - state_setting = false; - state_running = true; - } - cvar.notify_all(); - return cur_state; - } - - // Called by the main thread at the end of each state to indicate it has finished. - void state_exit(bool exit_ok = true) - { - std::lock_guard lock(inside); - if (cur_state == SFN_SYNC && exit_ok == true) { - next_state = CAMPING; - } else { - next_state = IDLE; - } - state_running = false; - cvar.notify_all(); - } - void force_sfn_sync() - { - std::lock_guard lock(inside); - next_state = SFN_SYNC; - } - - /* Functions to be called from outside the STM thread to instruct the STM to switch state. - * The functions change the state and wait until it has changed it. - * - * These functions are mutexed and only 1 can be called at a time - */ - void go_idle() - { - std::lock_guard lock(outside); - go_state(IDLE); - } - void run_cell_search() - { - std::lock_guard lock(outside); - go_state(CELL_SEARCH); - wait_state_run(); - wait_state_next(); - } - void run_sfn_sync() - { - std::lock_guard lock(outside); - go_state(SFN_SYNC); - wait_state_run(); - wait_state_next(); - } - - /* Helpers below this */ - bool is_idle() { return cur_state == IDLE; } - bool is_camping() { return cur_state == CAMPING; } - - const char* to_string() - { - switch (cur_state) { - case IDLE: - return "IDLE"; - case CELL_SEARCH: - return "SEARCH"; - case SFN_SYNC: - return "SYNC"; - case CAMPING: - return "CAMPING"; - default: - return "UNKNOWN"; - } - } - - sync_state() = default; - - private: - void go_state(state_t s) - { - std::unique_lock ul(inside); - next_state = s; - state_setting = true; - while (state_setting) { - cvar.wait(ul); - } - } - - /* Waits until there is a call to set_state() and then run_state(). Returns when run_state() returns */ - void wait_state_run() - { - std::unique_lock ul(inside); - while (state_running) { - cvar.wait(ul); - } - } - void wait_state_next() - { - std::unique_lock ul(inside); - while (cur_state != next_state) { - cvar.wait(ul); - } - } - - bool state_running = false; - bool state_setting = false; - state_t cur_state = IDLE; - state_t next_state = IDLE; - std::mutex inside; - std::mutex outside; - std::condition_variable cvar; - }; - std::mutex rrc_mutex; sync_state phy_state; @@ -338,10 +202,11 @@ private: float current_srate = 0; // This is the primary cell - srslte_cell_t cell = {}; - uint32_t tti = 0; - srslte_timestamp_t tti_ts = {}; - std::array mib = {}; + srslte_cell_t cell = {}; + bool force_camping_sfn_sync = false; + uint32_t tti = 0; + srslte_timestamp_t tti_ts = {}; + std::array mib = {}; uint32_t nof_workers = 0; uint32_t nof_rf_channels = 0; diff --git a/srsue/hdr/phy/sync_state.h b/srsue/hdr/phy/sync_state.h new file mode 100644 index 000000000..920d26bca --- /dev/null +++ b/srsue/hdr/phy/sync_state.h @@ -0,0 +1,155 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_SYNC_STATE_H +#define SRSUE_SYNC_STATE_H + +namespace srsue { + +class sync_state +{ +public: + typedef enum { + IDLE = 0, + CELL_SEARCH, + SFN_SYNC, + CAMPING, + } state_t; + + /* Run_state is called by the main thread at the start of each loop. It updates the state + * and returns the current state + */ + state_t run_state() + { + std::lock_guard lock(inside); + cur_state = next_state; + if (state_setting) { + state_setting = false; + state_running = true; + } + cvar.notify_all(); + return cur_state; + } + + // Called by the main thread at the end of each state to indicate it has finished. + void state_exit(bool exit_ok = true) + { + std::lock_guard lock(inside); + if (cur_state == SFN_SYNC && exit_ok == true) { + next_state = CAMPING; + } else { + next_state = IDLE; + } + state_running = false; + cvar.notify_all(); + } + void force_sfn_sync() + { + std::lock_guard lock(inside); + next_state = SFN_SYNC; + } + + /* Functions to be called from outside the STM thread to instruct the STM to switch state. + * The functions change the state and wait until it has changed it. + * + * These functions are mutexed and only 1 can be called at a time + */ + void go_idle() + { + std::lock_guard lock(outside); + go_state(IDLE); + } + void run_cell_search() + { + std::lock_guard lock(outside); + go_state(CELL_SEARCH); + wait_state_run(); + wait_state_next(); + } + void run_sfn_sync() + { + std::lock_guard lock(outside); + go_state(SFN_SYNC); + wait_state_run(); + wait_state_next(); + } + + /* Helpers below this */ + bool is_idle() { return cur_state == IDLE; } + bool is_camping() { return cur_state == CAMPING; } + + const char* to_string() + { + switch (cur_state) { + case IDLE: + return "IDLE"; + case CELL_SEARCH: + return "SEARCH"; + case SFN_SYNC: + return "SYNC"; + case CAMPING: + return "CAMPING"; + default: + return "UNKNOWN"; + } + } + + sync_state() = default; + +private: + void go_state(state_t s) + { + std::unique_lock ul(inside); + next_state = s; + state_setting = true; + while (state_setting) { + cvar.wait(ul); + } + } + + /* Waits until there is a call to set_state() and then run_state(). Returns when run_state() returns */ + void wait_state_run() + { + std::unique_lock ul(inside); + while (state_running) { + cvar.wait(ul); + } + } + void wait_state_next() + { + std::unique_lock ul(inside); + while (cur_state != next_state) { + cvar.wait(ul); + } + } + + bool state_running = false; + bool state_setting = false; + state_t cur_state = IDLE; + state_t next_state = IDLE; + std::mutex inside; + std::mutex outside; + std::condition_variable cvar; +}; + +}; // namespace srsue + +#endif // SRSUE_SYNC_STATE_H diff --git a/srsue/src/phy/search.cc b/srsue/src/phy/search.cc new file mode 100644 index 000000000..ba5c39d44 --- /dev/null +++ b/srsue/src/phy/search.cc @@ -0,0 +1,200 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/phy/search.h" + +#define Error(fmt, ...) \ + if (SRSLTE_DEBUG_ENABLED) \ + log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) \ + if (SRSLTE_DEBUG_ENABLED) \ + log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) \ + if (SRSLTE_DEBUG_ENABLED) \ + log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) \ + if (SRSLTE_DEBUG_ENABLED) \ + log_h->debug(fmt, ##__VA_ARGS__) + +namespace srsue { + +static int +radio_recv_callback(void* obj, cf_t* data[SRSLTE_MAX_CHANNELS], uint32_t nsamples, srslte_timestamp_t* rx_time) +{ + srslte::rf_buffer_t x(data); + return ((search_callback*)obj)->radio_recv_fnc(x, nsamples, rx_time); +} + +static SRSLTE_AGC_CALLBACK(callback_set_rx_gain) +{ + ((search_callback*)h)->set_rx_gain(gain_db); +} + +search::~search() +{ + srslte_ue_mib_sync_free(&ue_mib_sync); + srslte_ue_cellsearch_free(&cs); +} + +void search::init(srslte::rf_buffer_t& buffer_, srslte::log* log_h_, uint32_t nof_rx_channels, search_callback* parent) +{ + log_h = log_h_; + p = parent; + + buffer = buffer_; + + if (srslte_ue_cellsearch_init_multi(&cs, 8, radio_recv_callback, nof_rx_channels, parent)) { + Error("SYNC: Initiating UE cell search\n"); + } + srslte_ue_cellsearch_set_nof_valid_frames(&cs, 4); + + if (srslte_ue_mib_sync_init_multi(&ue_mib_sync, radio_recv_callback, nof_rx_channels, parent)) { + Error("SYNC: Initiating UE MIB synchronization\n"); + } + + // Set options defined in expert section + p->set_ue_sync_opts(&cs.ue_sync, 0); + + force_N_id_2 = -1; +} + +void search::reset() +{ + srslte_ue_sync_reset(&ue_mib_sync.ue_sync); +} + +float search::get_last_cfo() +{ + return srslte_ue_sync_get_cfo(&ue_mib_sync.ue_sync); +} + +void search::set_agc_enable(bool enable) +{ + if (enable) { + srslte_rf_info_t* rf_info = p->get_radio()->get_info(); + srslte_ue_sync_start_agc(&ue_mib_sync.ue_sync, + callback_set_rx_gain, + rf_info->min_rx_gain, + rf_info->max_rx_gain, + p->get_radio()->get_rx_gain()); + } else { + ERROR("Error stop AGC not implemented\n"); + } +} + +search::ret_code search::run(srslte_cell_t* cell_, std::array& bch_payload) +{ + srslte_cell_t new_cell = {}; + + srslte_ue_cellsearch_result_t found_cells[3]; + + bzero(found_cells, 3 * sizeof(srslte_ue_cellsearch_result_t)); + + /* Find a cell in the given N_id_2 or go through the 3 of them to find the strongest */ + uint32_t max_peak_cell = 0; + int ret = SRSLTE_ERROR; + + Info("SYNC: Searching for cell...\n"); + log_h->console("."); + + if (force_N_id_2 >= 0 && force_N_id_2 < 3) { + ret = srslte_ue_cellsearch_scan_N_id_2(&cs, force_N_id_2, &found_cells[force_N_id_2]); + max_peak_cell = force_N_id_2; + } else { + ret = srslte_ue_cellsearch_scan(&cs, found_cells, &max_peak_cell); + } + + if (ret < 0) { + Error("SYNC: Error decoding MIB: Error searching PSS\n"); + return ERROR; + } else if (ret == 0) { + Info("SYNC: Could not find any cell in this frequency\n"); + return CELL_NOT_FOUND; + } + // Save result + new_cell.id = found_cells[max_peak_cell].cell_id; + new_cell.cp = found_cells[max_peak_cell].cp; + new_cell.frame_type = found_cells[max_peak_cell].frame_type; + float cfo = found_cells[max_peak_cell].cfo; + + log_h->console("\n"); + Info("SYNC: PSS/SSS detected: Mode=%s, PCI=%d, CFO=%.1f KHz, CP=%s\n", + new_cell.frame_type ? "TDD" : "FDD", + new_cell.id, + cfo / 1000, + srslte_cp_string(new_cell.cp)); + + if (srslte_ue_mib_sync_set_cell(&ue_mib_sync, new_cell)) { + Error("SYNC: Setting UE MIB cell\n"); + return ERROR; + } + + // Set options defined in expert section + p->set_ue_sync_opts(&ue_mib_sync.ue_sync, cfo); + + srslte_ue_sync_reset(&ue_mib_sync.ue_sync); + + /* Find and decode MIB */ + int sfn_offset; + ret = srslte_ue_mib_sync_decode(&ue_mib_sync, 40, bch_payload.data(), &new_cell.nof_ports, &sfn_offset); + if (ret == 1) { + srslte_pbch_mib_unpack(bch_payload.data(), &new_cell, NULL); + // pack MIB and store inplace for PCAP dump + std::array mib_packed; + srslte_bit_pack_vector(bch_payload.data(), mib_packed.data(), SRSLTE_BCH_PAYLOAD_LEN); + std::copy(std::begin(mib_packed), std::end(mib_packed), std::begin(bch_payload)); + + fprintf(stdout, + "Found Cell: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n", + new_cell.frame_type ? "TDD" : "FDD", + new_cell.id, + new_cell.nof_prb, + new_cell.nof_ports, + cfo / 1000); + + Info("SYNC: MIB Decoded: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n", + new_cell.frame_type ? "TDD" : "FDD", + new_cell.id, + new_cell.nof_prb, + new_cell.nof_ports, + cfo / 1000); + + if (!srslte_cell_isvalid(&new_cell)) { + Error("SYNC: Detected invalid cell.\n"); + return CELL_NOT_FOUND; + } + + // Save cell pointer + if (cell_) { + *cell_ = new_cell; + } + + return CELL_FOUND; + } else if (ret == 0) { + Warning("SYNC: Found PSS but could not decode PBCH\n"); + return CELL_NOT_FOUND; + } else { + Error("SYNC: Receiving MIB\n"); + return ERROR; + } +} + +}; // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/sfn_sync.cc b/srsue/src/phy/sfn_sync.cc new file mode 100644 index 000000000..5cfd399b0 --- /dev/null +++ b/srsue/src/phy/sfn_sync.cc @@ -0,0 +1,167 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/phy/sfn_sync.h" + +#define Error(fmt, ...) \ + if (SRSLTE_DEBUG_ENABLED) \ + log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) \ + if (SRSLTE_DEBUG_ENABLED) \ + log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) \ + if (SRSLTE_DEBUG_ENABLED) \ + log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) \ + if (SRSLTE_DEBUG_ENABLED) \ + log_h->debug(fmt, ##__VA_ARGS__) + +namespace srsue { + +sfn_sync::~sfn_sync() +{ + srslte_ue_mib_free(&ue_mib); +} + +void sfn_sync::init(srslte_ue_sync_t* ue_sync_, + const phy_args_t* phy_args_, + srslte::rf_buffer_t& buffer, + uint32_t buffer_max_samples_, + srslte::log* log_h_, + uint32_t nof_subframes) +{ + log_h = log_h_; + ue_sync = ue_sync_; + phy_args = phy_args_; + timeout = nof_subframes; + + mib_buffer = buffer; + buffer_max_samples = buffer_max_samples_; + + // MIB decoder uses a single receiver antenna in logical channel 0 + if (srslte_ue_mib_init(&ue_mib, buffer.get(0), SRSLTE_MAX_PRB)) { + Error("SYNC: Initiating UE MIB decoder\n"); + } +} + +bool sfn_sync::set_cell(srslte_cell_t cell_) +{ + if (srslte_ue_mib_set_cell(&ue_mib, cell_)) { + Error("SYNC: Setting cell: initiating ue_mib\n"); + return false; + } + reset(); + return true; +} + +void sfn_sync::reset() +{ + cnt = 0; + srslte_ue_mib_reset(&ue_mib); +} + +sfn_sync::ret_code sfn_sync::run_subframe(srslte_cell_t* cell_, + uint32_t* tti_cnt, + std::array& bch_payload, + bool sfidx_only) +{ + int ret = srslte_ue_sync_zerocopy(ue_sync, mib_buffer.to_cf_t(), buffer_max_samples); + if (ret < 0) { + Error("SYNC: Error calling ue_sync_get_buffer.\n"); + return ERROR; + } + + if (ret == 1) { + sfn_sync::ret_code ret2 = decode_mib(cell_, tti_cnt, nullptr, bch_payload, sfidx_only); + if (ret2 != SFN_NOFOUND) { + return ret2; + } + } else { + Info("SYNC: Waiting for PSS while trying to decode MIB (%d/%d)\n", cnt, timeout); + } + + cnt++; + if (cnt >= timeout) { + cnt = 0; + return SFN_NOFOUND; + } + + return IDLE; +} + +sfn_sync::ret_code sfn_sync::decode_mib(srslte_cell_t* cell_, + uint32_t* tti_cnt, + srslte::rf_buffer_t* ext_buffer, + std::array& bch_payload, + bool sfidx_only) +{ + // If external buffer provided not equal to internal buffer, copy samples from channel/port 0 + if (ext_buffer != nullptr) { + memcpy(mib_buffer.get(0), ext_buffer->get(0), sizeof(cf_t) * ue_sync->sf_len); + } + + 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 n = srslte_ue_mib_decode(&ue_mib, bch_payload.data(), NULL, &sfn_offset); + switch (n) { + default: + Error("SYNC: Error decoding MIB while synchronising SFN"); + return ERROR; + case SRSLTE_UE_MIB_FOUND: + uint32_t sfn; + srslte_pbch_mib_unpack(bch_payload.data(), cell_, &sfn); + + sfn = (sfn + sfn_offset) % 1024; + if (tti_cnt) { + *tti_cnt = 10 * sfn; + + // Check if SNR is below the minimum threshold + if (ue_mib.chest_res.snr_db < phy_args->in_sync_snr_db_th) { + Info("SYNC: MIB decoded, SNR is too low (%+.1f < %+.1f)\n", + ue_mib.chest_res.snr_db, + phy_args->in_sync_snr_db_th); + return SFN_NOFOUND; + } + + Info("SYNC: DONE, SNR=%.1f dB, TTI=%d, sfn_offset=%d\n", ue_mib.chest_res.snr_db, *tti_cnt, sfn_offset); + } + + reset(); + return SFN_FOUND; + case SRSLTE_UE_MIB_NOTFOUND: + Info("SYNC: Found PSS but could not decode MIB. SNR=%.1f dB (%d/%d)\n", ue_mib.chest_res.snr_db, cnt, timeout); + return SFN_NOFOUND; + } + } + + return IDLE; +} + +}; // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index ade2ae2d6..6deef6c02 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -78,7 +78,8 @@ void sync::init(srslte::radio_interface_phy* _radio, return; } - if (srslte_ue_sync_init_multi(&ue_sync, SRSLTE_MAX_PRB, false, radio_recv_callback, nof_rf_channels, this)) { + if (srslte_ue_sync_init_multi(&ue_sync, SRSLTE_MAX_PRB, false, radio_recv_callback, nof_rf_channels, this) != + SRSLTE_SUCCESS) { Error("SYNC: Initiating ue_sync\n"); return; } @@ -98,7 +99,7 @@ void sync::init(srslte::radio_interface_phy* _radio, // Start intra-frequency measurement for (uint32_t i = 0; i < worker_com->args->nof_carriers; i++) { - auto q = new scell::intra_measure; + scell::intra_measure* q = new scell::intra_measure; q->init(worker_com, stack, log_h); intra_freq_meas.push_back(std::unique_ptr(q)); } @@ -134,10 +135,10 @@ void sync::stop() void sync::reset() { - in_sync_cnt = 0; - out_of_sync_cnt = 0; - current_earfcn = -1; - srate_mode = SRATE_NONE; + in_sync_cnt = 0; + out_of_sync_cnt = 0; + current_earfcn = -1; + srate_mode = SRATE_NONE; sfn_p.reset(); search_p.reset(); } @@ -181,8 +182,8 @@ phy_interface_rrc_lte::cell_search_ret_t sync::cell_search(phy_interface_rrc_lte std::unique_lock ul(rrc_mutex); phy_interface_rrc_lte::cell_search_ret_t ret = {}; - ret.found = phy_interface_rrc_lte::cell_search_ret_t::ERROR; - ret.last_freq = phy_interface_rrc_lte::cell_search_ret_t::NO_MORE_FREQS; + ret.found = phy_interface_rrc_lte::cell_search_ret_t::ERROR; + ret.last_freq = phy_interface_rrc_lte::cell_search_ret_t::NO_MORE_FREQS; // Move state to IDLE Info("Cell Search: Start EARFCN index=%u/%zd\n", cellsearch_earfcn_index, worker_com->args->dl_earfcn_list.size()); @@ -309,13 +310,13 @@ bool sync::cell_select(const phy_interface_rrc_lte::phy_cell_t* new_cell) intra_freq_meas[0]->set_primary_cell(current_earfcn, cell); } - /* Change sampling rate if necessary */ + // Change sampling rate if necessary if (srate_mode != SRATE_CAMP) { log_h->info("Cell Select: Setting CAMPING sampling rate\n"); set_sampling_rate(); } - /* SFN synchronization */ + // SFN synchronization phy_state.run_sfn_sync(); if (phy_state.is_camping()) { Info("Cell Select: SFN synchronized. CAMPING...\n"); @@ -333,37 +334,196 @@ bool sync::cell_is_camping() return phy_state.is_camping(); } -/** - * MAIN THREAD - * - * The main thread process the SYNC state machine. Every state except IDLE must have exclusive access to - * all variables. If any change of cell configuration must be done, the thread must be in IDLE. - * - * On each state except campling, 1 function is called and the thread jumps to the next state based on the output. - * - * It has 3 states: Cell search, SFN synchronization, initial measurement and camping. - * - CELL_SEARCH: Initial Cell id and MIB acquisition. Uses 1.92 MHz sampling rate - * - CELL_SYNC: Full sampling rate, uses MIB to obtain SFN. When SFN is obtained, moves to CELL_CAMP - * - CELL_CAMP: Cell camping state. Calls the PHCH workers to process subframes and maintains cell synchronization. - * - IDLE: Receives and discards received samples. Does not maintain synchronization. - * - */ +void sync::run_cell_search_state() +{ + cell_search_ret = search_p.run(&cell, mib); + if (cell_search_ret == search::CELL_FOUND) { + stack->bch_decoded_ok(SYNC_CC_IDX, mib.data(), mib.size() / 8); + } + phy_state.state_exit(); +} + +void sync::run_sfn_sync_state() +{ + srslte_cell_t temp_cell = cell; + switch (sfn_p.run_subframe(&temp_cell, &tti, mib)) { + case sfn_sync::SFN_FOUND: + if (memcmp(&cell, &temp_cell, sizeof(srslte_cell_t)) != 0) { + srslte_cell_fprint(stdout, &cell, 0); + srslte_cell_fprint(stdout, &temp_cell, 0); + log_h->error("Detected cell during SFN synchronization differs from configured cell. Cell reselection to " + "cells with different MIB is not supported\n"); + log_h->console("Detected cell during SFN synchronization differs from configured cell. Cell reselection " + "to cells with different MIB is not supported\n"); + phy_state.state_exit(false); + } + stack->in_sync(); + phy_state.state_exit(); + break; + case sfn_sync::IDLE: + break; + default: + phy_state.state_exit(false); + break; + } +} + +void sync::run_camping_in_sync_state(sf_worker* worker, srslte::rf_buffer_t& sync_buffer) +{ + + // Check tti is synched with ue_sync + if (srslte_ue_sync_get_sfidx(&ue_sync) != tti % 10) { + uint32_t sfn = tti / 10; + tti = (sfn * 10 + srslte_ue_sync_get_sfidx(&ue_sync)) % 10240; + + // Force SFN decode, just in case it is in the wrong frame + force_camping_sfn_sync = true; + } + + if (is_overflow) { + force_camping_sfn_sync = true; + is_overflow = false; + log_h->info("Detected overflow, trying to resync SFN\n"); + } + + // Force decode MIB if required + if (force_camping_sfn_sync) { + uint32_t _tti = 0; + srslte_cell_t temp_cell = cell; + sfn_sync::ret_code ret = sfn_p.decode_mib(&temp_cell, &_tti, &sync_buffer, mib); + + if (ret == sfn_sync::SFN_FOUND) { + // Force tti + tti = _tti; + + // Disable + force_camping_sfn_sync = false; + + if (memcmp(&cell, &temp_cell, sizeof(srslte_cell_t)) != 0) { + log_h->error("Detected cell during SFN synchronization differs from configured cell. Cell " + "reselection to cells with different MIB is not supported\n"); + log_h->console("Detected cell during SFN synchronization differs from configured cell. Cell " + "reselection to cells with different MIB is not supported\n"); + } else { + log_h->info("SFN resynchronized successfully\n"); + } + } else { + log_h->warning("SFN not yet synchronized, sending out-of-sync\n"); + } + } + + Debug("SYNC: Worker %d synchronized\n", worker->get_id()); + + metrics.sfo = srslte_ue_sync_get_sfo(&ue_sync); + metrics.cfo = srslte_ue_sync_get_cfo(&ue_sync); + metrics.ta_us = worker_com->ta.get_usec(); + for (uint32_t i = 0; i < worker_com->args->nof_carriers; i++) { + worker_com->set_sync_metrics(i, metrics); + } + + // Check if we need to TX a PRACH + if (prach_buffer->is_ready_to_send(tti)) { + prach_ptr = prach_buffer->generate(get_tx_cfo(), &prach_nof_sf, &prach_power); + if (prach_ptr == nullptr) { + Error("Generating PRACH\n"); + } + } + + // Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time + srslte_timestamp_t tx_time; + srslte_ue_sync_get_last_timestamp(&ue_sync, &tx_time); // Get Rx Timestamp + srslte_timestamp_add(&tx_time, 0, FDD_HARQ_DELAY_DL_MS * 1e-3); // Add Tx delay + + worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSLTE_SF_LEN_PRB(cell.nof_prb)] : nullptr, prach_power); + + // Set CFO for all Carriers + for (uint32_t cc = 0; cc < worker_com->args->nof_carriers; cc++) { + worker->set_cfo(cc, get_tx_cfo()); + worker_com->avg_cfo_hz[cc] = srslte_ue_sync_get_cfo(&ue_sync); + } + + worker->set_tti(tti); + worker->set_tx_time(tx_time); + + // Advance/reset prach subframe pointer + if (prach_ptr) { + prach_sf_cnt++; + if (prach_sf_cnt == prach_nof_sf) { + prach_sf_cnt = 0; + prach_ptr = nullptr; + } + } + + // Start worker + worker_com->semaphore.push(worker); + workers_pool->start_worker(worker); +} +void sync::run_camping_state() +{ + sf_worker* worker = (sf_worker*)workers_pool->wait_worker(tti); + srslte::rf_buffer_t sync_buffer = {}; + + if (worker == nullptr) { + // wait_worker() only returns NULL if it's being closed. Quit now to avoid unnecessary loops here + running = false; + return; + } + + // Map carrier/antenna buffers to worker buffers + for (uint32_t c = 0; c < worker_com->args->nof_carriers; c++) { + for (uint32_t i = 0; i < worker_com->args->nof_rx_ant; i++) { + sync_buffer.set(c, i, worker_com->args->nof_rx_ant, worker->get_buffer(c, i)); + } + } + + // Primary Cell (PCell) Synchronization + switch (srslte_ue_sync_zerocopy(&ue_sync, sync_buffer.to_cf_t(), worker->get_buffer_len())) { + case 1: + run_camping_in_sync_state(worker, sync_buffer); + break; + case 0: + Warning("SYNC: Out-of-sync detected in PSS/SSS\n"); + out_of_sync(); + worker->release(); + + // Force decoding MIB, for making sure that the TTI will be right + if (!force_camping_sfn_sync) { + force_camping_sfn_sync = true; + } + + break; + default: + radio_error(); + worker->release(); + break; + } +} + +void sync::run_idle_state() +{ + if (radio_h->is_init()) { + uint32_t nsamples = 1920; + if (current_srate > 0) { + nsamples = current_srate / 1000; + } + Debug("Discarding %d samples\n", nsamples); + srslte_timestamp_t rx_time = {}; + if (radio_recv_fnc(dummy_buffer, nsamples, &rx_time) == SRSLTE_SUCCESS) { + log_h->console("SYNC: Receiving from radio while in IDLE_RX\n"); + } + // If radio is in locked state returns immediately. In that case, do a 1 ms sleep + if (rx_time.frac_secs == 0 && rx_time.full_secs == 0) { + usleep(1000); + } + radio_h->tx_end(); + } else { + Debug("Sleeping\n"); + usleep(1000); + } +} void sync::run_thread() { - sf_worker* worker = nullptr; - srslte_cell_t temp_cell = {}; - - bool force_camping_sfn_sync = false; - - srslte::rf_buffer_t dummy_buffer(sync_nof_rx_subframes); - srslte::rf_buffer_t sync_buffer = {}; - - uint32_t prach_nof_sf = 0; - uint32_t prach_sf_cnt = 0; - cf_t* prach_ptr = NULL; - float prach_power = 0; - while (running) { Debug("SYNC: state=%s, tti=%d\n", phy_state.to_string(), tti); @@ -378,191 +538,21 @@ void sync::run_thread() switch (phy_state.run_state()) { case sync_state::CELL_SEARCH: - /* Search for a cell in the current frequency and go to IDLE. - * The function search_p.run() will not return until the search finishes - */ - cell_search_ret = search_p.run(&cell, mib); - if (cell_search_ret == search::CELL_FOUND) { - stack->bch_decoded_ok(SYNC_CC_IDX, mib.data(), mib.size() / 8); - } - phy_state.state_exit(); + run_cell_search_state(); break; case sync_state::SFN_SYNC: - - /* SFN synchronization using MIB. run_subframe() receives and processes 1 subframe - * and returns - */ - temp_cell = cell; - switch (sfn_p.run_subframe(&temp_cell, &tti, mib)) { - case sfn_sync::SFN_FOUND: - if (memcmp(&cell, &temp_cell, sizeof(srslte_cell_t))) { - srslte_cell_fprint(stdout, &cell, 0); - srslte_cell_fprint(stdout, &temp_cell, 0); - log_h->error("Detected cell during SFN synchronization differs from configured cell. Cell reselection to " - "cells with different MIB is not supported\n"); - log_h->console("Detected cell during SFN synchronization differs from configured cell. Cell reselection " - "to cells with different MIB is not supported\n"); - phy_state.state_exit(false); - } - stack->in_sync(); - phy_state.state_exit(); - break; - case sfn_sync::IDLE: - break; - default: - phy_state.state_exit(false); - break; - } + run_sfn_sync_state(); break; case sync_state::CAMPING: - - worker = (sf_worker*)workers_pool->wait_worker(tti); - if (worker) { - // Map carrier/antenna buffers to worker buffers - for (uint32_t c = 0; c < worker_com->args->nof_carriers; c++) { - for (uint32_t i = 0; i < worker_com->args->nof_rx_ant; i++) { - sync_buffer.set(c, i, worker_com->args->nof_rx_ant, worker->get_buffer(c, i)); - } - } - - // Primary Cell (PCell) Synchronization - switch (srslte_ue_sync_zerocopy(&ue_sync, sync_buffer.to_cf_t(), worker->get_buffer_len())) { - case 1: - - // Check tti is synched with ue_sync - if (srslte_ue_sync_get_sfidx(&ue_sync) != tti % 10) { - uint32_t sfn = tti / 10; - tti = (sfn * 10 + srslte_ue_sync_get_sfidx(&ue_sync)) % 10240; - - // Force SFN decode, just in case it is in the wrong frame - force_camping_sfn_sync = true; - } - - if (is_overflow) { - force_camping_sfn_sync = true; - is_overflow = false; - log_h->info("Detected overflow, trying to resync SFN\n"); - } - - // Force decode MIB if required - if (force_camping_sfn_sync) { - uint32_t _tti = 0; - temp_cell = cell; - sync::sfn_sync::ret_code ret = sfn_p.decode_mib(&temp_cell, &_tti, &sync_buffer, mib); - if (ret == sfn_sync::SFN_FOUND) { - // Force tti - tti = _tti; - - // Disable - force_camping_sfn_sync = false; - - if (memcmp(&cell, &temp_cell, sizeof(srslte_cell_t))) { - log_h->error("Detected cell during SFN synchronization differs from configured cell. Cell " - "reselection to cells with different MIB is not supported\n"); - log_h->console("Detected cell during SFN synchronization differs from configured cell. Cell " - "reselection to cells with different MIB is not supported\n"); - } else { - log_h->info("SFN resynchronized successfully\n"); - } - } else { - log_h->warning("SFN not yet synchronized, sending out-of-sync\n"); - } - } - - Debug("SYNC: Worker %d synchronized\n", worker->get_id()); - - metrics.sfo = srslte_ue_sync_get_sfo(&ue_sync); - metrics.cfo = srslte_ue_sync_get_cfo(&ue_sync); - metrics.ta_us = worker_com->ta.get_usec(); - for (uint32_t i = 0; i < worker_com->args->nof_carriers; i++) { - worker_com->set_sync_metrics(i, metrics); - } - - // Check if we need to TX a PRACH - if (prach_buffer->is_ready_to_send(tti)) { - prach_ptr = prach_buffer->generate(get_tx_cfo(), &prach_nof_sf, &prach_power); - if (!prach_ptr) { - Error("Generating PRACH\n"); - } - } - - /* Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time */ - srslte_timestamp_t rx_time, tx_time; - srslte_ue_sync_get_last_timestamp(&ue_sync, &rx_time); - srslte_timestamp_copy(&tx_time, &rx_time); - srslte_timestamp_add(&tx_time, 0, FDD_HARQ_DELAY_DL_MS * 1e-3); - - worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSLTE_SF_LEN_PRB(cell.nof_prb)] : nullptr, - prach_power); - - // Set CFO for all Carriers - for (uint32_t cc = 0; cc < worker_com->args->nof_carriers; cc++) { - worker->set_cfo(cc, get_tx_cfo()); - worker_com->avg_cfo_hz[cc] = srslte_ue_sync_get_cfo(&ue_sync); - } - - worker->set_tti(tti); - worker->set_tx_time(tx_time); - - // Advance/reset prach subframe pointer - if (prach_ptr) { - prach_sf_cnt++; - if (prach_sf_cnt == prach_nof_sf) { - prach_sf_cnt = 0; - prach_ptr = nullptr; - } - } - - // Start worker - worker_com->semaphore.push(worker); - workers_pool->start_worker(worker); - - break; - case 0: - Warning("SYNC: Out-of-sync detected in PSS/SSS\n"); - out_of_sync(); - worker->release(); - - // Force decoding MIB, for making sure that the TTI will be right - if (!force_camping_sfn_sync) { - force_camping_sfn_sync = true; - } - - break; - default: - radio_error(); - break; - } - } else { - // wait_worker() only returns NULL if it's being closed. Quit now to avoid unnecessary loops here - running = false; - } + run_camping_state(); break; case sync_state::IDLE: - if (radio_h->is_init()) { - uint32_t nsamples = 1920; - if (current_srate > 0) { - nsamples = current_srate / 1000; - } - Debug("Discarding %d samples\n", nsamples); - srslte_timestamp_t rx_time = {}; - if (!radio_recv_fnc(dummy_buffer, nsamples, &rx_time)) { - log_h->console("SYNC: Receiving from radio while in IDLE_RX\n"); - } - // If radio is in locked state returns immediately. In that case, do a 1 ms sleep - if (rx_time.frac_secs == 0 && rx_time.full_secs == 0) { - usleep(1000); - } - radio_h->tx_end(); - } else { - Debug("Sleeping\n"); - usleep(1000); - } + run_idle_state(); break; } // Increase TTI counter - tti = (tti + 1) % 10240; + tti = TTI_ADD(tti, 1); } } @@ -786,7 +776,6 @@ void sync::set_sampling_rate() return; } - current_sflen = (uint32_t)SRSLTE_SF_LEN_PRB(cell.nof_prb); if (current_srate != new_srate || srate_mode != SRATE_CAMP) { current_srate = new_srate; Info("SYNC: Setting sampling rate %.2f MHz\n", current_srate / 1000000); @@ -887,290 +876,6 @@ void sync::set_rx_gain(float gain) radio_h->set_rx_gain_th(gain); } -/********* - * Cell search class - */ -sync::search::~search() -{ - srslte_ue_mib_sync_free(&ue_mib_sync); - srslte_ue_cellsearch_free(&cs); -} - -void sync::search::init(srslte::rf_buffer_t& buffer_, srslte::log* log_h_, uint32_t nof_rx_channels, sync* parent) -{ - log_h = log_h_; - p = parent; - - buffer = buffer_; - - if (srslte_ue_cellsearch_init_multi(&cs, 8, radio_recv_callback, nof_rx_channels, parent)) { - Error("SYNC: Initiating UE cell search\n"); - } - srslte_ue_cellsearch_set_nof_valid_frames(&cs, 4); - - if (srslte_ue_mib_sync_init_multi(&ue_mib_sync, radio_recv_callback, nof_rx_channels, parent)) { - Error("SYNC: Initiating UE MIB synchronization\n"); - } - - // Set options defined in expert section - p->set_ue_sync_opts(&cs.ue_sync, 0); - - force_N_id_2 = -1; -} - -void sync::search::reset() -{ - srslte_ue_sync_reset(&ue_mib_sync.ue_sync); -} - -float sync::search::get_last_cfo() -{ - return srslte_ue_sync_get_cfo(&ue_mib_sync.ue_sync); -} - -void sync::search::set_agc_enable(bool enable) -{ - if (enable) { - srslte_rf_info_t* rf_info = p->radio_h->get_info(); - srslte_ue_sync_start_agc(&ue_mib_sync.ue_sync, - callback_set_rx_gain, - rf_info->min_rx_gain, - rf_info->max_rx_gain, - p->radio_h->get_rx_gain()); - } else { - ERROR("Error stop AGC not implemented\n"); - } -} - -sync::search::ret_code sync::search::run(srslte_cell_t* cell_, std::array& bch_payload) -{ - srslte_cell_t new_cell = {}; - - srslte_ue_cellsearch_result_t found_cells[3]; - - bzero(found_cells, 3 * sizeof(srslte_ue_cellsearch_result_t)); - - /* Find a cell in the given N_id_2 or go through the 3 of them to find the strongest */ - uint32_t max_peak_cell = 0; - int ret = SRSLTE_ERROR; - - Info("SYNC: Searching for cell...\n"); - log_h->console("."); - - if (force_N_id_2 >= 0 && force_N_id_2 < 3) { - ret = srslte_ue_cellsearch_scan_N_id_2(&cs, force_N_id_2, &found_cells[force_N_id_2]); - max_peak_cell = force_N_id_2; - } else { - ret = srslte_ue_cellsearch_scan(&cs, found_cells, &max_peak_cell); - } - - if (ret < 0) { - Error("SYNC: Error decoding MIB: Error searching PSS\n"); - return ERROR; - } else if (ret == 0) { - Info("SYNC: Could not find any cell in this frequency\n"); - return CELL_NOT_FOUND; - } - // Save result - new_cell.id = found_cells[max_peak_cell].cell_id; - new_cell.cp = found_cells[max_peak_cell].cp; - new_cell.frame_type = found_cells[max_peak_cell].frame_type; - float cfo = found_cells[max_peak_cell].cfo; - - log_h->console("\n"); - Info("SYNC: PSS/SSS detected: Mode=%s, PCI=%d, CFO=%.1f KHz, CP=%s\n", - new_cell.frame_type ? "TDD" : "FDD", - new_cell.id, - cfo / 1000, - srslte_cp_string(new_cell.cp)); - - if (srslte_ue_mib_sync_set_cell(&ue_mib_sync, new_cell)) { - Error("SYNC: Setting UE MIB cell\n"); - return ERROR; - } - - // Set options defined in expert section - p->set_ue_sync_opts(&ue_mib_sync.ue_sync, cfo); - - srslte_ue_sync_reset(&ue_mib_sync.ue_sync); - - /* Find and decode MIB */ - int sfn_offset; - ret = srslte_ue_mib_sync_decode(&ue_mib_sync, 40, bch_payload.data(), &new_cell.nof_ports, &sfn_offset); - if (ret == 1) { - srslte_pbch_mib_unpack(bch_payload.data(), &new_cell, NULL); - // pack MIB and store inplace for PCAP dump - std::array mib_packed; - srslte_bit_pack_vector(bch_payload.data(), mib_packed.data(), SRSLTE_BCH_PAYLOAD_LEN); - std::copy(std::begin(mib_packed), std::end(mib_packed), std::begin(bch_payload)); - - fprintf(stdout, - "Found Cell: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n", - new_cell.frame_type ? "TDD" : "FDD", - new_cell.id, - new_cell.nof_prb, - new_cell.nof_ports, - cfo / 1000); - - Info("SYNC: MIB Decoded: Mode=%s, PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n", - new_cell.frame_type ? "TDD" : "FDD", - new_cell.id, - new_cell.nof_prb, - new_cell.nof_ports, - cfo / 1000); - - if (!srslte_cell_isvalid(&new_cell)) { - Error("SYNC: Detected invalid cell.\n"); - return CELL_NOT_FOUND; - } - - // Save cell pointer - if (cell_) { - *cell_ = new_cell; - } - - return CELL_FOUND; - } else if (ret == 0) { - Warning("SYNC: Found PSS but could not decode PBCH\n"); - return CELL_NOT_FOUND; - } else { - Error("SYNC: Receiving MIB\n"); - return ERROR; - } -} - -/********* - * SFN synchronizer class - */ - -sync::sfn_sync::~sfn_sync() -{ - srslte_ue_mib_free(&ue_mib); -} - -void sync::sfn_sync::init(srslte_ue_sync_t* ue_sync_, - const phy_args_t* phy_args_, - srslte::rf_buffer_t& buffer, - uint32_t buffer_max_samples_, - srslte::log* log_h_, - uint32_t nof_subframes) -{ - log_h = log_h_; - ue_sync = ue_sync_; - phy_args = phy_args_; - timeout = nof_subframes; - - mib_buffer = buffer; - buffer_max_samples = buffer_max_samples_; - - // MIB decoder uses a single receiver antenna in logical channel 0 - if (srslte_ue_mib_init(&ue_mib, buffer.get(0), SRSLTE_MAX_PRB)) { - Error("SYNC: Initiating UE MIB decoder\n"); - } -} - -bool sync::sfn_sync::set_cell(srslte_cell_t cell_) -{ - if (srslte_ue_mib_set_cell(&ue_mib, cell_)) { - Error("SYNC: Setting cell: initiating ue_mib\n"); - return false; - } - reset(); - return true; -} - -void sync::sfn_sync::reset() -{ - cnt = 0; - srslte_ue_mib_reset(&ue_mib); -} - -sync::sfn_sync::ret_code sync::sfn_sync::run_subframe(srslte_cell_t* cell_, - uint32_t* tti_cnt, - std::array& bch_payload, - bool sfidx_only) -{ - int ret = srslte_ue_sync_zerocopy(ue_sync, mib_buffer.to_cf_t(), buffer_max_samples); - if (ret < 0) { - Error("SYNC: Error calling ue_sync_get_buffer.\n"); - return ERROR; - } - - if (ret == 1) { - sync::sfn_sync::ret_code ret2 = decode_mib(cell_, tti_cnt, nullptr, bch_payload, sfidx_only); - if (ret2 != SFN_NOFOUND) { - return ret2; - } - } else { - Info("SYNC: Waiting for PSS while trying to decode MIB (%d/%d)\n", cnt, timeout); - } - - cnt++; - if (cnt >= timeout) { - cnt = 0; - return SFN_NOFOUND; - } - - return IDLE; -} - -sync::sfn_sync::ret_code sync::sfn_sync::decode_mib(srslte_cell_t* cell_, - uint32_t* tti_cnt, - srslte::rf_buffer_t* ext_buffer, - std::array& bch_payload, - bool sfidx_only) -{ - // If external buffer provided not equal to internal buffer, copy samples from channel/port 0 - if (ext_buffer != nullptr) { - memcpy(mib_buffer.get(0), ext_buffer->get(0), sizeof(cf_t) * ue_sync->sf_len); - } - - 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 n = srslte_ue_mib_decode(&ue_mib, bch_payload.data(), NULL, &sfn_offset); - switch (n) { - default: - Error("SYNC: Error decoding MIB while synchronising SFN"); - return ERROR; - case SRSLTE_UE_MIB_FOUND: - uint32_t sfn; - srslte_pbch_mib_unpack(bch_payload.data(), cell_, &sfn); - - sfn = (sfn + sfn_offset) % 1024; - if (tti_cnt) { - *tti_cnt = 10 * sfn; - - // Check if SNR is below the minimum threshold - if (ue_mib.chest_res.snr_db < phy_args->in_sync_snr_db_th) { - Info("SYNC: MIB decoded, SNR is too low (%+.1f < %+.1f)\n", - ue_mib.chest_res.snr_db, - phy_args->in_sync_snr_db_th); - return SFN_NOFOUND; - } - - Info("SYNC: DONE, SNR=%.1f dB, TTI=%d, sfn_offset=%d\n", ue_mib.chest_res.snr_db, *tti_cnt, sfn_offset); - } - - reset(); - return SFN_FOUND; - case SRSLTE_UE_MIB_NOTFOUND: - Info("SYNC: Found PSS but could not decode MIB. SNR=%.1f dB (%d/%d)\n", ue_mib.chest_res.snr_db, cnt, timeout); - return SFN_NOFOUND; - } - } - - return IDLE; -} - /********** * PHY measurements *