Made intra frequency cell search and measurment generic

This commit is contained in:
Xavier Arteaga 2021-05-13 12:30:54 +02:00 committed by Xavier Arteaga
parent 648f0af437
commit 60015e7ceb
11 changed files with 676 additions and 498 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<std::unique_ptr<scell::intra_measure> > intra_freq_meas;
search search_p;
sfn_sync sfn_p;
std::vector<std::unique_ptr<scell::intra_measure_lte> > intra_freq_meas;
// Pointers to other classes
stack_interface_phy_lte* stack = nullptr;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<scell::intra_measure>(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<scell::intra_measure_lte>(q));
}
// Allocate Secondary serving cell synchronization

View File

@ -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 <boost/program_options.hpp>
#include <boost/program_options/parsers.hpp>
#include <iostream>
@ -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<std::unique_ptr<test_enb> > test_enb_v;
@ -411,7 +410,6 @@ int main(int argc, char** argv)
std::unique_ptr<srsran::radio> 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) {

View File

@ -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_t> 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);