Write TPC sched unit test and fix sched logical channel test

master
Francisco 4 years ago committed by Andre Puschmann
parent 8670558692
commit d42dc50c44

@ -13,6 +13,7 @@
#ifndef SRSLTE_ACCUMULATORS_H
#define SRSLTE_ACCUMULATORS_H
#include <cassert>
#include <cstdint>
#include <limits>
#include <vector>
@ -62,7 +63,7 @@ namespace detail {
template <typename T>
struct sliding_window {
sliding_window(uint32_t N, T val) : window(N, val) {}
sliding_window(uint32_t N, T val = 0) : window(N, val) {}
void push(T sample)
{
window[next_idx++] = sample;
@ -71,6 +72,7 @@ struct sliding_window {
}
}
size_t size() const { return window.size(); }
const T& oldest() const { return window[(next_idx + size() - 1) % size()]; }
T& operator[](size_t i) { return window[i]; }
const T& operator[](size_t i) const { return window[i]; }
std::vector<T> window;
@ -80,22 +82,21 @@ struct sliding_window {
} // namespace detail
template <typename T>
struct sliding_sum {
sliding_sum(uint32_t N) : window(N, 0) {}
struct sliding_sum : private detail::sliding_window<T> {
using base_t = detail::sliding_window<T>;
using base_t::oldest;
using base_t::push;
using base_t::size;
using base_t::sliding_window;
void push(T sample) { window.push(sample); }
T value() const
{
T ret = 0;
for (size_t i = 0; i < window.size(); ++i) {
ret += window[i];
for (size_t i = 0; i < size(); ++i) {
ret += (*this)[i];
}
return ret;
}
size_t size() const { return window.size(); }
private:
detail::sliding_window<T> window;
};
template <typename T>

@ -26,12 +26,18 @@ namespace srsenb {
*/
class tpc
{
static constexpr size_t SNR_WINDOW_SIZE_MS = 8;
static constexpr int PHR_NEG_NOF_PRB = 1;
static constexpr size_t SNR_WINDOW_SIZE_MS = 20;
static constexpr int undefined_phr = std::numeric_limits<int>::max();
public:
static constexpr int PHR_NEG_NOF_PRB = 1;
tpc(uint32_t cell_nof_prb, float target_snr_dB_ = -1.0) :
nof_prb(cell_nof_prb), target_snr_dB(target_snr_dB_), snr_avg(SNR_WINDOW_SIZE_MS)
nof_prb(cell_nof_prb),
target_snr_dB(target_snr_dB_),
snr_avg(SNR_WINDOW_SIZE_MS),
win_pusch_tpc_values(FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS),
win_pucch_tpc_values(FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS)
{
max_prbs_cached = nof_prb;
}
@ -41,12 +47,12 @@ public:
void set_phr(int phr_)
{
last_phr = phr_;
sum_pusch_tpc_values = 0;
sum_pucch_tpc_values = 0;
pucch_phr_flag = false;
pusch_phr_flag = false;
// compute and cache the max nof UL PRBs that avoids overflowing PHR
max_prbs_cached = PHR_NEG_NOF_PRB;
for (int n = nof_prb; n >= PHR_NEG_NOF_PRB - 1; --n) {
for (int n = nof_prb; n > PHR_NEG_NOF_PRB; --n) {
if (last_phr >= 10 * log10(n)) {
max_prbs_cached = n;
break;
@ -57,17 +63,27 @@ public:
void new_tti()
{
if (target_snr_dB < 0) {
return;
}
// Enqueue PUSCH/PUCCH TPC sent in last TTI (zero for both Delta_PUSCH/Delta_PUCCH=0 and TPC not sent)
sum_pusch_tpc_values += pending_pusch_tpc;
pending_pusch_tpc = 0;
sum_pucch_tpc_values += pending_pucch_tpc;
pending_pucch_tpc = 0;
return;
}
// Enqueue pending SNR measurement
if (pending_snr == snr_avg.null_value) {
acc_pusch_tpc_values += win_pusch_tpc_values.oldest();
acc_pucch_tpc_values += win_pusch_tpc_values.oldest();
} else {
acc_pucch_tpc_values = 0;
acc_pusch_tpc_values = 0;
}
snr_avg.push(pending_snr);
pending_snr = snr_avg.null_value;
// Enqueue PUSCH/PUCCH TPC sent in last TTI (zero for both Delta_PUSCH/Delta_PUCCH=0 and TPC not sent)
win_pusch_tpc_values.push(pending_pusch_tpc);
pending_pusch_tpc = 0;
win_pucch_tpc_values.push(pending_pucch_tpc);
pending_pucch_tpc = 0;
}
/**
@ -77,15 +93,18 @@ public:
*/
int8_t encode_pusch_tpc()
{
pending_pusch_tpc = 0;
if (target_snr_dB <= 0) {
// undefined target SINR case. Increase Tx power, while the number of allocable PRBs remains unchanged
assert(pending_pusch_tpc == 0); // ensure called once per {cc,tti}
if (target_snr_dB < 0) {
// undefined target SINR case. Increase Tx power once per PHR, considering the number of allocable PRBs remains
// unchanged
if (not pusch_phr_flag) {
pending_pusch_tpc = (max_prbs_cached == nof_prb) ? 1 : (last_phr < 0 ? -1 : 0);
pusch_phr_flag = true;
}
} else if (snr_avg.value() != snr_avg.null_value) {
// target SINR is finite and there is power headroom
float diff = target_snr_dB - snr_avg.value();
diff -= sum_pusch_tpc_values;
pending_pusch_tpc = 0;
diff -= win_pusch_tpc_values.value() + acc_pusch_tpc_values;
if (diff > 1) {
pending_pusch_tpc = diff > 3 ? 3 : 1;
} else if (diff <= -1) {
@ -107,14 +126,18 @@ public:
*/
int8_t encode_pucch_tpc()
{
pending_pusch_tpc = 0;
if (target_snr_dB <= 0) {
// undefined target SINR case. Increase Tx power, while the number of allocable PRBs remains unchanged
assert(pending_pucch_tpc == 0); // ensure called once per {cc,tti}
if (target_snr_dB < 0) {
// undefined target SINR case. Increase Tx power once per PHR, considering the number of allocable PRBs remains
// unchanged
if (not pucch_phr_flag) {
pending_pucch_tpc = (max_prbs_cached == nof_prb) ? 1 : (last_phr < 0 ? -1 : 0);
pucch_phr_flag = true;
}
} else if (snr_avg.value() != snr_avg.null_value) {
// target SINR is finite and there is power headroom
float diff = target_snr_dB - snr_avg.value();
diff -= sum_pucch_tpc_values;
diff -= win_pucch_tpc_values.value() + acc_pucch_tpc_values;
pending_pucch_tpc = 0;
if (diff > 1) {
pending_pucch_tpc = diff > 3 ? 3 : 1;
@ -136,12 +159,13 @@ private:
float target_snr_dB;
srslte::null_sliding_average<float> snr_avg;
int sum_pusch_tpc_values = 0;
int sum_pucch_tpc_values = 0;
srslte::sliding_sum<int> win_pusch_tpc_values, win_pucch_tpc_values;
uint32_t max_prbs_cached = 100;
int last_phr = std::numeric_limits<int>::max();
int pending_pusch_tpc = 0, pending_pucch_tpc = 0;
float pending_snr = srslte::null_sliding_average<float>::null_value;
int acc_pusch_tpc_values = 0, acc_pucch_tpc_values = 0;
int last_phr = undefined_phr;
bool pusch_phr_flag = false, pucch_phr_flag = false;
};
} // namespace srsenb

@ -6,8 +6,9 @@
# the distribution.
#
add_library(scheduler_test_common STATIC sched_test_common.cc sched_common_test_suite.cc sched_ue_ded_test_suite.cc
add_library(sched_test_common STATIC sched_test_common.cc sched_common_test_suite.cc sched_ue_ded_test_suite.cc
sched_sim_ue.cc sched_sim_ue.cc)
target_link_libraries(sched_test_common srslte_common srslte_mac srsenb_mac)
# Scheduler subcomponent testing
add_executable(sched_grid_test sched_grid_test.cc)
@ -15,36 +16,41 @@ target_link_libraries(sched_grid_test srsenb_mac
srsenb_phy
srslte_common
srslte_mac
scheduler_test_common
sched_test_common
${CMAKE_THREAD_LIBS_INIT}
${Boost_LIBRARIES})
add_test(sched_grid_test sched_grid_test)
# Scheduler test random
add_executable(scheduler_test_rand sched_test_rand.cc)
target_link_libraries(scheduler_test_rand srsenb_mac
add_executable(sched_test_rand sched_test_rand.cc)
target_link_libraries(sched_test_rand srsenb_mac
srsenb_phy
srslte_common
srslte_mac
scheduler_test_common
sched_test_common
srslte_phy
rrc_asn1
${CMAKE_THREAD_LIBS_INIT}
${Boost_LIBRARIES})
add_test(scheduler_test_rand scheduler_test_rand)
add_test(sched_test_rand sched_test_rand)
# Scheduler test random for CA
add_executable(scheduler_ca_test sched_ca_test.cc sched_test_common.cc)
target_link_libraries(scheduler_ca_test srsenb_mac
add_executable(sched_ca_test sched_ca_test.cc)
target_link_libraries(sched_ca_test srsenb_mac
srsenb_phy
srslte_common
srslte_mac
srslte_phy
scheduler_test_common
sched_test_common
rrc_asn1
${CMAKE_THREAD_LIBS_INIT}
${Boost_LIBRARIES})
add_test(scheduler_ca_test scheduler_ca_test)
add_test(sched_ca_test sched_ca_test)
add_executable(sched_lc_ch_test sched_lc_ch_test.cc sched_test_common.cc)
target_link_libraries(sched_lc_ch_test srsenb_mac srslte_common srslte_mac scheduler_test_common)
add_executable(sched_lc_ch_test sched_lc_ch_test.cc)
target_link_libraries(sched_lc_ch_test srslte_common srsenb_mac srslte_mac sched_test_common)
add_test(sched_lc_ch_test sched_lc_ch_test)
add_executable(sched_tpc_test sched_tpc_test.cc)
target_link_libraries(sched_tpc_test srslte_common srslte_mac sched_test_common)
add_test(sched_tpc_test sched_tpc_test)

@ -17,42 +17,59 @@
using namespace srsenb;
const uint32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
uint32_t rlc_overhead(uint32_t lcid)
{
return lcid == 0 ? 0 : 3;
}
uint32_t add_rlc_overhead(uint32_t lcid, uint32_t rlc_payload_size)
{
return rlc_payload_size + (rlc_payload_size == 0 ? 0 : rlc_overhead(lcid));
}
/// Tests if a PDU was allocated with lcid and pdu_size bytes
int test_pdu_alloc_successful(srsenb::lch_ue_manager& lch_handler,
sched_interface::dl_sched_pdu_t& pdu,
int lcid,
uint32_t pdu_size)
uint32_t rlc_payload_size)
{
uint32_t mac_sdu_size = add_rlc_overhead(lcid, rlc_payload_size);
TESTASSERT(lch_handler.get_max_prio_lcid() == lcid);
TESTASSERT(lch_handler.alloc_rlc_pdu(&pdu, pdu_size) == (int)pdu_size);
TESTASSERT(lch_handler.alloc_rlc_pdu(&pdu, mac_sdu_size) == (int)mac_sdu_size);
TESTASSERT(pdu.lcid == (uint32_t)lcid);
TESTASSERT(pdu.nbytes == pdu_size);
TESTASSERT(pdu.nbytes == mac_sdu_size);
return SRSLTE_SUCCESS;
}
int test_retx_until_empty(srsenb::lch_ue_manager& lch_handler, int lcid, uint32_t pdu_size)
int test_retx_until_empty(srsenb::lch_ue_manager& lch_handler, int lcid, uint32_t rlc_payload_size)
{
uint32_t nof_pdus = lch_handler.get_dl_retx(lcid) / pdu_size;
sched_interface::dl_sched_pdu_t pdu;
int start_rlc_bytes = lch_handler.get_dl_retx(lcid);
int nof_pdus = ceil(start_rlc_bytes / (float)rlc_payload_size);
int rem_rlc_bytes = start_rlc_bytes;
for (uint32_t i = 0; i < nof_pdus; ++i) {
TESTASSERT(test_pdu_alloc_successful(lch_handler, pdu, lcid, pdu_size) == SRSLTE_SUCCESS);
TESTASSERT(lch_handler.get_dl_retx(lcid) == (int)((nof_pdus - i - 1) * pdu_size));
sched_interface::dl_sched_pdu_t pdu;
for (int i = 0; i < nof_pdus; ++i) {
uint32_t expected_payload_size = std::min(rlc_payload_size, (uint32_t)rem_rlc_bytes);
TESTASSERT(test_pdu_alloc_successful(lch_handler, pdu, lcid, expected_payload_size) == SRSLTE_SUCCESS);
rem_rlc_bytes -= expected_payload_size;
TESTASSERT(lch_handler.get_dl_retx(lcid) == rem_rlc_bytes);
}
return nof_pdus * pdu_size;
return start_rlc_bytes;
}
int test_newtx_until_empty(srsenb::lch_ue_manager& lch_handler, int lcid, uint32_t pdu_size)
int test_newtx_until_empty(srsenb::lch_ue_manager& lch_handler, int lcid, uint32_t rlc_payload_size)
{
uint32_t nof_pdus = lch_handler.get_dl_tx(lcid) / pdu_size;
sched_interface::dl_sched_pdu_t pdu;
int start_rlc_bytes = lch_handler.get_dl_tx(lcid);
int nof_pdus = ceil(start_rlc_bytes / (float)rlc_payload_size);
int rem_rlc_bytes = start_rlc_bytes;
for (uint32_t i = 0; i < nof_pdus; ++i) {
TESTASSERT(test_pdu_alloc_successful(lch_handler, pdu, lcid, pdu_size) == SRSLTE_SUCCESS);
TESTASSERT(lch_handler.get_dl_tx(lcid) == (int)((nof_pdus - i - 1) * pdu_size));
sched_interface::dl_sched_pdu_t pdu;
for (int i = 0; i < nof_pdus; ++i) {
uint32_t expected_payload_size = std::min(rlc_payload_size, (uint32_t)rem_rlc_bytes);
TESTASSERT(test_pdu_alloc_successful(lch_handler, pdu, lcid, expected_payload_size) == SRSLTE_SUCCESS);
rem_rlc_bytes -= expected_payload_size;
TESTASSERT(lch_handler.get_dl_tx(lcid) == (int)rem_rlc_bytes);
}
return nof_pdus * pdu_size;
return start_rlc_bytes;
}
int test_lc_ch_pbr_infinity()

@ -0,0 +1,142 @@
/**
*
* \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/stack/mac/sched_ue_ctrl/tpc.h"
#include "srslte/common/test_common.h"
namespace srsenb {
int test_finite_target_snr()
{
const uint32_t nof_prbs = 50;
const int target_snr = 15;
tpc tpcfsm(nof_prbs, 15);
// TEST: While no SNR info is provided, no TPC commands are sent
for (uint32_t i = 0; i < 100; ++i) {
tpcfsm.new_tti();
TESTASSERT(tpcfsm.encode_pucch_tpc() == 0);
TESTASSERT(tpcfsm.encode_pusch_tpc() == 0);
}
// TEST: current SNR above target SNR. Checks:
// - TPC commands should be sent to decrease power
// - The sum power of TPC commands should not exceed the difference between current and target SNRs
int snr_diff = 10;
tpcfsm.set_snr(target_snr + snr_diff);
int sum_pusch = 0, sum_pucch = 0;
for (uint32_t i = 0; i < 100; ++i) {
tpcfsm.new_tti();
sum_pusch += tpcfsm.encode_pusch_tpc();
TESTASSERT(sum_pusch < 0 and sum_pusch >= -snr_diff);
sum_pucch += tpcfsm.encode_pucch_tpc();
TESTASSERT(sum_pucch < 0 and sum_pucch >= -snr_diff);
}
// TEST: current SNR below target SNR. Checks:
// - TPC commands should be sent to increase power
// - The sum of TPC commands should not exceed the difference between current and target SNRs
snr_diff = -10;
tpcfsm.set_snr(target_snr + snr_diff);
sum_pusch = 0;
sum_pucch = 0;
for (uint32_t i = 0; i < 100; ++i) {
tpcfsm.new_tti();
sum_pusch += tpcfsm.encode_pusch_tpc();
TESTASSERT(sum_pusch > 0 and sum_pusch <= -snr_diff);
sum_pucch += tpcfsm.encode_pucch_tpc();
TESTASSERT(sum_pucch > 0 and sum_pucch <= -snr_diff);
}
return SRSLTE_SUCCESS;
}
int test_undefined_target_snr()
{
const uint32_t nof_prbs = 50;
tpc tpcfsm(nof_prbs);
TESTASSERT(tpcfsm.max_ul_prbs() == 50);
// TEST: While the PHR is not updated, a limited number of TPC commands should be sent
int sum_pusch = 0, sum_pucch = 0;
for (uint32_t i = 0; i < 100; ++i) {
tpcfsm.new_tti();
sum_pusch += tpcfsm.encode_pusch_tpc();
sum_pucch += tpcfsm.encode_pucch_tpc();
}
TESTASSERT(sum_pusch <= 3 and sum_pusch >= -1);
TESTASSERT(sum_pucch <= 3 and sum_pucch >= -1);
// TEST: SNR info should not affect TPC in undefined target SNR mode
int snr_info = 10;
tpcfsm.set_snr(snr_info);
sum_pusch = 0;
sum_pucch = 0;
for (uint32_t i = 0; i < 100; ++i) {
tpcfsm.new_tti();
sum_pusch += tpcfsm.encode_pusch_tpc();
sum_pucch += tpcfsm.encode_pucch_tpc();
}
TESTASSERT(sum_pusch == 0);
TESTASSERT(sum_pucch == 0);
// TEST: If the PHR allows full utilization of available PRBs, the TPC slightly increments UL Tx power
int phr = 30;
tpcfsm.set_phr(phr);
TESTASSERT(tpcfsm.max_ul_prbs() == 50);
sum_pusch = 0;
sum_pucch = 0;
for (uint32_t i = 0; i < 100; ++i) {
tpcfsm.new_tti();
sum_pusch += tpcfsm.encode_pusch_tpc();
sum_pucch += tpcfsm.encode_pucch_tpc();
}
TESTASSERT(sum_pusch > 0 and sum_pusch <= 3);
TESTASSERT(sum_pucch > 0 and sum_pucch <= 3);
// TEST: PHR is too low to allow all PRBs to be allocated. This event should not affect TPC commands
phr = 5;
tpcfsm.set_phr(phr);
TESTASSERT(tpcfsm.max_ul_prbs() < 50);
for (uint32_t i = 0; i < 100; ++i) {
tpcfsm.new_tti();
TESTASSERT(tpcfsm.encode_pusch_tpc() == 0);
TESTASSERT(tpcfsm.encode_pucch_tpc() == 0);
}
// TEST: PHR is negative. The TPC should slightly decrease Tx UL power until next PHR
phr = -1;
tpcfsm.set_phr(phr);
TESTASSERT(tpcfsm.max_ul_prbs() == tpc::PHR_NEG_NOF_PRB);
sum_pusch = 0;
sum_pucch = 0;
for (uint32_t i = 0; i < 100; ++i) {
tpcfsm.new_tti();
sum_pusch += tpcfsm.encode_pusch_tpc();
sum_pucch += tpcfsm.encode_pucch_tpc();
}
TESTASSERT(sum_pusch <= 0 and sum_pusch >= -1);
TESTASSERT(sum_pucch <= 0 and sum_pucch >= -1);
return SRSLTE_SUCCESS;
}
} // namespace srsenb
int main()
{
TESTASSERT(srsenb::test_finite_target_snr() == 0);
TESTASSERT(srsenb::test_undefined_target_snr() == 0);
printf("Success\n");
}
Loading…
Cancel
Save