From 60015e7ceb29f3365c175a2ab015fbb24fecb08f Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Thu, 13 May 2021 12:30:54 +0200 Subject: [PATCH] Made intra frequency cell search and measurment generic --- .../srsran/interfaces/phy_interface_types.h | 12 +- srsue/hdr/phy/scell/intra_measure.h | 218 --------------- srsue/hdr/phy/scell/intra_measure_base.h | 249 ++++++++++++++++++ srsue/hdr/phy/scell/intra_measure_lte.h | 83 ++++++ srsue/hdr/phy/sync.h | 10 +- srsue/src/phy/scell/intra_measure.cc | 248 ----------------- srsue/src/phy/scell/intra_measure_base.cc | 188 +++++++++++++ srsue/src/phy/scell/intra_measure_lte.cc | 115 ++++++++ srsue/src/phy/sync.cc | 10 +- srsue/test/phy/scell_search_test.cc | 21 +- srsue/test/upper/rrc_meas_test.cc | 20 +- 11 files changed, 676 insertions(+), 498 deletions(-) delete mode 100644 srsue/hdr/phy/scell/intra_measure.h create mode 100644 srsue/hdr/phy/scell/intra_measure_base.h create mode 100644 srsue/hdr/phy/scell/intra_measure_lte.h delete mode 100644 srsue/src/phy/scell/intra_measure.cc create mode 100644 srsue/src/phy/scell/intra_measure_base.cc create mode 100644 srsue/src/phy/scell/intra_measure_lte.cc diff --git a/lib/include/srsran/interfaces/phy_interface_types.h b/lib/include/srsran/interfaces/phy_interface_types.h index d446eae11..b5068d8fd 100644 --- a/lib/include/srsran/interfaces/phy_interface_types.h +++ b/lib/include/srsran/interfaces/phy_interface_types.h @@ -13,6 +13,7 @@ #ifndef SRSRAN_PHY_INTERFACE_TYPES_H #define SRSRAN_PHY_INTERFACE_TYPES_H +#include "srsran/common/common.h" #include "srsran/srsran.h" /// Common types defined by the PHY layer. @@ -52,11 +53,12 @@ struct phy_meas_nr_t { }; struct phy_meas_t { - float rsrp; - float rsrq; - float cfo_hz; - uint32_t earfcn; - uint32_t pci; + srsran::srsran_rat_t rat; ///< LTE or NR + float rsrp; + float rsrq; + float cfo_hz; + uint32_t earfcn; + uint32_t pci; }; struct phy_cell_t { diff --git a/srsue/hdr/phy/scell/intra_measure.h b/srsue/hdr/phy/scell/intra_measure.h deleted file mode 100644 index cff540036..000000000 --- a/srsue/hdr/phy/scell/intra_measure.h +++ /dev/null @@ -1,218 +0,0 @@ -/** - * - * \section COPYRIGHT - * - * Copyright 2013-2021 Software Radio Systems Limited - * - * By using this file, you agree to the terms and conditions set - * forth in the LICENSE file which can be found at the top level of - * the distribution. - * - */ -#ifndef SRSUE_INTRA_MEASURE_H -#define SRSUE_INTRA_MEASURE_H - -#include -#include -#include - -#include "scell_recv.h" - -namespace srsue { -namespace scell { - -// Class to perform intra-frequency measurements -class intra_measure : public srsran::thread -{ - /* - * The intra-cell measurment has 5 different states: - * - idle: it has been initiated and it is waiting to get configured to start capturing samples. From any state - * except quit can transition to idle. - * - wait: waits for at least intra_freq_meas_period_ms since last receive start and goes to receive. - * - receive: captures base-band samples for intra_freq_meas_len_ms and goes to measure. - * - measure: enables the inner thread to start the measuring function. The asynchronous buffer will transition to - * wait as soon as it has read the data from the buffer. - * - quit: stops the inner thread and quits. Transition from any state measure state. - * - * FSM abstraction: - * - * +------+ set_cells_to_meas +------+ intra_freq_meas_period_ms +---------+ - * | Idle | --------------------->| Wait |------------------------------>| Receive | - * +------+ +------+ +---------+ - * ^ ^ | stop +------+ - * | Read buffer | | ----->| Quit | - * init +---------+ intra_freq_meas_len_ms | +------+ - * meas_stop | Measure |<----------------------------------+ - * +---------+ - */ -public: - // Interface for reporting new cell measurements - class meas_itf - { - public: - virtual void cell_meas_reset(uint32_t cc_idx) = 0; - virtual void new_cell_meas(uint32_t cc_idx, const std::vector& meas) = 0; - }; - - /** - * Constructor - */ - intra_measure(srslog::basic_logger& logger); - - /** - * Destructor - */ - ~intra_measure(); - - /** - * Initiation function, necessary to configure main parameters - * @param common SRSUE phy_common instance pointer for providing intra_freq_meas_len_ms and intra_freq_meas_period_ms - * @param rrc SRSUE PHY->RRC interface for supplying the RRC with the measurements - */ - void init(uint32_t cc_idx, phy_common* common, meas_itf* new_cell_itf); - - /** - * Stops the operation of this component - */ - void stop(); - - /** - * Sets the primary cell, configures the cell bandwidth and sampling rate - * @param earfcn Frequency the component is receiving base-band from. Used only for reporting the EARFCN to the RRC - * @param cell Actual cell configuration - */ - void set_primary_cell(uint32_t earfcn, srsran_cell_t cell); - - /** - * Sets receiver gain offset to convert estimated dBFs to dBm in RSRP - * @param rx_gain_offset Gain offset in dB - */ - void set_rx_gain_offset(float rx_gain_offset_db); - - /** - * Sets the PCI list of the cells this components needs to measure and starts the FSM for measuring - * @param pci is the list of PCIs to measure - */ - void set_cells_to_meas(const std::set& pci); - - /** - * Stops the measurment FSM, setting the inner state to idle. - */ - void meas_stop(); - - /** - * Inputs the baseband IQ samples into the component, internal state dictates whether it will be written or not. - * @param tti The current physical layer TTI, used for calculating the buffer write - * @param data buffer with baseband IQ samples - * @param nsamples number of samples to write - */ - void write(uint32_t tti, cf_t* data, uint32_t nsamples); - - /** - * Get EARFCN of this component - * @return EARFCN - */ - uint32_t get_earfcn() { return current_earfcn; }; - - /** - * Synchronous wait mechanism, blocks the writer thread while it is in measure state. If the asynchonous thread is too - * slow, use this method for stalling the writing thread and wait the asynchronous thread to clear the buffer. - */ - void wait_meas() - { // Only used by scell_search_test - state.wait_change(internal_state::measure); - } - -private: - class internal_state ///< Internal state class, provides thread safe state management - { - public: - typedef enum { - idle = 0, ///< Initial state, internal thread runs, it does not capture data - wait, ///< Wait for the period time to pass - receive, ///< Accumulate samples in ring buffer - measure, ///< Module is busy measuring - quit ///< Quit thread, no transitions are allowed - } state_t; - - private: - state_t state = idle; - std::mutex mutex; - std::condition_variable cvar; - - public: - /** - * Get the internal state - * @return protected state - */ - state_t get_state() { return state; } - - /** - * Transitions to a different state, all transitions are allowed except from quit - * @param new_state - */ - void set_state(state_t new_state) - { - std::unique_lock lock(mutex); - // Do not allow transition from quit - if (state != quit) { - state = new_state; - } - - // Notifies to the inner thread about the change of state - cvar.notify_all(); - } - - /** - * Waits for a state transition to a state different than the provided, used for blocking the inner thread - */ - void wait_change(state_t s) - { - std::unique_lock lock(mutex); - while (state == s) { - cvar.wait(lock); - } - } - }; - - internal_state state; - - /** - * Measurement process helper method. Encapusulates the neighbour cell measurement functionality - */ - void measure_proc(); - - /** - * Internal asynchronous low priority thread, waits for measure internal state to execute the measurement process. It - * stops when the internal state transitions to quit. - */ - void run_thread() override; - - ///< Internal Thread priority, low by default - const static int INTRA_FREQ_MEAS_PRIO = DEFAULT_PRIORITY + 5; - - scell_recv scell; - meas_itf* new_cell_itf = nullptr; - srslog::basic_logger& logger; - uint32_t cc_idx = 0; - uint32_t current_earfcn = 0; - uint32_t current_sflen = 0; - srsran_cell_t serving_cell = {}; - std::set active_pci = {}; - std::mutex active_pci_mutex = {}; - uint32_t last_measure_tti = 0; - uint32_t intra_freq_meas_len_ms = 20; - uint32_t intra_freq_meas_period_ms = 200; - uint32_t rx_gain_offset_db = 0; - - cf_t* search_buffer = nullptr; - - srsran_ringbuffer_t ring_buffer = {}; - - srsran_refsignal_dl_sync_t refsignal_dl_sync = {}; -}; - -} // namespace scell -} // namespace srsue - -#endif // SRSUE_INTRA_MEASURE_H diff --git a/srsue/hdr/phy/scell/intra_measure_base.h b/srsue/hdr/phy/scell/intra_measure_base.h new file mode 100644 index 000000000..c61a04892 --- /dev/null +++ b/srsue/hdr/phy/scell/intra_measure_base.h @@ -0,0 +1,249 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#ifndef SRSUE_INTRA_MEASURE_BASE_H +#define SRSUE_INTRA_MEASURE_BASE_H + +#include +#include +#include +#include + +#include "scell_recv.h" + +namespace srsue { +namespace scell { + +/** + * @brief Describes a generic base class to perform intra-frequency measurements + */ +class intra_measure_base : public srsran::thread +{ + /* + * The intra-cell measurment has 5 different states: + * - idle: it has been initiated and it is waiting to get configured to start capturing samples. From any state + * except quit can transition to idle. + * - wait: waits for at least intra_freq_meas_period_ms since last receive start and goes to receive. + * - receive: captures base-band samples for intra_freq_meas_len_ms and goes to measure. + * - measure: enables the inner thread to start the measuring function. The asynchronous buffer will transition to + * wait as soon as it has read the data from the buffer. + * - quit: stops the inner thread and quits. Transition from any state measure state. + * + * FSM abstraction: + * + * +------+ set_cells_to_meas +------+ intra_freq_meas_period_ms +---------+ + * | Idle | --------------------->| Wait |------------------------------>| Receive | + * +------+ +------+ +---------+ + * ^ ^ | stop +------+ + * | Read buffer | | ----->| Quit | + * init +---------+ intra_freq_meas_len_ms | +------+ + * meas_stop | Measure |<----------------------------------+ + * +---------+ + */ +public: + /** + * @brief Describes an interface for reporting new cell measurements + */ + class meas_itf + { + public: + virtual void cell_meas_reset(uint32_t cc_idx) = 0; + virtual void new_cell_meas(uint32_t cc_idx, const std::vector& meas) = 0; + }; + + /** + * @brief Describes the default generic configuration arguments + */ + struct args_t { + double srate_hz = 0.0; ///< Sampling rate in Hz, set to 0.0 for maximum + uint32_t len_ms = 20; ///< Amount of time to accumulate + uint32_t period_ms = 200; ///< Accumulation trigger period + float rx_gain_offset_db = 0.0f; ///< Gain offset, for calibrated measurements + }; + + /** + * @brief Stops the operation of this component and it cannot be started again + * @note use meas_stop() method to stop measurements temporally + */ + void stop(); + + /** + * @brief Updates the receiver gain offset to convert estimated dBFs to dBm in RSRP + * @param rx_gain_offset Gain offset in dB + */ + void set_rx_gain_offset(float rx_gain_offset_db); + + /** + * @brief Sets the PCI list of the cells this components needs to measure and starts the FSM for measuring + * @param pci is the list of PCIs to measure + */ + void set_cells_to_meas(const std::set& pci); + + /** + * @brief Stops the measurement FSM, setting the inner state to idle. + */ + void meas_stop(); + + /** + * @brief Inputs the baseband IQ samples into the component, internal state dictates whether it will be written or + * not. + * @param tti The current physical layer TTI, used for calculating the buffer write + * @param data buffer with baseband IQ samples + * @param nsamples number of samples to write + */ + void write(uint32_t tti, cf_t* data, uint32_t nsamples); + + /** + * @brief Get EARFCN of this component + * @return EARFCN + */ + virtual uint32_t get_earfcn() const = 0; + + /** + * @brief Synchronous wait mechanism, blocks the writer thread while it is in measure state. If the asynchronous + * thread is too slow, use this method for stalling the writing thread and wait the asynchronous thread to clear the + * buffer. + */ + void wait_meas() + { // Only used by scell_search_test + state.wait_change(internal_state::measure); + } + +protected: + struct measure_context_t { + uint32_t cc_idx = 0; ///< Component carrier index + float rx_gain_offset_db = 0.0f; ///< Current gain offset + std::set active_pci = {}; ///< Set with the active PCIs + uint32_t sf_len = 0; ///< Subframe length in samples + uint32_t meas_len_ms = 20; ///< Measure length in milliseconds/sub-frames + uint32_t meas_period_ms = 200; ///< Measure period in milliseconds/sub-frames + meas_itf& new_cell_itf; + + explicit measure_context_t(meas_itf& new_cell_itf_) : new_cell_itf(new_cell_itf_) {} + }; + + /** + * @brief Generic initialization method, necessary to configure main parameters + * @param cc_idx_ Indicates the component carrier index linked to the intra frequency measurement instance + * @param args Generic configuration arguments + */ + void init_generic(uint32_t cc_idx_, const args_t& args); + + /** + * @brief Constructor is only accessible through inherited classes + */ + intra_measure_base(srslog::basic_logger& logger, meas_itf& new_cell_itf_); + + /** + * @brief Destructor is only accessible through inherited classes + */ + ~intra_measure_base() override; + + /** + * @brief Subframe length setter, the inherited class shall set the subframe length + * @param new_sf_len New subframe length + */ + void set_current_sf_len(uint32_t new_sf_len) { context.sf_len = new_sf_len; } + +private: + /** + * @brief Describes the internal state class, provides thread safe state management + */ + class internal_state + { + public: + typedef enum { + idle = 0, ///< Initial state, internal thread runs, it does not capture data + wait, ///< Wait for the period time to pass + receive, ///< Accumulate samples in ring buffer + measure, ///< Module is busy measuring + quit ///< Quit thread, no transitions are allowed + } state_t; + + private: + state_t state = idle; + std::mutex mutex; + std::condition_variable cvar; + + public: + /** + * @brief Get the internal state + * @return protected state + */ + state_t get_state() const { return state; } + + /** + * @brief Transitions to a different state, all transitions are allowed except from quit + * @param new_state + */ + void set_state(state_t new_state) + { + std::unique_lock lock(mutex); + // Do not allow transition from quit + if (state != quit) { + state = new_state; + } + + // Notifies to the inner thread about the change of state + cvar.notify_all(); + } + + /** + * @brief Waits for a state transition to a state different than the provided, used for blocking the inner thread + */ + void wait_change(state_t s) + { + std::unique_lock lock(mutex); + while (state == s) { + cvar.wait(lock); + } + } + }; + + /** + * @brief Get the Radio Access Technology (RAT) that is being measured + * @return The measured RAT + */ + virtual srsran::srsran_rat_t get_rat() const = 0; + + /** + * @brief Pure virtual function to perform measurements + */ + virtual void measure_rat(const measure_context_t& context, std::vector& buffer) = 0; + + /** + * @brief Measurement process helper method. Encapsulates the neighbour cell measurement functionality + */ + void measure_proc(); + + /** + * @brief Internal asynchronous low priority thread, waits for measure internal state to execute the measurement + * process. It stops when the internal state transitions to quit. + */ + void run_thread() override; + + ///< Internal Thread priority, low by default + const static int INTRA_FREQ_MEAS_PRIO = DEFAULT_PRIORITY + 5; + + internal_state state; + srslog::basic_logger& logger; + mutable std::mutex active_pci_mutex = {}; + uint32_t last_measure_tti = 0; + measure_context_t context; + + std::vector search_buffer; + srsran_ringbuffer_t ring_buffer = {}; +}; + +} // namespace scell +} // namespace srsue + +#endif // SRSUE_INTRA_MEASURE_BASE_H diff --git a/srsue/hdr/phy/scell/intra_measure_lte.h b/srsue/hdr/phy/scell/intra_measure_lte.h new file mode 100644 index 000000000..2a9db1b71 --- /dev/null +++ b/srsue/hdr/phy/scell/intra_measure_lte.h @@ -0,0 +1,83 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#ifndef SRSRAN_INTRA_MEASURE_LTE_H +#define SRSRAN_INTRA_MEASURE_LTE_H + +#include "intra_measure_base.h" + +namespace srsue { +namespace scell { + +/** + * @brief Describes a class for performing LTE intra-frequency cell search and measurement + */ +class intra_measure_lte : public intra_measure_base +{ +public: + /** + * @brief Constructor + * @param logger Logging object + * @param new_meas_itf_ Interface to report measurement to higher layers + */ + intra_measure_lte(srslog::basic_logger& logger, meas_itf& new_meas_itf_); + + /** + * @brief Destructor + */ + ~intra_measure_lte() override; + + /** + * @brief Initialises LTE specific measurement objects + * @param args Configuration arguments + */ + void init(uint32_t cc_idx, const args_t& args); + + /** + * @brief Sets the primary cell and selects LTE operation mode, configures the cell bandwidth and sampling rate + * @param earfcn Frequency the component is receiving base-band from. Used only for reporting the EARFCN to the RRC + * @param cell Actual cell configuration + */ + void set_primary_cell(uint32_t earfcn, srsran_cell_t cell); + + /** + * @brief Get EARFCN of this component + * @return EARFCN + */ + uint32_t get_earfcn() const override { return current_earfcn; }; + +private: + /** + * @brief Provides with the RAT to the base class + * @return The RAT measured by this class which is LTE + */ + srsran::srsran_rat_t get_rat() const override { return srsran::srsran_rat_t::lte; } + + /** + * @brief LTE specific measurement process + * @param context Measurement context + * @param buffer Provides the baseband buffer to perform the measurements + */ + void measure_rat(const measure_context_t& context, std::vector& buffer) override; + + srslog::basic_logger& logger; + srsran_cell_t serving_cell = {}; ///< Current serving cell in the EARFCN, to avoid reporting it + uint32_t current_earfcn; ///< Current EARFCN + + /// LTE-based measuring objects + scell_recv scell_rx; ///< Secondary cell searcher + srsran_refsignal_dl_sync_t refsignal_dl_sync = {}; ///< Reference signal based measurement +}; + +} // namespace scell +} // namespace srsue + +#endif // SRSRAN_INTRA_MEASURE_LTE_H diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index 93e2f7a1e..8dc055ae2 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -20,7 +20,7 @@ #include "phy_common.h" #include "prach.h" -#include "scell/intra_measure.h" +#include "scell/intra_measure_lte.h" #include "scell/scell_sync.h" #include "search.h" #include "sfn_sync.h" @@ -42,7 +42,7 @@ class sync : public srsran::thread, public rsrp_insync_itf, public search_callback, public scell::sync_callback, - public scell::intra_measure::meas_itf + public scell::intra_measure_base::meas_itf { public: sync(srslog::basic_logger& phy_logger, srslog::basic_logger& phy_lib_logger) : @@ -196,9 +196,9 @@ private: bool forced_rx_time_init = true; // Rx time sync after first receive from radio // Objects for internal use - search search_p; - sfn_sync sfn_p; - std::vector > intra_freq_meas; + search search_p; + sfn_sync sfn_p; + std::vector > intra_freq_meas; // Pointers to other classes stack_interface_phy_lte* stack = nullptr; diff --git a/srsue/src/phy/scell/intra_measure.cc b/srsue/src/phy/scell/intra_measure.cc deleted file mode 100644 index 01fa6ea99..000000000 --- a/srsue/src/phy/scell/intra_measure.cc +++ /dev/null @@ -1,248 +0,0 @@ -/** - * - * \section COPYRIGHT - * - * Copyright 2013-2021 Software Radio Systems Limited - * - * By using this file, you agree to the terms and conditions set - * forth in the LICENSE file which can be found at the top level of - * the distribution. - * - */ -#include "srsue/hdr/phy/scell/intra_measure.h" - -#define Error(fmt, ...) \ - if (SRSRAN_DEBUG_ENABLED) \ - logger.error(fmt, ##__VA_ARGS__) -#define Warning(fmt, ...) \ - if (SRSRAN_DEBUG_ENABLED) \ - logger.warning(fmt, ##__VA_ARGS__) -#define Info(fmt, ...) \ - if (SRSRAN_DEBUG_ENABLED) \ - logger.info(fmt, ##__VA_ARGS__) -#define Debug(fmt, ...) \ - if (SRSRAN_DEBUG_ENABLED) \ - logger.debug(fmt, ##__VA_ARGS__) - -namespace srsue { -namespace scell { - -intra_measure::intra_measure(srslog::basic_logger& logger) : scell(logger), logger(logger), thread("SYNC_INTRA_MEASURE") -{} - -intra_measure::~intra_measure() -{ - srsran_ringbuffer_free(&ring_buffer); - scell.deinit(); - free(search_buffer); -} - -void intra_measure::init(uint32_t cc_idx_, phy_common* common, meas_itf* new_cell_itf_) -{ - cc_idx = cc_idx_; - new_cell_itf = new_cell_itf_; - - if (common) { - intra_freq_meas_len_ms = common->args->intra_freq_meas_len_ms; - intra_freq_meas_period_ms = common->args->intra_freq_meas_period_ms; - rx_gain_offset_db = common->args->rx_gain_offset; - } - - // Initialise Reference signal measurement - srsran_refsignal_dl_sync_init(&refsignal_dl_sync); - - // Start scell - scell.init(intra_freq_meas_len_ms); - - search_buffer = srsran_vec_cf_malloc(intra_freq_meas_len_ms * SRSRAN_SF_LEN_PRB(SRSRAN_MAX_PRB)); - - // Initialise buffer for the maximum number of PRB - uint32_t max_required_bytes = (uint32_t)sizeof(cf_t) * intra_freq_meas_len_ms * SRSRAN_SF_LEN_PRB(SRSRAN_MAX_PRB); - if (srsran_ringbuffer_init(&ring_buffer, max_required_bytes)) { - return; - } - - state.set_state(internal_state::idle); - start(INTRA_FREQ_MEAS_PRIO); -} - -void intra_measure::stop() -{ - // Notify quit to asynchronous thread. If it is measuring, it will first finish the measure, report to stack and - // then it will finish - state.set_state(internal_state::quit); - - // Wait for the asynchronous thread to finish - wait_thread_finish(); - - srsran_ringbuffer_stop(&ring_buffer); - srsran_refsignal_dl_sync_free(&refsignal_dl_sync); -} - -void intra_measure::set_primary_cell(uint32_t earfcn, srsran_cell_t cell) -{ - current_earfcn = earfcn; - current_sflen = (uint32_t)SRSRAN_SF_LEN_PRB(cell.nof_prb); - serving_cell = cell; -} - -void intra_measure::set_rx_gain_offset(float rx_gain_offset_db_) -{ - rx_gain_offset_db = rx_gain_offset_db_; -} - -void intra_measure::meas_stop() -{ - // Transition state to idle - // Ring-buffer shall not be reset, it will automatically be reset as soon as the FSM transitions to receive - state.set_state(internal_state::idle); - Info("INTRA: Disabled neighbour cell search for EARFCN %d", get_earfcn()); -} - -void intra_measure::set_cells_to_meas(const std::set& pci) -{ - active_pci_mutex.lock(); - active_pci = pci; - active_pci_mutex.unlock(); - state.set_state(internal_state::receive); - logger.info("INTRA: Received list of %zd neighbour cells to measure in EARFCN %d.", pci.size(), current_earfcn); -} - -void intra_measure::write(uint32_t tti, cf_t* data, uint32_t nsamples) -{ - int nbytes = (int)(nsamples * sizeof(cf_t)); - int required_nbytes = (int)(intra_freq_meas_len_ms * current_sflen * sizeof(cf_t)); - uint32_t elapsed_tti = TTI_SUB(tti, last_measure_tti); - - switch (state.get_state()) { - - case internal_state::idle: - case internal_state::measure: - case internal_state::quit: - // Do nothing - break; - case internal_state::wait: - if (elapsed_tti >= intra_freq_meas_period_ms) { - state.set_state(internal_state::receive); - last_measure_tti = tti; - srsran_ringbuffer_reset(&ring_buffer); - } - break; - case internal_state::receive: - // As nbytes might not match the sub-frame size, make sure that buffer does not overflow - nbytes = SRSRAN_MIN(srsran_ringbuffer_space(&ring_buffer), nbytes); - - // Try writing in the buffer - if (srsran_ringbuffer_write(&ring_buffer, data, nbytes) < nbytes) { - Warning("INTRA: Error writing to ringbuffer (EARFCN=%d)", current_earfcn); - - // Transition to wait, so it can keep receiving without stopping the component operation - state.set_state(internal_state::wait); - } else { - // As soon as there are enough samples in the buffer, transition to measure - if (srsran_ringbuffer_status(&ring_buffer) >= required_nbytes) { - state.set_state(internal_state::measure); - } - } - break; - } -} - -void intra_measure::measure_proc() -{ - std::set cells_to_measure = {}; - - // Load cell list to measure - active_pci_mutex.lock(); - cells_to_measure = active_pci; - active_pci_mutex.unlock(); - - // Read data from buffer and find cells in it - srsran_ringbuffer_read(&ring_buffer, search_buffer, (int)intra_freq_meas_len_ms * current_sflen * sizeof(cf_t)); - - // Go to receive before finishing, so new samples can be enqueued before the thread finishes - if (state.get_state() == internal_state::measure) { - // Prevents transition to wait if state has changed while reading the ring-buffer - state.set_state(internal_state::wait); - } - - // Detect new cells using PSS/SSS - std::set detected_cells = scell.find_cells(search_buffer, serving_cell, intra_freq_meas_len_ms); - - // Add detected cells to the list of cells to measure - for (const uint32_t& c : detected_cells) { - cells_to_measure.insert(c); - } - - // Initialise empty neighbour cell list - std::vector neighbour_cells = {}; - - new_cell_itf->cell_meas_reset(cc_idx); - - // Use Cell Reference signal to measure cells in the time domain for all known active PCI - for (const uint32_t& id : cells_to_measure) { - // Do not measure serving cell here since it's measured by workers - if (id == serving_cell.id) { - continue; - } - srsran_cell_t cell = serving_cell; - cell.id = id; - - srsran_refsignal_dl_sync_set_cell(&refsignal_dl_sync, cell); - srsran_refsignal_dl_sync_run(&refsignal_dl_sync, search_buffer, intra_freq_meas_len_ms * current_sflen); - - if (refsignal_dl_sync.found) { - phy_meas_t m = {}; - m.pci = cell.id; - m.earfcn = current_earfcn; - m.rsrp = refsignal_dl_sync.rsrp_dBfs - rx_gain_offset_db; - m.rsrq = refsignal_dl_sync.rsrq_dB; - m.cfo_hz = refsignal_dl_sync.cfo_Hz; - neighbour_cells.push_back(m); - - Info("INTRA: Found neighbour cell: EARFCN=%d, PCI=%03d, RSRP=%5.1f dBm, RSRQ=%5.1f, peak_idx=%5d, " - "CFO=%+.1fHz", - m.earfcn, - m.pci, - m.rsrp, - m.rsrq, - refsignal_dl_sync.peak_index, - refsignal_dl_sync.cfo_Hz); - } - } - - // Send measurements to RRC if any cell found - if (not neighbour_cells.empty()) { - new_cell_itf->new_cell_meas(cc_idx, neighbour_cells); - } -} - -void intra_measure::run_thread() -{ - bool quit = false; - - do { - // Get state - internal_state::state_t s = state.get_state(); - switch (s) { - - case internal_state::idle: - case internal_state::wait: - case internal_state::receive: - // Wait for a different state - state.wait_change(s); - break; - case internal_state::measure: - // Run the measurement process - measure_proc(); - break; - case internal_state::quit: - // Quit loop - quit = true; - break; - } - } while (not quit); -} - -} // namespace scell -} // namespace srsue diff --git a/srsue/src/phy/scell/intra_measure_base.cc b/srsue/src/phy/scell/intra_measure_base.cc new file mode 100644 index 000000000..f34a63760 --- /dev/null +++ b/srsue/src/phy/scell/intra_measure_base.cc @@ -0,0 +1,188 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#include "srsue/hdr/phy/scell/intra_measure_base.h" + +#define Error(fmt, ...) \ + if (SRSRAN_DEBUG_ENABLED) \ + logger.error("INTRA-%s: " fmt, to_string(get_rat()).c_str(), ##__VA_ARGS__) +#define Warning(fmt, ...) \ + if (SRSRAN_DEBUG_ENABLED) \ + logger.warning("INTRA-%s: " fmt, to_string(get_rat()).c_str(), ##__VA_ARGS__) +#define Info(fmt, ...) \ + if (SRSRAN_DEBUG_ENABLED) \ + logger.info("INTRA-%s: " fmt, to_string(get_rat()).c_str(), ##__VA_ARGS__) +#define Debug(fmt, ...) \ + if (SRSRAN_DEBUG_ENABLED) \ + logger.debug("INTRA-%s: " fmt, to_string(get_rat()).c_str(), ##__VA_ARGS__) + +namespace srsue { +namespace scell { + +intra_measure_base::intra_measure_base(srslog::basic_logger& logger, meas_itf& new_cell_itf_) : + logger(logger), context(new_cell_itf_), thread("SYNC_INTRA_MEASURE") +{} + +intra_measure_base::~intra_measure_base() +{ + srsran_ringbuffer_free(&ring_buffer); +} + +void intra_measure_base::init_generic(uint32_t cc_idx_, const args_t& args) +{ + context.cc_idx = cc_idx_; + + context.meas_len_ms = args.len_ms; + context.meas_period_ms = args.period_ms; + context.rx_gain_offset_db = args.rx_gain_offset_db; + + context.sf_len = SRSRAN_SF_LEN_PRB(SRSRAN_MAX_PRB); + if (isnormal(args.srate_hz)) { + context.sf_len = (uint32_t)round(args.srate_hz / 1000.0); + } + + // Calculate the new required bytes + int max_required_bytes = (int)(sizeof(cf_t) * context.meas_len_ms * context.sf_len); + + // Reallocate only if the required capacity exceds the new requirement + if (ring_buffer.capacity < max_required_bytes) { + search_buffer.resize(context.meas_len_ms * context.sf_len); + + srsran_ringbuffer_free(&ring_buffer); + + // Initialise buffer for the maximum number of PRB + if (srsran_ringbuffer_init(&ring_buffer, max_required_bytes) < SRSRAN_SUCCESS) { + return; + } + } + + state.set_state(internal_state::idle); + start(INTRA_FREQ_MEAS_PRIO); +} + +void intra_measure_base::stop() +{ + // Notify quit to asynchronous thread. If it is measuring, it will first finish the measure, report to stack and + // then it will finish + state.set_state(internal_state::quit); + + // Wait for the asynchronous thread to finish + wait_thread_finish(); + + srsran_ringbuffer_stop(&ring_buffer); +} + +void intra_measure_base::set_rx_gain_offset(float rx_gain_offset_db_) +{ + context.rx_gain_offset_db = rx_gain_offset_db_; +} + +void intra_measure_base::meas_stop() +{ + // Transition state to idle + // Ring-buffer shall not be reset, it will automatically be reset as soon as the FSM transitions to receive + state.set_state(internal_state::idle); + Info("Disabled neighbour cell search for EARFCN %d", get_earfcn()); +} + +void intra_measure_base::set_cells_to_meas(const std::set& pci) +{ + active_pci_mutex.lock(); + context.active_pci = pci; + active_pci_mutex.unlock(); + state.set_state(internal_state::receive); + Info("Received list of %zd neighbour cells to measure in EARFCN %d.", pci.size(), get_earfcn()); +} + +void intra_measure_base::write(uint32_t tti, cf_t* data, uint32_t nsamples) +{ + int nbytes = (int)(nsamples * sizeof(cf_t)); + int required_nbytes = (int)(context.meas_len_ms * context.sf_len * sizeof(cf_t)); + uint32_t elapsed_tti = TTI_SUB(tti, last_measure_tti); + + switch (state.get_state()) { + case internal_state::idle: + case internal_state::measure: + case internal_state::quit: + // Do nothing + break; + case internal_state::wait: + if (elapsed_tti >= context.meas_period_ms) { + state.set_state(internal_state::receive); + last_measure_tti = tti; + srsran_ringbuffer_reset(&ring_buffer); + } + break; + case internal_state::receive: + // As nbytes might not match the sub-frame size, make sure that buffer does not overflow + nbytes = SRSRAN_MIN(srsran_ringbuffer_space(&ring_buffer), nbytes); + + // Try writing in the buffer + if (srsran_ringbuffer_write(&ring_buffer, data, nbytes) < nbytes) { + Warning("Error writing to ringbuffer (EARFCN=%d)", get_earfcn()); + + // Transition to wait, so it can keep receiving without stopping the component operation + state.set_state(internal_state::wait); + } else { + // As soon as there are enough samples in the buffer, transition to measure + if (srsran_ringbuffer_status(&ring_buffer) >= required_nbytes) { + state.set_state(internal_state::measure); + } + } + break; + } +} + +void intra_measure_base::measure_proc() +{ + std::set cells_to_measure = {}; + + // Read data from buffer and find cells in it + srsran_ringbuffer_read(&ring_buffer, search_buffer.data(), (int)context.meas_len_ms * context.sf_len * sizeof(cf_t)); + + // Go to receive before finishing, so new samples can be enqueued before the thread finishes + if (state.get_state() == internal_state::measure) { + // Prevents transition to wait if state has changed while reading the ring-buffer + state.set_state(internal_state::wait); + } + + // Perform measurements for the actual RAT + measure_rat(context, search_buffer); +} + +void intra_measure_base::run_thread() +{ + bool quit = false; + + do { + // Get state + internal_state::state_t s = state.get_state(); + switch (s) { + case internal_state::idle: + case internal_state::wait: + case internal_state::receive: + // Wait for a different state + state.wait_change(s); + break; + case internal_state::measure: + // Run the measurement process + measure_proc(); + break; + case internal_state::quit: + // Quit loop + quit = true; + break; + } + } while (not quit); +} + +} // namespace scell +} // namespace srsue diff --git a/srsue/src/phy/scell/intra_measure_lte.cc b/srsue/src/phy/scell/intra_measure_lte.cc new file mode 100644 index 000000000..98dee3701 --- /dev/null +++ b/srsue/src/phy/scell/intra_measure_lte.cc @@ -0,0 +1,115 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#include "srsue/hdr/phy/scell/intra_measure_lte.h" + +namespace srsue { +namespace scell { + +#define Error(fmt, ...) \ + if (SRSRAN_DEBUG_ENABLED) \ + logger.error("INTRA-%s: " fmt, to_string(get_rat()).c_str(), ##__VA_ARGS__) +#define Warning(fmt, ...) \ + if (SRSRAN_DEBUG_ENABLED) \ + logger.warning("INTRA-%s: " fmt, to_string(get_rat()).c_str(), ##__VA_ARGS__) +#define Info(fmt, ...) \ + if (SRSRAN_DEBUG_ENABLED) \ + logger.info("INTRA-%s: " fmt, to_string(get_rat()).c_str(), ##__VA_ARGS__) +#define Debug(fmt, ...) \ + if (SRSRAN_DEBUG_ENABLED) \ + logger.debug("INTRA-%s: " fmt, to_string(get_rat()).c_str(), ##__VA_ARGS__) + +intra_measure_lte::intra_measure_lte(srslog::basic_logger& logger_, meas_itf& new_cell_itf_) : + logger(logger_), scell_rx(logger_), intra_measure_base(logger_, new_cell_itf_) +{} + +intra_measure_lte::~intra_measure_lte() +{ + scell_rx.deinit(); + srsran_refsignal_dl_sync_free(&refsignal_dl_sync); +} + +void intra_measure_lte::init(uint32_t cc_idx, const args_t& args) +{ + init_generic(cc_idx, args); + + // Initialise Reference signal measurement + srsran_refsignal_dl_sync_init(&refsignal_dl_sync); + + // Start scell + scell_rx.init(args.len_ms); +} + +void intra_measure_lte::set_primary_cell(uint32_t earfcn, srsran_cell_t cell) +{ + current_earfcn = earfcn; + serving_cell = cell; + set_current_sf_len((uint32_t)SRSRAN_SF_LEN_PRB(cell.nof_prb)); +} + +void intra_measure_lte::measure_rat(const measure_context_t& context, std::vector& buffer) +{ + std::set cells_to_measure = context.active_pci; + + // Detect new cells using PSS/SSS + std::set detected_cells = scell_rx.find_cells(buffer.data(), serving_cell, context.meas_len_ms); + + // Add detected cells to the list of cells to measure + for (const uint32_t& c : detected_cells) { + cells_to_measure.insert(c); + } + + // Initialise empty neighbour cell list + std::vector neighbour_cells = {}; + + context.new_cell_itf.cell_meas_reset(context.cc_idx); + + // Use Cell Reference signal to measure cells in the time domain for all known active PCI + for (const uint32_t& id : cells_to_measure) { + // Do not measure serving cell here since it's measured by workers + if (id == serving_cell.id) { + continue; + } + srsran_cell_t cell = serving_cell; + cell.id = id; + + srsran_refsignal_dl_sync_set_cell(&refsignal_dl_sync, cell); + srsran_refsignal_dl_sync_run(&refsignal_dl_sync, buffer.data(), context.meas_len_ms * context.sf_len); + + if (refsignal_dl_sync.found) { + phy_meas_t m = {}; + m.rat = srsran::srsran_rat_t::lte; + m.pci = cell.id; + m.earfcn = current_earfcn; + m.rsrp = refsignal_dl_sync.rsrp_dBfs - context.rx_gain_offset_db; + m.rsrq = refsignal_dl_sync.rsrq_dB; + m.cfo_hz = refsignal_dl_sync.cfo_Hz; + neighbour_cells.push_back(m); + + Info("Found neighbour cell: EARFCN=%d, PCI=%03d, RSRP=%5.1f dBm, RSRQ=%5.1f, peak_idx=%5d, " + "CFO=%+.1fHz", + m.earfcn, + m.pci, + m.rsrp, + m.rsrq, + refsignal_dl_sync.peak_index, + refsignal_dl_sync.cfo_Hz); + } + } + + // Send measurements to RRC if any cell found + if (not neighbour_cells.empty()) { + context.new_cell_itf.new_cell_meas(context.cc_idx, neighbour_cells); + } +} + +} // namespace scell +} // namespace srsue diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index bb1e0253d..0758cadd6 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -88,9 +88,13 @@ void sync::init(srsran::radio_interface_phy* _radio, // Start intra-frequency measurement for (uint32_t i = 0; i < worker_com->args->nof_lte_carriers; i++) { - scell::intra_measure* q = new scell::intra_measure(phy_logger); - q->init(i, worker_com, this); - intra_freq_meas.push_back(std::unique_ptr(q)); + scell::intra_measure_lte* q = new scell::intra_measure_lte(phy_logger, *this); + scell::intra_measure_base::args_t args = {}; + args.len_ms = worker_com->args->intra_freq_meas_len_ms; + args.period_ms = worker_com->args->intra_freq_meas_period_ms; + args.rx_gain_offset_db = worker_com->args->rx_gain_offset; + q->init(i, args); + intra_freq_meas.push_back(std::unique_ptr(q)); } // Allocate Secondary serving cell synchronization diff --git a/srsue/test/phy/scell_search_test.cc b/srsue/test/phy/scell_search_test.cc index e8528c12a..1a418c699 100644 --- a/srsue/test/phy/scell_search_test.cc +++ b/srsue/test/phy/scell_search_test.cc @@ -12,7 +12,7 @@ #include "srsran/interfaces/phy_interface_types.h" #include "srsran/srslog/srslog.h" -#include "srsue/hdr/phy/scell/intra_measure.h" +#include "srsue/hdr/phy/scell/intra_measure_lte.h" #include #include #include @@ -212,7 +212,7 @@ public: } }; -class meas_itf_listener : public srsue::scell::intra_measure::meas_itf +class meas_itf_listener : public srsue::scell::intra_measure_base::meas_itf { public: typedef struct { @@ -396,11 +396,10 @@ int main(int argc, char** argv) srslog::basic_logger& logger = srslog::fetch_basic_logger("intra_measure"); srslog::init(); - cf_t* baseband_buffer = srsran_vec_cf_malloc(SRSRAN_SF_LEN_MAX); - srsran::rf_timestamp_t ts = {}; - srsue::scell::intra_measure intra_measure(logger); - meas_itf_listener rrc; - srsue::phy_common common(logger); + cf_t* baseband_buffer = srsran_vec_cf_malloc(SRSRAN_SF_LEN_MAX); + srsran::rf_timestamp_t ts = {}; + meas_itf_listener rrc; + srsue::scell::intra_measure_lte intra_measure(logger, rrc); // Simulation only std::vector > test_enb_v; @@ -411,7 +410,6 @@ int main(int argc, char** argv) std::unique_ptr radio = nullptr; // Set Receiver args - common.args = &phy_args; phy_args.estimator_fil_auto = false; phy_args.estimator_fil_order = 4; phy_args.estimator_fil_stddev = 1.0f; @@ -469,7 +467,12 @@ int main(int argc, char** argv) logger.set_level(srslog::str_to_basic_level(intra_meas_log_level)); - intra_measure.init(0, &common, &rrc); + srsue::scell::intra_measure_base::args_t args = {}; + args.len_ms = phy_args.intra_freq_meas_len_ms; + args.period_ms = phy_args.intra_freq_meas_period_ms; + args.rx_gain_offset_db = phy_args.rx_gain_offset; + + intra_measure.init(0, args); intra_measure.set_primary_cell(SRSRAN_MAX(earfcn_dl, 0), cell_base); if (earfcn_dl >= 0) { diff --git a/srsue/test/upper/rrc_meas_test.cc b/srsue/test/upper/rrc_meas_test.cc index 5e2cfce1f..e966ab42e 100644 --- a/srsue/test/upper/rrc_meas_test.cc +++ b/srsue/test/upper/rrc_meas_test.cc @@ -674,18 +674,18 @@ int meas_obj_test() rrc_meas_logger.info("Test7: PHY finds new neighbours in frequency 1 and 2, check RRC instructs to search them"); std::vector phy_meas = {}; - phy_meas.push_back({0, 0, 0.0f, 1, 31}); - phy_meas.push_back({-1, 0, 0.0f, 1, 32}); - phy_meas.push_back({-2, 0, 0.0f, 1, 33}); - phy_meas.push_back({-3, 0, 0.0f, 1, 34}); + phy_meas.push_back({srsran::srsran_rat_t::lte, 0, 0, 0.0f, 1, 31}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -1, 0, 0.0f, 1, 32}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -2, 0, 0.0f, 1, 33}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -3, 0, 0.0f, 1, 34}); rrctest.new_cell_meas(phy_meas); rrctest.run_tti(1); phy_meas = {}; - phy_meas.push_back({-4, 0, 0.0f, 1, 35}); - phy_meas.push_back({-5, 0, 0.0f, 1, 36}); - phy_meas.push_back({-6, 0, 0.0f, 1, 37}); - phy_meas.push_back({1, 0, 0.0f, 1, 30}); - phy_meas.push_back({0, 0, 0.0f, 2, 31}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -4, 0, 0.0f, 1, 35}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -5, 0, 0.0f, 1, 36}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -6, 0, 0.0f, 1, 37}); + phy_meas.push_back({srsran::srsran_rat_t::lte, 1, 0, 0.0f, 1, 30}); + phy_meas.push_back({srsran::srsran_rat_t::lte, 0, 0, 0.0f, 2, 31}); rrctest.new_cell_meas(phy_meas); rrctest.run_tti(1); @@ -806,7 +806,7 @@ void send_report(rrc_test& rrctest, if (earfcn.size() == pci.size()) { e = earfcn[i]; } - phy_meas.push_back({r, -5, 0.0f, e, pci[i]}); + phy_meas.push_back({srsran::srsran_rat_t::lte, r, -5, 0.0f, e, pci[i]}); } rrctest.new_cell_meas(phy_meas); rrctest.run_tti(1);