You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

308 lines
9.1 KiB
C++

/**
*
* \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 "srsenb/hdr/phy/txrx.h"
#include "srslte/common/threads.h"
#include "srslte/phy/channel/channel.h"
#include <sstream>
#include <assert.h>
using namespace std;
using namespace asn1::rrc;
namespace srsenb {
void phy_common::reset()
{
for (auto& q : ul_grants) {
for (auto& g : q) {
g = {};
}
}
}
bool phy_common::init(const phy_cell_cfg_list_t& cell_list_,
const phy_cell_cfg_list_nr_t& cell_list_nr_,
srslte::radio_interface_phy* radio_h_,
stack_interface_phy_lte* stack_)
{
radio = radio_h_;
stack = stack_;
cell_list_lte = cell_list_;
cell_list_nr = cell_list_nr_;
pthread_mutex_init(&mtch_mutex, nullptr);
pthread_cond_init(&mtch_cvar, nullptr);
// Instantiate DL channel emulator
if (params.dl_channel_args.enable) {
dl_channel = srslte::channel_ptr(
new srslte::channel(params.dl_channel_args, get_nof_rf_channels(), srslog::fetch_basic_logger("PHY")));
dl_channel->set_srate((uint32_t)srslte_sampling_freq_hz(cell_list_lte[0].cell.nof_prb));
dl_channel->set_signal_power_dBfs(srslte_enb_dl_get_maximum_signal_power_dBfs(cell_list_lte[0].cell.nof_prb));
}
// Create grants
for (auto& q : ul_grants) {
q.resize(cell_list_lte.size());
}
// Set UE PHY data-base stack and configuration
ue_db.init(stack, params, cell_list_lte);
reset();
return true;
}
void phy_common::stop()
{
semaphore.wait_all();
}
void phy_common::clear_grants(uint16_t rnti)
{
std::lock_guard<std::mutex> lock(grant_mutex);
// remove any pending dci for each subframe
for (auto& list : ul_grants) {
for (auto& q : list) {
for (uint32_t j = 0; j < q.nof_grants; j++) {
if (q.pusch[j].dci.rnti == rnti) {
q.pusch[j].dci.rnti = 0;
}
}
}
}
}
const stack_interface_phy_lte::ul_sched_list_t& phy_common::get_ul_grants(uint32_t tti)
{
std::lock_guard<std::mutex> lock(grant_mutex);
return ul_grants[tti];
}
void phy_common::set_ul_grants(uint32_t tti, const stack_interface_phy_lte::ul_sched_list_t& ul_grant_list)
{
std::lock_guard<std::mutex> lock(grant_mutex);
ul_grants[tti] = ul_grant_list;
}
/* The transmission of UL subframes must be in sequence. The correct sequence is guaranteed by a chain of N semaphores,
* one per TTI%nof_workers. Each threads waits for the semaphore for the current thread and after transmission allows
* next TTI to be transmitted
*
* Each worker uses this function to indicate that all processing is done and data is ready for transmission or
* there is no transmission at all (tx_enable). In that case, the end of burst message will be sent to the radio
*/
void phy_common::worker_end(void* tx_sem_id, srslte::rf_buffer_t& buffer, srslte::rf_timestamp_t& tx_time, bool is_nr)
{
// Wait for the green light to transmit in the current TTI
semaphore.wait(tx_sem_id);
// If this is for NR, save Tx buffers...
if (is_nr) {
nr_tx_buffer = buffer;
nr_tx_buffer_ready = true;
semaphore.release();
return;
}
// ... otherwise, append NR base-band from saved buffer if available
if (nr_tx_buffer_ready) {
uint32_t j = 0;
for (uint32_t i = 0; i < SRSLTE_MAX_CHANNELS; i++) {
if (buffer.get(i) == nullptr) {
buffer.set(i, nr_tx_buffer.get(j));
j++;
}
}
nr_tx_buffer_ready = false;
}
// Run DL channel emulator if created
if (dl_channel) {
dl_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), buffer.get_nof_samples(), tx_time.get(0));
}
// Always transmit on single radio
radio->tx(buffer, tx_time);
// Allow next TTI to transmit
semaphore.release();
}
void phy_common::set_mch_period_stop(uint32_t stop)
{
pthread_mutex_lock(&mtch_mutex);
have_mtch_stop = true;
mch_period_stop = stop;
pthread_cond_signal(&mtch_cvar);
pthread_mutex_unlock(&mtch_mutex);
}
void phy_common::configure_mbsfn(srslte::phy_cfg_mbsfn_t* cfg)
{
mbsfn = *cfg;
build_mch_table();
build_mcch_table();
sib13_configured = true;
mcch_configured = true;
}
void phy_common::build_mch_table()
{
// First reset tables
ZERO_OBJECT(mcch_table);
// 40 element table represents 4 frames (40 subframes)
uint32_t nof_sfs = 0;
if (mbsfn.mbsfn_subfr_cnfg.nof_alloc_subfrs == srslte::mbsfn_sf_cfg_t::sf_alloc_type_t::one_frame) {
generate_mch_table(&mch_table[0], (uint32_t)mbsfn.mbsfn_subfr_cnfg.sf_alloc, 1);
nof_sfs = 10;
} else if (mbsfn.mbsfn_subfr_cnfg.nof_alloc_subfrs == srslte::mbsfn_sf_cfg_t::sf_alloc_type_t::four_frames) {
generate_mch_table(&mch_table[0], (uint32_t)mbsfn.mbsfn_subfr_cnfg.sf_alloc, 4);
nof_sfs = 40;
} else {
fprintf(stderr, "No valid SF alloc\n");
}
// Debug
std::stringstream ss;
ss << "|";
for (uint32_t j = 0; j < 40; j++) {
ss << (int)mch_table[j] << "|";
}
stack->set_sched_dl_tti_mask(mch_table, nof_sfs);
}
void phy_common::build_mcch_table()
{
ZERO_OBJECT(mcch_table);
generate_mcch_table(mcch_table, static_cast<uint32_t>(mbsfn.mbsfn_area_info.mcch_cfg.sf_alloc_info));
std::stringstream ss;
ss << "|";
for (uint32_t j = 0; j < 10; j++) {
ss << (int)mcch_table[j] << "|";
}
}
bool phy_common::is_mcch_subframe(srslte_mbsfn_cfg_t* cfg, uint32_t phy_tti)
{
uint32_t sfn; // System Frame Number
uint8_t sf; // Subframe
uint8_t offset;
uint8_t period;
sfn = phy_tti / 10;
sf = phy_tti % 10;
if (sib13_configured) {
// mbsfn_area_info_r9_s* area_info = &mbsfn.mbsfn_area_info;
srslte::mbsfn_area_info_t* area_info = &mbsfn.mbsfn_area_info;
offset = area_info->mcch_cfg.mcch_offset;
period = enum_to_number(area_info->mcch_cfg.mcch_repeat_period);
if ((sfn % period == offset) && mcch_table[sf] > 0) {
cfg->mbsfn_area_id = area_info->mbsfn_area_id;
cfg->non_mbsfn_region_length = enum_to_number(area_info->non_mbsfn_region_len);
cfg->mbsfn_mcs = enum_to_number(area_info->mcch_cfg.sig_mcs);
cfg->enable = true;
cfg->is_mcch = true;
have_mtch_stop = false;
return true;
}
}
return false;
}
bool phy_common::is_mch_subframe(srslte_mbsfn_cfg_t* cfg, uint32_t phy_tti)
{
uint32_t sfn; // System Frame Number
uint8_t sf; // Subframe
uint8_t offset;
uint8_t period;
sfn = phy_tti / 10;
sf = phy_tti % 10;
// Set some defaults
cfg->mbsfn_area_id = 0;
cfg->non_mbsfn_region_length = 1;
cfg->mbsfn_mcs = 2;
cfg->enable = false;
cfg->is_mcch = false;
// Check for MCCH
if (is_mcch_subframe(cfg, phy_tti)) {
return true;
}
if (not mcch_configured) {
return false;
}
// Not MCCH, check for MCH
srslte::mbsfn_sf_cfg_t* subfr_cnfg = &mbsfn.mbsfn_subfr_cnfg;
srslte::mbsfn_area_info_t* area_info = &mbsfn.mbsfn_area_info;
offset = subfr_cnfg->radioframe_alloc_offset;
period = enum_to_number(subfr_cnfg->radioframe_alloc_period);
if (subfr_cnfg->nof_alloc_subfrs == srslte::mbsfn_sf_cfg_t::sf_alloc_type_t::one_frame) {
if ((sfn % period == offset) && (mch_table[sf] > 0)) {
if (sib13_configured) {
cfg->mbsfn_area_id = area_info->mbsfn_area_id;
cfg->non_mbsfn_region_length = enum_to_number(area_info->non_mbsfn_region_len);
if (mcch_configured) {
// Iterate through PMCH configs to see which one applies in the current frame
uint32_t frame_alloc_idx = sfn % enum_to_number(mbsfn.mcch.common_sf_alloc_period);
uint32_t mbsfn_per_frame = mbsfn.mcch.pmch_info_list[0].sf_alloc_end /
+enum_to_number(mbsfn.mcch.pmch_info_list[0].mch_sched_period);
uint32_t sf_alloc_idx = frame_alloc_idx * mbsfn_per_frame + ((sf < 4) ? sf - 1 : sf - 3);
while (!have_mtch_stop) {
pthread_cond_wait(&mtch_cvar, &mtch_mutex);
}
for (uint32_t i = 0; i < mbsfn.mcch.nof_pmch_info; i++) {
if (sf_alloc_idx <= mch_period_stop) {
cfg->mbsfn_mcs = mbsfn.mcch.pmch_info_list[i].data_mcs;
cfg->enable = true;
}
}
}
}
return true;
}
} else if (subfr_cnfg->nof_alloc_subfrs == srslte::mbsfn_sf_cfg_t::sf_alloc_type_t::four_frames) {
uint8_t idx = sfn % period;
if ((idx >= offset) && (idx < offset + 4)) {
if (mch_table[(idx * 10) + sf] > 0) {
if (sib13_configured) {
cfg->mbsfn_area_id = area_info->mbsfn_area_id;
cfg->non_mbsfn_region_length = enum_to_number(area_info->non_mbsfn_region_len);
// TODO: check for MCCH configuration, set MCS and decode
}
return true;
}
}
}
return false;
}
bool phy_common::is_mbsfn_sf(srslte_mbsfn_cfg_t* cfg, uint32_t phy_tti)
{
return is_mch_subframe(cfg, phy_tti);
}
} // namespace srsenb