separated sched tests based on output into a separate file. Added scheduler random tester to unit tests

This commit is contained in:
Francisco Paisana 2020-01-28 20:07:01 +00:00
parent f6ed99b71b
commit c537d38650
7 changed files with 326 additions and 191 deletions

View File

@ -32,6 +32,25 @@
namespace srslte {
class test_log_singleton
{
public:
static srslte::log* get_log() { return get_instance()->current_log; }
static test_log_singleton* get_instance()
{
static test_log_singleton* inst = new test_log_singleton{};
return inst;
}
void register_log(srslte::log* log_) { current_log = log_; }
private:
test_log_singleton() = default;
srslte::log* current_log = nullptr;
};
// logger that we can instantiate in a specific test scope
// useful if we want to define specific logging policies within a scope (e.g. null logger, count number of errors,
// exit on error, log special diagnostics on destruction). It restores the previous logger after exiting the scope
@ -40,15 +59,15 @@ class scoped_tester_log : public srslte::log_filter
public:
explicit scoped_tester_log(std::string layer) : srslte::log_filter(layer)
{
previous_log_test = current_log;
current_log = this;
previous_log_test = test_log_singleton::get_log();
test_log_singleton::get_instance()->register_log(this);
set_level(srslte::LOG_LEVEL_DEBUG);
}
scoped_tester_log(const scoped_tester_log&) = delete;
scoped_tester_log(scoped_tester_log&&) = delete;
scoped_tester_log& operator=(const scoped_tester_log&) = delete;
scoped_tester_log& operator=(scoped_tester_log&&) = delete;
~scoped_tester_log() override { current_log = previous_log_test; }
~scoped_tester_log() override { test_log_singleton::get_instance()->register_log(previous_log_test); }
void error(const char* message, ...) override __attribute__((format(printf, 2, 3)))
{
@ -91,13 +110,9 @@ public:
bool exit_on_error = false;
uint32_t error_counter = 0, warn_counter = 0;
static srslte::log* get_instance() { return current_log; }
private:
srslte::log* previous_log_test = nullptr;
static srslte::log* current_log;
srslte::log* previous_log_test = nullptr;
};
srslte::log* scoped_tester_log::current_log = nullptr;
// specialization of scoped_tester_log to store last logged message
class nullsink_log : public scoped_tester_log
@ -162,20 +177,20 @@ private:
#define TESTERROR(fmt, ...) \
do { \
if (srslte::scoped_tester_log::get_instance() == nullptr) { \
if (srslte::test_log_singleton::get_instance() == nullptr) { \
printf(fmt, ##__VA_ARGS__); \
} else { \
srslte::scoped_tester_log::get_instance()->error(fmt, ##__VA_ARGS__); \
srslte::test_log_singleton::get_log()->error(fmt, ##__VA_ARGS__); \
} \
return SRSLTE_ERROR; \
} while (0)
#define TESTWARN(fmt, ...) \
do { \
if (srslte::scoped_tester_log::get_instance() == nullptr) { \
if (srslte::test_log_singleton::get_instance() == nullptr) { \
printf(fmt, ##__VA_ARGS__); \
} else { \
srslte::scoped_tester_log::get_instance()->warning(fmt, ##__VA_ARGS__); \
srslte::test_log_singleton::get_log()->warning(fmt, ##__VA_ARGS__); \
} \
} while (0)

View File

@ -28,7 +28,7 @@ int test_nullsink_log()
// without contaminating the console/log file
srslte::nullsink_log null_logger{"TEST"};
TESTASSERT(srslte::scoped_tester_log::get_instance() == &null_logger);
TESTASSERT(srslte::test_log_singleton::get_log() == &null_logger);
TESTASSERT(null_logger.error_counter == 0);
TESTASSERT(null_logger.last_log_level == srslte::LOG_LEVEL_NONE);
@ -48,7 +48,7 @@ int test_log_scoping()
// This behavior is useful for the cases we have one generic logger for all tests, but in a specific test
// we want to use a different one
srslte::nullsink_log logger1("TEST1");
TESTASSERT(srslte::scoped_tester_log::get_instance() == &logger1);
TESTASSERT(srslte::test_log_singleton::get_log() == &logger1);
logger1.error("message1");
logger1.error("message2");
@ -57,7 +57,7 @@ int test_log_scoping()
{
// the global test log should be overwriten here, and used by TESTASSERT macro
srslte::nullsink_log logger2("TEST2");
TESTASSERT(srslte::scoped_tester_log::get_instance() == &logger2);
TESTASSERT(srslte::test_log_singleton::get_log() == &logger2);
TESTASSERT(logger2.error_counter == 0);
logger2.error("error message in logger2\n");
TESTASSERT(logger2.last_log_msg == "error message in logger2\n");
@ -65,7 +65,7 @@ int test_log_scoping()
}
// the last logger should be recovered
TESTASSERT(srslte::scoped_tester_log::get_instance() == &logger1);
TESTASSERT(srslte::test_log_singleton::get_log() == &logger1);
TESTASSERT(logger1.error_counter == 2);
return 0;
}

View File

@ -111,7 +111,7 @@ public:
void init(rrc_interface_mac* rrc, srslte::log* log);
void set_metric(metric_dl* dl_metric, metric_ul* ul_metric);
int cell_cfg(cell_cfg_t* cell_cfg) final;
int cell_cfg(cell_cfg_t* cell_cfg) override;
void set_sched_cfg(sched_args_t* sched_cfg);
int reset() final;

View File

@ -29,7 +29,7 @@ target_link_libraries(scheduler_test srsenb_mac
${Boost_LIBRARIES})
# Scheduler test random
add_executable(scheduler_test_rand scheduler_test_rand.cc)
add_executable(scheduler_test_rand scheduler_test_rand.cc scheduler_test_common.cc)
target_link_libraries(scheduler_test_rand srsenb_mac
srsenb_phy
srslte_common
@ -37,3 +37,4 @@ target_link_libraries(scheduler_test_rand srsenb_mac
rrc_asn1
${CMAKE_THREAD_LIBS_INIT}
${Boost_LIBRARIES})
add_test(scheduler_test_rand scheduler_test_rand)

View File

@ -0,0 +1,192 @@
/*
* Copyright 2013-2019 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/.
*
*/
#include "scheduler_test_common.h"
#include "srsenb/hdr/stack/mac/scheduler.h"
#include "srslte/common/test_common.h"
using namespace srsenb;
int output_sched_tester::test_ul_rb_collisions(const tti_params_t& tti_params,
const sched_interface::ul_sched_res_t& ul_result,
prbmask_t& ul_allocs) const
{
uint32_t nof_prb = params.cfg->cell.nof_prb;
ul_allocs.resize(nof_prb);
ul_allocs.reset();
auto try_ul_fill = [&](srsenb::ul_harq_proc::ul_alloc_t alloc, const char* ch_str, bool strict = true) {
CONDERROR((alloc.RB_start + alloc.L) > nof_prb,
"[TESTER] Allocated RBs (%d,%d) out-of-bounds\n",
alloc.RB_start,
alloc.RB_start + alloc.L);
CONDERROR(alloc.L == 0, "[TESTER] Allocations must have at least one PRB\n");
if (strict and ul_allocs.any(alloc.RB_start, alloc.RB_start + alloc.L)) {
TESTERROR("[TESTER] Collision Detected of %s alloc=(%d,%d) and cumulative_mask=0x%s\n",
ch_str,
alloc.RB_start,
alloc.RB_start + alloc.L,
ul_allocs.to_hex().c_str());
}
ul_allocs.fill(alloc.RB_start, alloc.RB_start + alloc.L, true);
return SRSLTE_SUCCESS;
};
/* TEST: Check if there is space for PRACH */
bool is_prach_tti_tx_ul = srslte_prach_tti_opportunity_config_fdd(params.cfg->prach_config, tti_params.tti_tx_ul, -1);
if (is_prach_tti_tx_ul) {
try_ul_fill({params.cfg->prach_freq_offset, 6}, "PRACH");
}
/* TEST: check collisions in PUCCH */
bool strict = nof_prb != 6 or (not is_prach_tti_tx_ul); // and not tti_data.ul_pending_msg3_present);
try_ul_fill({0, (uint32_t)params.cfg->nrb_pucch}, "PUCCH", strict);
try_ul_fill({params.cfg->cell.nof_prb - params.cfg->nrb_pucch, (uint32_t)params.cfg->nrb_pucch}, "PUCCH", strict);
/* TEST: check collisions in the UL PUSCH */
for (uint32_t i = 0; i < ul_result.nof_dci_elems; ++i) {
uint32_t L, RBstart;
srslte_ra_type2_from_riv(ul_result.pusch[i].dci.type2_alloc.riv, &L, &RBstart, nof_prb, nof_prb);
strict = ul_result.pusch[i].needs_pdcch or nof_prb != 6; // Msg3 may collide with PUCCH at PRB==6
try_ul_fill({RBstart, L}, "PUSCH", strict);
// ue_stats[ul_result.pusch[i].dci.rnti].nof_ul_rbs += L;
}
return SRSLTE_SUCCESS;
}
int output_sched_tester::test_dl_rb_collisions(const tti_params_t& tti_params,
const sched_interface::dl_sched_res_t& dl_result,
rbgmask_t& rbgmask) const
{
srslte::bounded_bitset<100, true> dl_allocs(params.cfg->cell.nof_prb), alloc_mask(params.cfg->cell.nof_prb);
auto try_dl_mask_fill = [&](const srslte_dci_dl_t& dci, const char* channel) {
if (extract_dl_prbmask(params.cfg->cell, dci, &alloc_mask) != SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
}
if ((dl_allocs & alloc_mask).any()) {
TESTERROR("[TESTER] Detected collision in the DL %s allocation (%s intersects %s)\n",
channel,
dl_allocs.to_string().c_str(),
alloc_mask.to_string().c_str());
}
dl_allocs |= alloc_mask;
return SRSLTE_SUCCESS;
};
// Decode BC allocations, check collisions, and fill cumulative mask
for (uint32_t i = 0; i < dl_result.nof_bc_elems; ++i) {
TESTASSERT(try_dl_mask_fill(dl_result.bc[i].dci, "BC") == SRSLTE_SUCCESS);
}
// Decode RAR allocations, check collisions, and fill cumulative mask
for (uint32_t i = 0; i < dl_result.nof_rar_elems; ++i) {
TESTASSERT(try_dl_mask_fill(dl_result.rar[i].dci, "RAR") == SRSLTE_SUCCESS);
}
// forbid Data in DL if it conflicts with PRACH for PRB==6
if (params.cfg->cell.nof_prb == 6) {
uint32_t tti_rx_ack = TTI_RX_ACK(tti_params.tti_rx);
if (srslte_prach_tti_opportunity_config_fdd(params.cfg->prach_config, tti_rx_ack, -1)) {
dl_allocs.fill(0, dl_allocs.size());
}
}
// Decode Data allocations, check collisions and fill cumulative mask
for (uint32_t i = 0; i < dl_result.nof_data_elems; ++i) {
TESTASSERT(try_dl_mask_fill(dl_result.data[i].dci, "data") == SRSLTE_SUCCESS);
}
// TEST: check for holes in the PRB mask (RBGs not fully filled)
rbgmask.resize(params.nof_rbgs);
rbgmask.reset();
srslte::bounded_bitset<100, true> rev_alloc = ~dl_allocs;
for (uint32_t i = 0; i < params.nof_rbgs; ++i) {
uint32_t lim = SRSLTE_MIN((i + 1) * params.P, dl_allocs.size());
bool val = dl_allocs.any(i * params.P, lim);
CONDERROR(rev_alloc.any(i * params.P, lim) and val, "[TESTER] No holes can be left in an RBG\n");
if (val) {
rbgmask.set(i);
}
}
return SRSLTE_SUCCESS;
}
int output_sched_tester::test_sib_scheduling(const tti_params_t& tti_params,
const sched_interface::dl_sched_res_t& dl_result) const
{
uint32_t sfn = tti_params.sfn;
uint32_t sf_idx = tti_params.sf_idx;
bool sib1_present = ((sfn % 2) == 0) and sf_idx == 5;
using bc_elem = const sched_interface::dl_sched_bc_t;
bc_elem* bc_begin = &dl_result.bc[0];
bc_elem* bc_end = &dl_result.bc[dl_result.nof_bc_elems];
/* Test if SIB1 was correctly scheduled */
if (sib1_present) {
auto it = std::find_if(bc_begin, bc_end, [](bc_elem& elem) { return elem.index == 0; });
CONDERROR(it == bc_end, "Failed to allocate SIB1 in even sfn, sf_idx==5\n");
}
/* Test if any SIB was scheduled outside of its window */
for (bc_elem* bc = bc_begin; bc != bc_end; ++bc) {
if (bc->index == 0) {
continue;
}
uint32_t x = (bc->index - 1) * params.cfg->si_window_ms;
uint32_t sf = x % 10;
uint32_t sfn_start = sfn;
while ((sfn_start % params.cfg->sibs[bc->index].period_rf) != x / 10) {
sfn_start--;
}
uint32_t win_start = sfn_start * 10 + sf;
uint32_t win_end = win_start + params.cfg->si_window_ms;
CONDERROR(tti_params.tti_tx_dl < win_start or tti_params.tti_tx_dl > win_end,
"Scheduled SIB is outside of its SIB window\n");
}
return SRSLTE_SUCCESS;
}
int srsenb::extract_dl_prbmask(const srslte_cell_t& cell,
const srslte_dci_dl_t& dci,
srslte::bounded_bitset<100, true>* alloc_mask)
{
srslte_pdsch_grant_t grant;
srslte_dl_sf_cfg_t dl_sf = {};
srslte_dci_dl_t* dci_dyn = const_cast<srslte_dci_dl_t*>(&dci); // TODO
srslte_cell_t* cell_dyn = const_cast<srslte_cell_t*>(&cell);
alloc_mask->resize(cell.nof_prb);
alloc_mask->reset();
CONDERROR(srslte_ra_dl_dci_to_grant(cell_dyn, &dl_sf, SRSLTE_TM1, false, dci_dyn, &grant) == SRSLTE_ERROR,
"Failed to decode PDSCH grant\n");
for (uint32_t j = 0; j < alloc_mask->size(); ++j) {
if (grant.prb_idx[0][j]) {
alloc_mask->set(j);
}
}
return SRSLTE_SUCCESS;
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2013-2019 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_SCHEDULER_TEST_COMMON_H
#define SRSLTE_SCHEDULER_TEST_COMMON_H
#include "srsenb/hdr/stack/mac/scheduler.h"
namespace srsenb {
// helpers
int extract_dl_prbmask(const srslte_cell_t& cell,
const srslte_dci_dl_t& dci,
srslte::bounded_bitset<100, true>* alloc_mask);
class output_sched_tester
{
public:
explicit output_sched_tester(const sched_params_t& params_) : params(params_) {}
/* Check for collisions between RB allocations in the PUSCH and PUCCH */
int test_ul_rb_collisions(const tti_params_t& tti_params,
const sched_interface::ul_sched_res_t& ul_result,
prbmask_t& ul_allocs) const;
/* Check for collision between RB allocations in the PDSCH */
int test_dl_rb_collisions(const tti_params_t& tti_params,
const sched_interface::dl_sched_res_t& dl_result,
rbgmask_t& dl_mask) const;
/* Check if SIBs are scheduled within their window */
int test_sib_scheduling(const tti_params_t& tti_params, const sched_interface::dl_sched_res_t& dl_result) const;
private:
const sched_params_t& params;
};
} // namespace srsenb
#endif // SRSLTE_SCHEDULER_TEST_COMMON_H

View File

@ -37,6 +37,7 @@
#include "srslte/phy/utils/debug.h"
#include "srslte/radio/radio.h"
#include "scheduler_test_common.h"
#include "srslte/common/test_common.h"
/********************************************************
@ -69,10 +70,11 @@
/***************************
* Setup Random generators
**************************/
// uint32_t const seed = std::random_device()();
uint32_t const seed = 2452071795; // time(nullptr);
uint32_t const seed = time(nullptr);
// uint32_t const seed = 2452071795;
std::default_random_engine rand_gen(seed);
std::uniform_real_distribution<float> unif_dist(0, 1.0);
bool check_old_pids = false;
float randf()
{
@ -119,7 +121,6 @@ public:
}
};
log_tester log_global;
bool check_old_pids = true;
/*******************
* Dummies *
@ -165,8 +166,6 @@ struct sched_tester : public srsenb::sched {
srsenb::ul_harq_proc ul_harq;
};
struct sched_tti_data {
bool is_prach_tti_tx_ul = false;
uint32_t ul_sf_idx;
uint32_t tti_rx;
uint32_t tti_tx_dl;
uint32_t tti_tx_ul;
@ -204,7 +203,6 @@ struct sched_tester : public srsenb::sched {
srsenb::ul_harq_proc ul_harq;
};
uint32_t nof_rbgs = 0;
sched_sim_args sim_args;
// tester control data
@ -214,8 +212,10 @@ struct sched_tester : public srsenb::sched {
typedef std::multimap<uint32_t, ack_info_t>::iterator ack_it_t;
// sched results
sched_tti_data tti_data;
sched_tti_data tti_data;
srsenb::tti_params_t tti_params{10241};
int cell_cfg(sched_interface::cell_cfg_t* cell_cfg) final;
int add_user(uint16_t rnti,
srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg,
srsenb::sched_interface::ue_cfg_t ue_cfg_);
@ -234,8 +234,17 @@ private:
void before_sched();
int process_results();
int ack_txs();
std::unique_ptr<srsenb::output_sched_tester> output_tester;
};
int sched_tester::cell_cfg(sched_interface::cell_cfg_t* cell_cfg)
{
sched::cell_cfg(cell_cfg);
output_tester.reset(new srsenb::output_sched_tester{sched_params});
return SRSLTE_SUCCESS;
}
int sched_tester::add_user(uint16_t rnti,
srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg,
srsenb::sched_interface::ue_cfg_t ue_cfg_)
@ -273,15 +282,10 @@ void sched_tester::rem_user(uint16_t rnti)
void sched_tester::new_test_tti(uint32_t tti_)
{
// NOTE: make a local copy, since some of these variables may be cleared during scheduling
tti_data.tti_rx = tti_;
tti_data.tti_tx_dl = TTI_TX(tti_);
tti_data.tti_tx_ul = TTI_RX_ACK(tti_);
tti_data.is_prach_tti_tx_ul = srslte_prach_tti_opportunity_config_fdd(cfg.prach_config, tti_data.tti_tx_ul, -1);
if (tti_data.tti_tx_ul > FDD_HARQ_DELAY_MS) {
tti_data.ul_sf_idx = (tti_data.tti_tx_ul - FDD_HARQ_DELAY_MS) % 10;
} else {
tti_data.ul_sf_idx = (tti_data.tti_tx_ul + 10240 - FDD_HARQ_DELAY_MS) % 10;
}
tti_params = srsenb::tti_params_t{tti_};
tti_data.tti_rx = tti_;
tti_data.tti_tx_dl = TTI_TX(tti_);
tti_data.tti_tx_ul = TTI_RX_ACK(tti_);
auto& pending_msg3s = carrier_schedulers[0]->get_sf_sched_ptr(tti_data.tti_rx)->get_pending_msg3();
tti_data.ul_pending_msg3_present = false;
if (not pending_msg3s.empty()) {
@ -779,82 +783,22 @@ int sched_tester::test_harqs()
int sched_tester::test_sibs()
{
uint32_t sfn = tti_data.tti_tx_dl / 10;
uint32_t sf_idx = TTI_TX(tti_data.tti_rx) % 10;
bool sib1_present = ((sfn % 2) == 0) and sf_idx == 5;
using bc_elem = sched_interface::dl_sched_bc_t;
bc_elem* bc_begin = &tti_data.sched_result_dl.bc[0];
bc_elem* bc_end = &tti_data.sched_result_dl.bc[tti_data.sched_result_dl.nof_bc_elems];
/* Test if SIB1 was correctly scheduled */
if (sib1_present) {
auto it = std::find_if(bc_begin, bc_end, [](bc_elem& elem) { return elem.index == 0; });
CONDERROR(it == bc_end, "Failed to allocate SIB1 in even sfn, sf_idx==5\n");
}
/* Test if any SIB was scheduled outside of its window */
for (bc_elem* bc = bc_begin; bc != bc_end; ++bc) {
if (bc->index == 0) {
continue;
}
uint32_t x = (bc->index - 1) * cfg.si_window_ms;
uint32_t sf = x % 10;
uint32_t sfn_start = sfn;
while ((sfn_start % cfg.sibs[bc->index].period_rf) != x / 10) {
sfn_start--;
}
uint32_t win_start = sfn_start * 10 + sf;
uint32_t win_end = win_start + cfg.si_window_ms;
CONDERROR(tti_data.tti_tx_dl < win_start or tti_data.tti_tx_dl > win_end,
"Scheduled SIB is outside of its SIB window\n");
}
return SRSLTE_SUCCESS;
return output_tester->test_sib_scheduling(tti_params, tti_data.sched_result_dl);
}
int sched_tester::test_collisions()
{
const srsenb::sf_sched* tti_sched = carrier_schedulers[0]->get_sf_sched_ptr(tti_data.tti_rx);
srsenb::prbmask_t ul_allocs(cfg.cell.nof_prb);
srsenb::prbmask_t ul_allocs(cfg.cell.nof_prb);
/* TEST: any collision in PUCCH and PUSCH */
TESTASSERT(output_tester->test_ul_rb_collisions(tti_params, tti_data.sched_result_ul, ul_allocs) == SRSLTE_SUCCESS);
// Helper function to fill RBG mask
auto try_ul_fill = [&](srsenb::ul_harq_proc::ul_alloc_t alloc, const char* ch_str, bool strict = true) {
CONDERROR((alloc.RB_start + alloc.L) > cfg.cell.nof_prb,
"[TESTER] Allocated RBs (%d,%d) out of bounds\n",
alloc.RB_start,
alloc.RB_start + alloc.L);
CONDERROR(alloc.L == 0, "[TESTER] Allocations must have at least one PRB\n");
if (strict and ul_allocs.any(alloc.RB_start, alloc.RB_start + alloc.L)) {
TESTERROR("[TESTER] There is a collision of %s alloc=(%d,%d) and cumulative_mask=%s\n",
ch_str,
alloc.RB_start,
alloc.RB_start + alloc.L,
ul_allocs.to_hex().c_str());
}
ul_allocs.fill(alloc.RB_start, alloc.RB_start + alloc.L, true);
return SRSLTE_SUCCESS;
};
/* TEST: Check if there is space for PRACH */
if (tti_data.is_prach_tti_tx_ul) {
try_ul_fill({cfg.prach_freq_offset, 6}, "PRACH");
/* TEST: check whether cumulative UL PRB masks coincide */
if (ul_allocs != tti_sched->get_ul_mask()) {
TESTERROR("[TESTER] The UL PRB mask and the scheduler result UL mask are not consistent\n");
}
/* TEST: check collisions in the UL PUSCH and PUCCH */
for (uint32_t i = 0; i < tti_data.sched_result_ul.nof_dci_elems; ++i) {
uint32_t L, RBstart;
srslte_ra_type2_from_riv(
tti_data.sched_result_ul.pusch[i].dci.type2_alloc.riv, &L, &RBstart, cfg.cell.nof_prb, cfg.cell.nof_prb);
try_ul_fill({RBstart, L}, "PUSCH");
ue_stats[tti_data.sched_result_ul.pusch[i].dci.rnti].nof_ul_rbs += L;
}
/* TEST: check collisions with PUCCH */
bool strict = cfg.cell.nof_prb != 6 or (not tti_data.is_prach_tti_tx_ul and not tti_data.ul_pending_msg3_present);
try_ul_fill({0, (uint32_t)cfg.nrb_pucch}, "PUCCH", strict);
try_ul_fill({cfg.cell.nof_prb - cfg.nrb_pucch, (uint32_t)cfg.nrb_pucch}, "PUCCH", strict);
/* TEST: Check if there is a collision with Msg3 or Msg3 alloc data is not consistent */
if (tti_data.ul_pending_msg3_present) {
bool passed = false;
@ -874,99 +818,27 @@ int sched_tester::test_collisions()
CONDERROR(not passed, "[TESTER] No Msg3 allocation was found in the sched_result\n");
}
/* TEST: check whether cumulative UL PRB masks coincide */
if (ul_allocs != tti_sched->get_ul_mask()) {
TESTERROR("[TESTER] The UL PRB mask and the scheduler result UL mask are not consistent\n");
// update ue stats with number of allocated UL PRBs
for (uint32_t i = 0; i < tti_data.sched_result_ul.nof_dci_elems; ++i) {
uint32_t L, RBstart;
srslte_ra_type2_from_riv(
tti_data.sched_result_ul.pusch[i].dci.type2_alloc.riv, &L, &RBstart, cfg.cell.nof_prb, cfg.cell.nof_prb);
ue_stats[tti_data.sched_result_ul.pusch[i].dci.rnti].nof_ul_rbs += L;
}
srslte::bounded_bitset<100, true> dl_allocs(cfg.cell.nof_prb), alloc_mask(cfg.cell.nof_prb);
srslte_dl_sf_cfg_t dl_sf;
ZERO_OBJECT(dl_sf);
for (uint32_t i = 0; i < tti_data.sched_result_dl.nof_bc_elems; ++i) {
srslte_pdsch_grant_t grant;
CONDERROR(srslte_ra_dl_dci_to_grant(
&cfg.cell, &dl_sf, SRSLTE_TM1, false, &tti_data.sched_result_dl.bc[i].dci, &grant) == SRSLTE_ERROR,
"Failed to decode PDSCH grant\n");
alloc_mask.reset();
for (uint32_t j = 0; j < alloc_mask.size(); ++j) {
if (grant.prb_idx[0][j]) {
alloc_mask.set(j);
}
}
if ((dl_allocs & alloc_mask).any()) {
TESTERROR("[TESTER] Detected collision in the DL bc allocation (%s intersects %s)\n",
dl_allocs.to_string().c_str(),
alloc_mask.to_string().c_str());
}
dl_allocs |= alloc_mask;
}
for (uint32_t i = 0; i < tti_data.sched_result_dl.nof_rar_elems; ++i) {
alloc_mask.reset();
srslte_pdsch_grant_t grant;
CONDERROR(srslte_ra_dl_dci_to_grant(
&cfg.cell, &dl_sf, SRSLTE_TM1, false, &tti_data.sched_result_dl.rar[i].dci, &grant) == SRSLTE_ERROR,
"Failed to decode PDSCH grant\n");
for (uint32_t j = 0; j < alloc_mask.size(); ++j) {
if (grant.prb_idx[0][j]) {
alloc_mask.set(j);
} else {
alloc_mask.reset(j);
}
}
if ((dl_allocs & alloc_mask).any()) {
TESTERROR("[TESTER] Detected collision in the DL RAR allocation (%s intersects %s)\n",
dl_allocs.to_string().c_str(),
alloc_mask.to_string().c_str());
}
dl_allocs |= alloc_mask;
}
// forbid Data in DL if it conflicts with PRACH for PRB==6
if (cfg.cell.nof_prb == 6) {
uint32_t tti_rx_ack = TTI_RX_ACK(tti_data.tti_rx);
if (srslte_prach_tti_opportunity_config_fdd(cfg.prach_config, tti_rx_ack, -1)) {
dl_allocs.fill(0, dl_allocs.size());
}
}
/* TEST: check any collision in PDSCH */
srsenb::rbgmask_t rbgmask(cfg.cell.nof_prb);
TESTASSERT(output_tester->test_dl_rb_collisions(tti_params, tti_data.sched_result_dl, rbgmask) == SRSLTE_SUCCESS);
// update ue stats with number of DL RB allocations
srslte::bounded_bitset<100, true> alloc_mask(cfg.cell.nof_prb);
for (uint32_t i = 0; i < tti_data.sched_result_dl.nof_data_elems; ++i) {
alloc_mask.reset();
srslte_pdsch_grant_t grant;
CONDERROR(srslte_ra_dl_dci_to_grant(
&cfg.cell, &dl_sf, SRSLTE_TM1, false, &tti_data.sched_result_dl.data[i].dci, &grant) == SRSLTE_ERROR,
"Failed to decode PDSCH grant\n");
for (uint32_t j = 0; j < alloc_mask.size(); ++j) {
if (grant.prb_idx[0][j]) {
alloc_mask.set(j);
} else {
alloc_mask.reset(j);
}
}
if ((dl_allocs & alloc_mask).any()) {
TESTERROR("[TESTER] Detected collision in the DL data allocation (%s intersects %s)\n",
dl_allocs.to_string().c_str(),
alloc_mask.to_string().c_str());
}
dl_allocs |= alloc_mask;
TESTASSERT(srsenb::extract_dl_prbmask(cfg.cell, tti_data.sched_result_dl.data[i].dci, &alloc_mask) ==
SRSLTE_SUCCESS);
ue_stats[tti_data.sched_result_dl.data[i].dci.rnti].nof_dl_rbs += alloc_mask.count();
}
// TEST: check if resulting DL mask is equal to scheduler internal DL mask
uint32_t P = srslte_ra_type0_P(cfg.cell.nof_prb);
nof_rbgs = srslte::ceil_div(cfg.cell.nof_prb, P);
srsenb::rbgmask_t rbgmask(nof_rbgs);
srslte::bounded_bitset<100, true> rev_alloc = ~dl_allocs;
for (uint32_t i = 0; i < nof_rbgs; ++i) {
uint32_t lim = SRSLTE_MIN((i + 1) * P, dl_allocs.size());
bool val = dl_allocs.any(i * P, lim);
CONDERROR(rev_alloc.any(i * P, lim) and val, "[TESTER] No holes can be left in an RBG\n");
if (val) {
rbgmask.set(i);
} else {
rbgmask.reset(i);
}
}
if (rbgmask != carrier_schedulers[0]->get_sf_sched_ptr(tti_data.tti_rx)->get_dl_mask()) {
TESTERROR("[TESTER] The UL PRB mask and the scheduler result UL mask are not consistent (%s!=%s)\n",
rbgmask.to_string().c_str(),
@ -1131,7 +1003,7 @@ sched_sim_args rand_sim_params(const srsenb::sched_interface::cell_cfg_t& cell_c
float P_prach = 0.99f; // 0.1f + randf()*0.3f;
float ul_sr_exps[] = {1, 4}; // log rand
float dl_data_exps[] = {1, 4}; // log rand
uint32_t max_nof_users = 500;
uint32_t max_nof_users = 5;
std::uniform_int_distribution<> connection_dur_dist(min_conn_dur, max_conn_dur);
bzero(&sim_args.ue_cfg, sizeof(srsenb::sched_interface::ue_cfg_t));
@ -1200,8 +1072,5 @@ int main()
test_scheduler_rand(cell_cfg, sim_args);
}
// // low UL-Txs
// printf("\n\n********* New Test ***********\n");
// sim_args.P_sr = 0.05;
// test_scheduler_rand(sim_args);
return 0;
}