SRSUE: Added time accurate SCell activation

master
Xavier Arteaga 4 years ago committed by Andre Puschmann
parent f64c268a69
commit c5cb4d9984

@ -31,6 +31,7 @@
#include "srslte/interfaces/ue_interfaces.h" #include "srslte/interfaces/ue_interfaces.h"
#include "srslte/radio/radio.h" #include "srslte/radio/radio.h"
#include "srslte/srslte.h" #include "srslte/srslte.h"
#include "srsue/hdr/phy/scell/scell_state.h"
#include "ta_control.h" #include "ta_control.h"
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
@ -59,14 +60,8 @@ public:
srslte::phy_cfg_mbsfn_t mbsfn_config = {}; srslte::phy_cfg_mbsfn_t mbsfn_config = {};
// SCell EARFCN, PCI, configured and enabled list // Secondary serving cell states
typedef struct { scell::state scell_state;
uint32_t earfcn = 0;
uint32_t pci = 0;
bool configured = false;
bool enabled = false;
} scell_cfg_t;
scell_cfg_t scell_cfg[SRSLTE_MAX_CARRIERS];
// Save last TBS for uplink (mcs >= 28) // Save last TBS for uplink (mcs >= 28)
srslte_ra_tb_t last_ul_tb[SRSLTE_MAX_HARQ_PROC][SRSLTE_MAX_CARRIERS] = {}; srslte_ra_tb_t last_ul_tb[SRSLTE_MAX_HARQ_PROC][SRSLTE_MAX_CARRIERS] = {};
@ -162,9 +157,6 @@ public:
void reset(); void reset();
void reset_radio(); void reset_radio();
/* SCell Management */
void enable_scell(uint32_t cc_idx, bool enable);
void build_mch_table(); void build_mch_table();
void build_mcch_table(); void build_mcch_table();
void set_mcch(); void set_mcch();
@ -240,22 +232,22 @@ public:
private: private:
std::mutex meas_mutex; std::mutex meas_mutex;
float pathloss[SRSLTE_MAX_CARRIERS] = {}; float pathloss[SRSLTE_MAX_CARRIERS] = {};
float cur_pathloss = 0.0f; float cur_pathloss = 0.0f;
float cur_pusch_power = 0.0f; float cur_pusch_power = 0.0f;
float avg_rsrp[SRSLTE_MAX_CARRIERS] = {}; float avg_rsrp[SRSLTE_MAX_CARRIERS] = {};
float avg_rsrp_dbm[SRSLTE_MAX_CARRIERS] = {}; float avg_rsrp_dbm[SRSLTE_MAX_CARRIERS] = {};
float avg_rsrq_db[SRSLTE_MAX_CARRIERS] = {}; float avg_rsrq_db[SRSLTE_MAX_CARRIERS] = {};
float avg_rssi_dbm[SRSLTE_MAX_CARRIERS] = {}; float avg_rssi_dbm[SRSLTE_MAX_CARRIERS] = {};
float avg_cfo_hz[SRSLTE_MAX_CARRIERS] = {}; float avg_cfo_hz[SRSLTE_MAX_CARRIERS] = {};
float rx_gain_offset = 0.0f; float rx_gain_offset = 0.0f;
float avg_sinr_db[SRSLTE_MAX_CARRIERS] = {}; float avg_sinr_db[SRSLTE_MAX_CARRIERS] = {};
float avg_snr_db[SRSLTE_MAX_CARRIERS] = {}; float avg_snr_db[SRSLTE_MAX_CARRIERS] = {};
float avg_noise[SRSLTE_MAX_CARRIERS] = {}; float avg_noise[SRSLTE_MAX_CARRIERS] = {};
float avg_rsrp_neigh[SRSLTE_MAX_CARRIERS] = {}; float avg_rsrp_neigh[SRSLTE_MAX_CARRIERS] = {};
uint32_t pcell_report_period = 0; uint32_t pcell_report_period = 0;
uint32_t rssi_read_cnt = 0; uint32_t rssi_read_cnt = 0;
rsrp_insync_itf* insync_itf = nullptr; rsrp_insync_itf* insync_itf = nullptr;

