|
|
|
/**
|
|
|
|
*
|
|
|
|
* \section COPYRIGHT
|
|
|
|
*
|
|
|
|
* Copyright 2013-2020 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 "lte_ttcn3_phy.h"
|
|
|
|
|
|
|
|
namespace srsue {
|
|
|
|
|
|
|
|
#define SUITABLE_CELL_RS_EPRE (-97.0) // Threshold for cell-search and cell-select
|
|
|
|
#define NON_SUITABLE_CELL_RS_EPRE (-115.0) // Threshold until sync can be maintained
|
|
|
|
#define NON_SUITABLE_OFF_CELL_RS_EPRE (-145.0)
|
|
|
|
#define SUITABLE_NEIGHBOR_INTRAFREQ_RS_EPRE (-91)
|
|
|
|
#define DEFAULT_RSRQ (-3.0)
|
|
|
|
|
|
|
|
lte_ttcn3_phy::lte_ttcn3_phy() : logger(srslog::fetch_basic_logger("PHY")) {}
|
|
|
|
|
|
|
|
int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, syssim_interface_phy* syssim_)
|
|
|
|
{
|
|
|
|
stack = stack_;
|
|
|
|
syssim = syssim_;
|
|
|
|
|
|
|
|
return init(args_);
|
|
|
|
}
|
|
|
|
|
|
|
|
int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srslte::radio_interface_phy* radio_)
|
|
|
|
{
|
|
|
|
return init(args_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ue_phy_base interface
|
|
|
|
int lte_ttcn3_phy::init(const phy_args_t& args_)
|
|
|
|
{
|
|
|
|
logger.set_level(srslog::str_to_basic_level(args_.log.phy_level));
|
|
|
|
logger.set_hex_dump_max_size(-1);
|
|
|
|
|
|
|
|
return SRSLTE_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::stop(){};
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::wait_initialize() {}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::start_plot() {}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::get_metrics(phy_metrics_t* m) {}
|
|
|
|
|
|
|
|
// The interface for the SS
|
|
|
|
void lte_ttcn3_phy::set_cell_map(const cell_list_t& cells_)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
cells = cells_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::set_config_tdd(srslte_tdd_config_t& tdd_config) {}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::enable_pregen_signals(bool enable)
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::deactivate_scells()
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::set_activation_deactivation_scell(uint32_t cmd, uint32_t tti)
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool lte_ttcn3_phy::set_config(srslte::phy_cfg_t config, uint32_t cc_idx_)
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
task_sched.defer_task([this]() { stack->set_config_complete(true); });
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool lte_ttcn3_phy::set_scell(srslte_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn)
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
task_sched.defer_task([this]() { stack->set_scell_complete(true); });
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Measurements interface
|
|
|
|
void lte_ttcn3_phy::meas_stop() {}
|
|
|
|
|
|
|
|
// Cell search and selection procedures
|
|
|
|
// Note that in contrast to a real PHY, we have visibility of all existing cells
|
|
|
|
// configured by the SS, including the ones that we should not even detect because
|
|
|
|
// their power is too weak. The cell search should only report the cells that
|
|
|
|
// are actually visible though.
|
|
|
|
bool lte_ttcn3_phy::cell_search()
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
|
|
|
|
logger.info("Running cell search in PHY");
|
|
|
|
|
|
|
|
rrc_interface_phy_lte::cell_search_ret_t ret = {};
|
|
|
|
ret.found = rrc_interface_phy_lte::cell_search_ret_t::CELL_NOT_FOUND;
|
|
|
|
phy_cell_t found_cell = {};
|
|
|
|
|
|
|
|
if (not cells.empty() && cell_idx < cells.size()) {
|
|
|
|
// only find suitable cells
|
|
|
|
if (cells[cell_idx].power >= SUITABLE_CELL_RS_EPRE) {
|
|
|
|
logger.info("Found Cell: EARFCN=%d CellId=%d power=%.2f",
|
|
|
|
cells[cell_idx].earfcn,
|
|
|
|
cells[cell_idx].info.id,
|
|
|
|
cells[cell_idx].power);
|
|
|
|
found_cell.earfcn = cells[cell_idx].earfcn;
|
|
|
|
found_cell.pci = cells[cell_idx].info.id;
|
|
|
|
ret.found = rrc_interface_phy_lte::cell_search_ret_t::CELL_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
// advance index
|
|
|
|
cell_idx++;
|
|
|
|
|
|
|
|
if (cell_idx < cells.size()) {
|
|
|
|
// more cells will be reported
|
|
|
|
ret.last_freq = rrc_interface_phy_lte::cell_search_ret_t::MORE_FREQS;
|
|
|
|
} else {
|
|
|
|
// all available cells have been reported, reset cell index
|
|
|
|
ret.last_freq = rrc_interface_phy_lte::cell_search_ret_t::NO_MORE_FREQS;
|
|
|
|
cell_idx = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
logger.warning("No cells configured yet.");
|
|
|
|
}
|
|
|
|
|
|
|
|
stack->cell_search_complete(ret, found_cell);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool lte_ttcn3_phy::cell_select(phy_cell_t rrc_cell)
|
|
|
|
{
|
|
|
|
// try to find RRC cell in current cell map
|
|
|
|
for (auto& cell : cells) {
|
|
|
|
if (cell.info.id == rrc_cell.pci && cell.earfcn == rrc_cell.earfcn) {
|
|
|
|
if (cell.power >= SUITABLE_CELL_RS_EPRE) {
|
|
|
|
pcell = cell;
|
|
|
|
pcell_set = true;
|
|
|
|
syssim->select_cell(pcell.info);
|
|
|
|
logger.info("Select PCell with %.2f on PCI=%d on EARFCN=%d.", cell.power, rrc_cell.pci, rrc_cell.earfcn);
|
|
|
|
} else {
|
|
|
|
pcell_set = false;
|
|
|
|
logger.error("Power of selected cell too low (%.2f < %.2f)", cell.power, SUITABLE_CELL_RS_EPRE);
|
|
|
|
}
|
|
|
|
|
|
|
|
stack->cell_select_complete(pcell_set);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.error("Couldn't find RRC cell with PCI=%d on EARFCN=%d in cell map.", rrc_cell.pci, rrc_cell.earfcn);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool lte_ttcn3_phy::cell_is_camping()
|
|
|
|
{
|
|
|
|
if (pcell_set) {
|
|
|
|
logger.info("pcell.power=%2.f", pcell.power);
|
|
|
|
return (pcell.power >= SUITABLE_CELL_RS_EPRE);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The interface for MAC (called from Stack thread context)
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm, float ta_base_sec)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
logger.info("Sending PRACH with preamble %d on PCID=%d", preamble_idx, pcell.info.id);
|
|
|
|
prach_tti_tx = current_tti;
|
|
|
|
ra_trans_cnt++;
|
|
|
|
|
|
|
|
syssim->prach_indication(preamble_idx, pcell.info.id);
|
|
|
|
};
|
|
|
|
|
|
|
|
std::string lte_ttcn3_phy::get_type()
|
|
|
|
{
|
|
|
|
return "lte_ttcn3";
|
|
|
|
}
|
|
|
|
|
|
|
|
phy_interface_mac_lte::prach_info_t lte_ttcn3_phy::prach_get_info()
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
prach_info_t info = {};
|
|
|
|
if (prach_tti_tx != -1) {
|
|
|
|
info.is_transmitted = true;
|
|
|
|
info.tti_ra = prach_tti_tx;
|
|
|
|
}
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Indicates the transmission of a SR signal in the next opportunity */
|
|
|
|
void lte_ttcn3_phy::sr_send()
|
|
|
|
{
|
|
|
|
sr_pending = true;
|
|
|
|
sr_tx_tti = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lte_ttcn3_phy::sr_last_tx_tti()
|
|
|
|
{
|
|
|
|
return sr_tx_tti;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The RAT-agnostic interface for MAC
|
|
|
|
|
|
|
|
/* Sets a C-RNTI allowing the PHY to pregenerate signals if necessary */
|
|
|
|
void lte_ttcn3_phy::set_crnti(uint16_t rnti)
|
|
|
|
{
|
|
|
|
logger.debug("Set Temp-RNTI=%d, pregen not used", rnti);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Time advance commands */
|
|
|
|
void lte_ttcn3_phy::set_timeadv_rar(uint32_t ta_cmd)
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::set_timeadv(uint32_t ta_cmd)
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sets RAR grant payload
|
|
|
|
void lte_ttcn3_phy::set_rar_grant(uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN], uint16_t rnti)
|
|
|
|
{
|
|
|
|
// Empty, SYSSIM knows when to provide UL grant for Msg3
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called from the SYSSIM to configure the current TTI
|
|
|
|
void lte_ttcn3_phy::set_current_tti(uint32_t tti)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
|
|
|
|
current_tti = tti;
|
|
|
|
run_tti();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called from MAC to retrieve the current TTI
|
|
|
|
uint32_t lte_ttcn3_phy::get_current_tti()
|
|
|
|
{
|
|
|
|
return current_tti;
|
|
|
|
}
|
|
|
|
|
|
|
|
float lte_ttcn3_phy::get_phr()
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
return 0.1;
|
|
|
|
}
|
|
|
|
|
|
|
|
float lte_ttcn3_phy::get_pathloss_db()
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
return 85.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only provides a new UL grant, Tx is then triggered
|
|
|
|
// Calling function hold mutex
|
|
|
|
void lte_ttcn3_phy::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t ul_mac_grant)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
|
|
|
|
mac_interface_phy_lte::tb_action_ul_t ul_action = {};
|
|
|
|
|
|
|
|
// Deliver grant and retrieve payload
|
|
|
|
stack->new_grant_ul(cc_idx, ul_mac_grant, &ul_action);
|
|
|
|
|
|
|
|
// Deliver MAC PDU to SYSSIM
|
|
|
|
if (ul_action.tb.enabled and ul_action.tb.payload != nullptr) {
|
|
|
|
syssim->tx_pdu(ul_action.tb.payload, ul_mac_grant.tb.tbs, ul_mac_grant.rnti);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Provides DL grant, copy data into DL action and pass up to MAC
|
|
|
|
void lte_ttcn3_phy::new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t dl_grant, const uint8_t* data)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
|
|
|
|
if (data == nullptr) {
|
|
|
|
logger.error("Invalid data buffer passed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// pass grant to MAC to retrieve DL action
|
|
|
|
mac_interface_phy_lte::tb_action_dl_t dl_action = {};
|
|
|
|
|
|
|
|
stack->new_grant_dl(cc_idx, dl_grant, &dl_action);
|
|
|
|
|
|
|
|
bool dl_ack[SRSLTE_MAX_CODEWORDS] = {};
|
|
|
|
|
|
|
|
if (dl_action.tb[0].enabled && dl_action.tb[0].payload != nullptr) {
|
|
|
|
logger.info(data,
|
|
|
|
dl_grant.tb[0].tbs,
|
|
|
|
"TB received rnti=%d, tti=%d, n_bytes=%d",
|
|
|
|
dl_grant.rnti,
|
|
|
|
current_tti,
|
|
|
|
dl_grant.tb[0].tbs);
|
|
|
|
|
|
|
|
if (dl_action.generate_ack) {
|
|
|
|
logger.debug("Calling generate ACK callback");
|
|
|
|
// action.generate_ack_callback(action.generate_ack_callback_arg);
|
|
|
|
}
|
|
|
|
memcpy(dl_action.tb->payload, data, dl_grant.tb[0].tbs);
|
|
|
|
|
|
|
|
// ACK first TB and pass up
|
|
|
|
dl_ack[0] = true;
|
|
|
|
|
|
|
|
logger.info("TB processed correctly");
|
|
|
|
} else {
|
|
|
|
logger.error("Couldn't get buffer for TB");
|
|
|
|
}
|
|
|
|
|
|
|
|
stack->tb_decoded(cc_idx, dl_grant, dl_ack);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::radio_overflow()
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::radio_failure()
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calling function set_tti() is holding mutex
|
|
|
|
void lte_ttcn3_phy::run_tti()
|
|
|
|
{
|
|
|
|
// send report for all cells stronger than non-suitable cell RS
|
|
|
|
std::vector<phy_meas_t> phy_meas;
|
|
|
|
for (auto& cell : cells) {
|
|
|
|
if (cell.power >= NON_SUITABLE_CELL_RS_EPRE) {
|
|
|
|
phy_meas_t m = {};
|
|
|
|
m.pci = cell.info.id;
|
|
|
|
m.earfcn = cell.earfcn;
|
|
|
|
m.rsrp = cell.power;
|
|
|
|
m.rsrq = DEFAULT_RSRQ;
|
|
|
|
|
|
|
|
// Measurement for PCell needs to have EARFCN set to 0
|
|
|
|
if (pcell_set && m.earfcn == pcell.earfcn && m.pci == pcell.info.id) {
|
|
|
|
logger.debug("Creating Pcell measurement for PCI=%d, EARFCN=%d with RSRP=%.2f", m.pci, m.earfcn, m.rsrp);
|
|
|
|
m.earfcn = 0;
|
|
|
|
} else {
|
|
|
|
logger.debug("Create cell measurement for PCI=%d, EARFCN=%d with RSRP=%.2f", m.pci, m.earfcn, m.rsrp);
|
|
|
|
}
|
|
|
|
phy_meas.push_back(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not phy_meas.empty()) {
|
|
|
|
stack->new_cell_meas(phy_meas);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if Pcell is in sync
|
|
|
|
if (pcell_set) {
|
|
|
|
for (auto& cell : cells) {
|
|
|
|
if (cell.info.id == pcell.info.id) {
|
|
|
|
// consider Pcell in-sync until reaching threshold
|
|
|
|
if (cell.power >= NON_SUITABLE_CELL_RS_EPRE) {
|
|
|
|
logger.debug("PCell id=%d power=%.2f -> sync", pcell.info.id, cell.power);
|
|
|
|
stack->in_sync();
|
|
|
|
} else {
|
|
|
|
logger.debug("PCell id=%d power=%.2f -> out of sync", pcell.info.id, cell.power);
|
|
|
|
stack->out_of_sync();
|
|
|
|
}
|
|
|
|
break; // make sure to call stack only once
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.set_context(current_tti);
|
|
|
|
|
|
|
|
// Check for SR
|
|
|
|
if (sr_pending) {
|
|
|
|
syssim->sr_req(current_tti);
|
|
|
|
sr_pending = false;
|
|
|
|
sr_tx_tti = current_tti;
|
|
|
|
}
|
|
|
|
|
|
|
|
task_sched.run_pending_tasks();
|
|
|
|
}
|
|
|
|
|
|
|
|
void lte_ttcn3_phy::set_cells_to_meas(uint32_t earfcn, const std::set<uint32_t>& pci)
|
|
|
|
{
|
|
|
|
logger.debug("%s not implemented.", __FUNCTION__);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace srsue
|