mirror of https://github.com/pvnis/srsRAN_4G.git
Made intra frequency cell search and measurment generic
parent
648f0af437
commit
60015e7ceb
@ -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 <srsran/common/threads.h>
|
||||
#include <srsran/common/tti_sync_cv.h>
|
||||
#include <srsran/srsran.h>
|
||||
|
||||
#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<phy_meas_t>& 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<uint32_t>& 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<std::mutex> 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<std::mutex> 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<uint32_t> 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
|
@ -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 <srsran/common/common.h>
|
||||
#include <srsran/common/threads.h>
|
||||
#include <srsran/common/tti_sync_cv.h>
|
||||
#include <srsran/srsran.h>
|
||||
|
||||
#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<phy_meas_t>& 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<uint32_t>& 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<uint32_t> 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<std::mutex> 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<std::mutex> 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<cf_t>& 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<cf_t> search_buffer;
|
||||
srsran_ringbuffer_t ring_buffer = {};
|
||||
};
|
||||
|
||||
} // namespace scell
|
||||
} // namespace srsue
|
||||
|
||||
#endif // SRSUE_INTRA_MEASURE_BASE_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<cf_t>& 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
|
@ -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<uint32_t>& 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<uint32_t> 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<uint32_t> 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<phy_meas_t> 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
|
@ -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<uint32_t>& 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<uint32_t> 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
|
@ -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<cf_t>& buffer)
|
||||
{
|
||||
std::set<uint32_t> cells_to_measure = context.active_pci;
|
||||
|
||||
// Detect new cells using PSS/SSS
|
||||
std::set<uint32_t> 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<phy_meas_t> 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
|
Loading…
Reference in New Issue