@ -0,0 +1,234 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLTE_SCELL_STATE_H
#define SRSLTE_SCELL_STATE_H
#include <cinttypes>
#include <mutex>
#include <srslte/common/common.h>
namespace srsue {
namespace scell {
/**
* References
*
* According to 3GPP 36.321 R10 (MAC procedures) section 5.13 Activation/Deactivation of SCells
* An activated cell operation shall include (Summarised):
* - SRS transmissions on the SCell;
* - CQI/PMI/RI/PTI reporting for the SCell;
* - PDCCH monitoring on the SCell;
* - PDCCH monitoring for the SCell
*
* According to 3GPP 36.213 R10 (PHY procedures) section 4.3 Timing for Secondary Cell Activation / Deactivation
* When a UE receives an activation command for a secondary cell in subframe n, the corresponding actions shall be
* applied at subframe n+8. (Summarised)
*/
class state
{
private:
static constexpr uint32_t activation_delay_tti = FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS;
static constexpr uint32_t activation_margin_tti = FDD_HARQ_DELAY_DL_MS;
// SCell EARFCN, PCI, configured and enabled list
struct cfg {
uint32_t earfcn = 0;
uint32_t pci = 0;
enum { none = 0, inactive, active } status = none;
};
std::array<cfg, SRSLTE_MAX_CARRIERS> scell_cfg;
enum { idle = 0, waiting, transition } activation_state = idle;
uint32_t activation_cmd = 0;
uint32_t activation_tti = 0;
mutable std::mutex mutex;
bool _get_cmd_activation(uint32_t cc_idx) const { return ((activation_cmd >> cc_idx) & 0x1) == 0x1; }
bool _tti_greater_or_equal_than(uint32_t a, uint32_t b) const { return TTI_SUB(a, b) < 10240 / 2; }
public:
/**
* A SCell Activation/Deactivation command is received. Stores the new command and the TTI. Also, the internal state
* goes to waiting.
*
* If a previous command was received and not applied, it will discard it.
*
* @param cmd SCell Activation/Deactivation command
* @param tti TTI in which the command was received
*/
void set_activation_deactivation(uint32_t cmd, uint32_t tti)
{
std::unique_lock<std::mutex> lock(mutex);
// Store command
activation_cmd = cmd;
// Command is waiting
activation_state = waiting;
activation_tti = TTI_ADD(tti, activation_delay_tti);
}
/**
* @brief Deactivates all the active SCells
*/
void deactivate_all()
{
for (cfg& e : scell_cfg) {
if (e.status == cfg::active) {
e.status = cfg::inactive;
}
}
}
void run_tti(uint32_t tti)
{
std::unique_lock<std::mutex> lock(mutex);
switch (activation_state) {
case idle:
// waiting for receiving a command, do nothing
break;
case waiting:
// Detect that TTI when the CMD needs to be applied, the activation cannot be done instantly because some
// workers might be currently ongoing, so only update state
if (_tti_greater_or_equal_than(tti, activation_tti)) {
activation_state = transition;
}
break;
case transition:
// Detect when the TTI has increased enough to make sure there arent workers, set the configuration
if (TTI_SUB(tti, activation_tti) >= activation_margin_tti) {
// Reload cell states
for (uint32_t i = 1; i < SRSLTE_MAX_CARRIERS; i++) {
// Get Activation command value
bool activate = _get_cmd_activation(i);
// Apply activation only if the cell was configured
if (scell_cfg[i].status != cfg::none) {
scell_cfg[i].status = activate ? cfg::active : cfg::inactive;
}
}
// Go back to initial state
activation_state = idle;
}
break;
}
}
void configure(uint32_t cc_idx, uint32_t earfcn, uint32_t pci)
{
std::unique_lock<std::mutex> lock(mutex);
if (cc_idx == 0 or cc_idx >= SRSLTE_MAX_CARRIERS) {
ERROR("CC IDX %d out-of-range\n", cc_idx);
return;
}
scell_cfg[cc_idx].status = cfg::inactive;
scell_cfg[cc_idx].earfcn = earfcn;
scell_cfg[cc_idx].pci = pci;
}
bool is_active(uint32_t cc_idx, uint32_t tti) const
{
if (cc_idx == 0) {
return true;
}
if (cc_idx >= SRSLTE_MAX_CARRIERS) {
return false;
}
std::unique_lock<std::mutex> lock(mutex);
// Use stashed activation if the activation is transitioning and the current TTI requires new value
if (activation_state == transition and scell_cfg[cc_idx].status != cfg::none and
_tti_greater_or_equal_than(tti, activation_tti)) {
return _get_cmd_activation(cc_idx);
}
return scell_cfg[cc_idx].status == cfg::active;
}
bool is_configured(uint32_t cc_idx) const
{
if (cc_idx == 0) {
return true;
}
if (cc_idx >= SRSLTE_MAX_CARRIERS) {
return false;
}
std::unique_lock<std::mutex> lock(mutex);
return scell_cfg[cc_idx].status != cfg::none;
}
void reset()
{
std::unique_lock<std::mutex> lock(mutex);
activation_state = idle;
for (cfg& e : scell_cfg) {
e.status = cfg::none;
e.earfcn = 0;
e.pci = UINT32_MAX;
}
}
uint32_t get_pci(uint32_t cc_idx)
{
std::unique_lock<std::mutex> lock(mutex);
if (cc_idx == 0 or cc_idx >= SRSLTE_MAX_CARRIERS) {
ERROR("CC IDX %d out-of-range\n", cc_idx);
return 0;
}
return scell_cfg[cc_idx].pci;
}
uint32_t get_earfcn(uint32_t cc_idx)
{
std::unique_lock<std::mutex> lock(mutex);
if (cc_idx == 0 or cc_idx >= SRSLTE_MAX_CARRIERS) {
ERROR("CC IDX %d out-of-range\n", cc_idx);
return 0;
}
return scell_cfg[cc_idx].earfcn;
}
};
} // namespace scell
} // namespace srsue
#endif // SRSLTE_SCELL_STATE_H

@ -847,7 +847,7 @@ void cc_worker::set_uci_ack(srslte_uci_data_t* uci_data,
// Only PCell generates ACK for all SCell // Only PCell generates ACK for all SCell
for (uint32_t i = 0; i < phy->args->nof_carriers; i++) { for (uint32_t i = 0; i < phy->args->nof_carriers; i++) {
if (i == 0 || phy->scell_cfg[i].configured) { if (phy->scell_state.is_configured(i)) {
phy->get_dl_pending_ack(&sf_cfg_ul, i, &ack_info.cc[i]); phy->get_dl_pending_ack(&sf_cfg_ul, i, &ack_info.cc[i]);
nof_configured_carriers++; nof_configured_carriers++;
} }

@ -246,8 +246,8 @@ void phy::get_metrics(phy_metrics_t* m)
m->info[0].dl_earfcn = dl_earfcn; m->info[0].dl_earfcn = dl_earfcn;
for (uint32_t i = 1; i < args.nof_carriers; i++) { for (uint32_t i = 1; i < args.nof_carriers; i++) {
m->info[i].dl_earfcn = common.scell_cfg[i].earfcn; m->info[i].dl_earfcn = common.scell_state.get_earfcn(i);
m->info[i].pci = common.scell_cfg[i].pci; m->info[i].pci = common.scell_state.get_pci(i);
} }
common.get_ch_metrics(m->ch); common.get_ch_metrics(m->ch);
@ -269,20 +269,12 @@ void phy::set_timeadv(uint32_t ta_cmd)
void phy::deactivate_scells() void phy::deactivate_scells()
{ {
for (uint32_t i = 1; i < SRSLTE_MAX_CARRIERS; i++) { common.scell_state.deactivate_all();
common.enable_scell(i, false);
}
} }
void phy::set_activation_deactivation_scell(uint32_t cmd, uint32_t tti) void phy::set_activation_deactivation_scell(uint32_t cmd, uint32_t tti)
{ {
/* Implements 3GPP 36.321 section 6.1.3.8. Activation/Deactivation MAC Control Element*/ common.scell_state.set_activation_deactivation(cmd, tti);
for (uint32_t i = 1; i < SRSLTE_MAX_CARRIERS; i++) {
bool activated = ((cmd >> i) & 0x1u) == 0x1u;
/* Enable actual cell */
common.enable_scell(i, activated);
}
} }
void phy::configure_prach_params() void phy::configure_prach_params()
@ -527,16 +519,13 @@ bool phy::set_scell(srslte_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn)
return false; return false;
} }
bool earfcn_is_different = common.scell_cfg[cc_idx].earfcn != earfcn; bool earfcn_is_different = common.scell_state.get_earfcn(cc_idx) != earfcn;
// Set inter-frequency measurement // Set inter-frequency measurement
sfsync.set_inter_frequency_measurement(cc_idx, earfcn, cell_info); sfsync.set_inter_frequency_measurement(cc_idx, earfcn, cell_info);
// Store SCell earfcn and pci // Store secondary serving cell EARFCN and PCI
common.scell_cfg[cc_idx].earfcn = earfcn; common.scell_state.configure(cc_idx, earfcn, cell_info.id);
common.scell_cfg[cc_idx].pci = cell_info.id;
common.scell_cfg[cc_idx].configured = true;
common.scell_cfg[cc_idx].enabled = false;
// Reset cell configuration // Reset cell configuration
for (uint32_t i = 0; i < nof_workers; i++) { for (uint32_t i = 0; i < nof_workers; i++) {

@ -742,7 +742,7 @@ void phy_common::update_measurements(uint32_t
set_ch_metrics(cc_idx, ch); set_ch_metrics(cc_idx, ch);
// Prepare measurements for serving cells // Prepare measurements for serving cells
bool active = (cc_idx == 0 || scell_cfg[cc_idx].configured); bool active = scell_state.is_configured(cc_idx);
if (active && ((sf_cfg_dl.tti % pcell_report_period) == pcell_report_period - 1)) { if (active && ((sf_cfg_dl.tti % pcell_report_period) == pcell_report_period - 1)) {
rrc_interface_phy_lte::phy_meas_t meas = {}; rrc_interface_phy_lte::phy_meas_t meas = {};
meas.rsrp = avg_rsrp_dbm[cc_idx]; meas.rsrp = avg_rsrp_dbm[cc_idx];
@ -753,8 +753,8 @@ void phy_common::update_measurements(uint32_t
if (cc_idx == 0) { if (cc_idx == 0) {
meas.pci = cell.id; meas.pci = cell.id;
} else { } else {
meas.earfcn = scell_cfg[cc_idx].earfcn; meas.earfcn = scell_state.get_earfcn(cc_idx);
meas.pci = scell_cfg[cc_idx].pci; meas.pci = scell_state.get_pci(cc_idx);
} }
serving_cells.push_back(meas); serving_cells.push_back(meas);
} }
@ -903,7 +903,7 @@ void phy_common::reset()
ZERO_OBJECT(avg_rsrp); ZERO_OBJECT(avg_rsrp);
ZERO_OBJECT(avg_rsrp_dbm); ZERO_OBJECT(avg_rsrp_dbm);
ZERO_OBJECT(avg_rsrq_db); ZERO_OBJECT(avg_rsrq_db);
ZERO_OBJECT(scell_cfg); scell_state.reset();
reset_neighbour_cells(); reset_neighbour_cells();
@ -1125,19 +1125,4 @@ bool phy_common::is_mbsfn_sf(srslte_mbsfn_cfg_t* cfg, uint32_t phy_tti)
return is_mch_subframe(cfg, phy_tti); return is_mch_subframe(cfg, phy_tti);
} }
void phy_common::enable_scell(uint32_t cc_idx, bool enable)
{
if (cc_idx < SRSLTE_MAX_CARRIERS) {
if (scell_cfg[cc_idx].configured) {
scell_cfg[cc_idx].enabled = enable;
} else {
if (enable) {
Error("Leaving SCell %s. cc_idx=%d has not been configured.\n",
scell_cfg[cc_idx].enabled ? "enabled" : "disabled",
cc_idx);
}
}
}
}
} // namespace srsue } // namespace srsue

@ -205,7 +205,7 @@ void sf_worker::work_imp()
if (carrier_idx == 0 && phy->is_mbsfn_sf(&mbsfn_cfg, tti)) { if (carrier_idx == 0 && phy->is_mbsfn_sf(&mbsfn_cfg, tti)) {
cc_workers[0]->work_dl_mbsfn(mbsfn_cfg); // Don't do chest_ok in mbsfn since it trigger measurements cc_workers[0]->work_dl_mbsfn(mbsfn_cfg); // Don't do chest_ok in mbsfn since it trigger measurements
} else { } else {
if ((carrier_idx == 0) || (phy->scell_cfg[carrier_idx].enabled && phy->scell_cfg[carrier_idx].configured)) { if (phy->scell_state.is_active(carrier_idx, tti)) {
rx_signal_ok = cc_workers[carrier_idx]->work_dl_regular(); rx_signal_ok = cc_workers[carrier_idx]->work_dl_regular();
} }
} }
@ -230,14 +230,14 @@ void sf_worker::work_imp()
for (uint32_t carrier_idx = 0; for (uint32_t carrier_idx = 0;
carrier_idx < phy->args->nof_carriers and not uci_data.cfg.cqi.data_enable and uci_data.cfg.cqi.ri_len == 0; carrier_idx < phy->args->nof_carriers and not uci_data.cfg.cqi.data_enable and uci_data.cfg.cqi.ri_len == 0;
carrier_idx++) { carrier_idx++) {
if (carrier_idx == 0 or phy->scell_cfg[carrier_idx].configured) { if (phy->scell_state.is_active(carrier_idx, TTI_TX(tti))) {
cc_workers[carrier_idx]->set_uci_periodic_cqi(&uci_data); cc_workers[carrier_idx]->set_uci_periodic_cqi(&uci_data);
} }
} }
// Loop through all carriers // Loop through all carriers
for (uint32_t carrier_idx = 0; carrier_idx < phy->args->nof_carriers; carrier_idx++) { for (uint32_t carrier_idx = 0; carrier_idx < phy->args->nof_carriers; carrier_idx++) {
if ((carrier_idx == 0) || (phy->scell_cfg[carrier_idx].enabled && phy->scell_cfg[carrier_idx].configured)) { if (phy->scell_state.is_active(carrier_idx, tti)) {
tx_signal_ready |= cc_workers[carrier_idx]->work_ul(uci_cc_idx == carrier_idx ? &uci_data : nullptr); tx_signal_ready |= cc_workers[carrier_idx]->work_ul(uci_cc_idx == carrier_idx ? &uci_data : nullptr);
// Set signal pointer based on offset // Set signal pointer based on offset

@ -480,6 +480,9 @@ void sync::run_camping_in_sync_state(sf_worker* worker, srslte::rf_buffer_t& syn
worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSLTE_SF_LEN_PRB(cell.nof_prb)] : nullptr, prach_power); worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSLTE_SF_LEN_PRB(cell.nof_prb)] : nullptr, prach_power);
// Execute Serving Cell state FSM
worker_com->scell_state.run_tti(tti);
// Set CFO for all Carriers // Set CFO for all Carriers
for (uint32_t cc = 0; cc < worker_com->args->nof_carriers; cc++) { for (uint32_t cc = 0; cc < worker_com->args->nof_carriers; cc++) {
worker->set_cfo_unlocked(cc, get_tx_cfo()); worker->set_cfo_unlocked(cc, get_tx_cfo());

Loading…
Cancel
Save