/** * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * * srsRAN 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. * * srsRAN 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 "srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h" #include "srsran/common/test_common.h" namespace srsenb { int8_t decode_tpc(uint8_t encoded_tpc) { const static int8_t tpc_table[] = {-1, 0, 1, 3}; return encoded_tpc < sizeof(tpc_table) ? tpc_table[encoded_tpc] : std::numeric_limits::max(); } int test_finite_target_snr() { const uint32_t nof_prbs = 50; const int target_snr = 15; tpc tpcfsm(0x46, nof_prbs, 15, 15, true); // TEST: While UL SNR ~ target, no TPC commands are sent for (uint32_t i = 0; i < 100 and tpcfsm.get_ul_snr_estim(0) < 14; ++i) { tpcfsm.set_snr(15, 0); tpcfsm.set_snr(15, 1); tpcfsm.new_tti(); } for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); TESTASSERT(decode_tpc(tpcfsm.encode_pucch_tpc()) == 0); TESTASSERT(decode_tpc(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, tpc::PUSCH_CODE); tpcfsm.set_snr(target_snr + snr_diff, tpc::PUCCH_CODE); int sum_pusch = 0, sum_pucch = 0; for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); sum_pusch += decode_tpc(tpcfsm.encode_pusch_tpc()); TESTASSERT(sum_pusch < 0 and sum_pusch >= -snr_diff); sum_pucch += decode_tpc(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, tpc::PUSCH_CODE); tpcfsm.set_snr(target_snr + snr_diff, tpc::PUCCH_CODE); sum_pusch = 0; sum_pucch = 0; for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); sum_pusch += decode_tpc(tpcfsm.encode_pusch_tpc()); TESTASSERT(sum_pusch > 0 and sum_pusch <= -snr_diff); sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc()); TESTASSERT(sum_pucch > 0 and sum_pucch <= -snr_diff); } // TEST: PHR is negative. Checks: // - TPCs sent should be negative or zero // - The accumulation of TPCs should lead to next PHR being zero. snr_diff = -10; int next_phr = -2; tpcfsm.set_snr(target_snr + snr_diff, tpc::PUSCH_CODE); tpcfsm.set_snr(target_snr + snr_diff, tpc::PUCCH_CODE); sum_pucch = 0; for (uint32_t i = 0; i < 3; ++i) { tpcfsm.set_phr(next_phr, 1); for (uint32_t j = 0; j < 100; ++j) { tpcfsm.new_tti(); int tpc_pusch = decode_tpc(tpcfsm.encode_pusch_tpc()); TESTASSERT(tpc_pusch <= 0); next_phr -= tpc_pusch; sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc()); } TESTASSERT(next_phr == 0); } TESTASSERT(sum_pucch == -snr_diff); // PUCCH doesnt get affected by neg PHR // TEST: PHR is positive and SINR < target SINR. Checks: // - accumulation of TPCs should not make next PHR negative // - TPCs should be positive or zero next_phr = 5; snr_diff = -10; tpcfsm.set_phr(next_phr, 1); tpcfsm.set_snr(target_snr + snr_diff, tpc::PUSCH_CODE); for (uint32_t j = 0; j < 100; ++j) { tpcfsm.new_tti(); int tpc_pusch = decode_tpc(tpcfsm.encode_pusch_tpc()); next_phr -= tpc_pusch; TESTASSERT(tpc_pusch >= 0); } TESTASSERT(next_phr == 0); return SRSRAN_SUCCESS; } int test_undefined_target_snr() { const uint32_t nof_prbs = 50; tpc tpcfsm(0x46, nof_prbs, -1, -1, true); 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 += decode_tpc(tpcfsm.encode_pusch_tpc()); sum_pucch += decode_tpc(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, tpc::PUSCH_CODE); tpcfsm.set_snr(snr_info, tpc::PUCCH_CODE); sum_pusch = 0; sum_pucch = 0; for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); sum_pusch += decode_tpc(tpcfsm.encode_pusch_tpc()); sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc()); } TESTASSERT(sum_pusch == 0); TESTASSERT(sum_pucch == 0); // TEST: Check that high PHR allows full utilization of available PRBs, TPC remains at zero (no target SINR) int phr = 30; tpcfsm.set_phr(phr, 1); 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 += decode_tpc(tpcfsm.encode_pusch_tpc()); sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc()); } TESTASSERT(sum_pusch == 0 and sum_pucch == 0); // 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, 1); TESTASSERT(tpcfsm.max_ul_prbs() < 50); for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); TESTASSERT(decode_tpc(tpcfsm.encode_pusch_tpc()) == 0); TESTASSERT(decode_tpc(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, 1); 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 += decode_tpc(tpcfsm.encode_pusch_tpc()); sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc()); } TESTASSERT(sum_pusch == 0); TESTASSERT(sum_pucch == 0); return SRSRAN_SUCCESS; } void test_finite_target_snr_tpc_period_above_1() { const uint32_t nof_prbs = 50; const int target_snr = 15; tpc tpcfsm(0x46, nof_prbs, 15, 15, true, 0, 5); // TEST: While UL SNR ~ target, no TPC commands are sent for (uint32_t i = 0; i < 100 and tpcfsm.get_ul_snr_estim(0) < 14; ++i) { tpcfsm.set_snr(15, 0); tpcfsm.set_snr(15, 1); tpcfsm.new_tti(); } for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); TESTASSERT(decode_tpc(tpcfsm.encode_pucch_tpc()) == 0); TESTASSERT(decode_tpc(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, tpc::PUSCH_CODE); tpcfsm.set_snr(target_snr + snr_diff, tpc::PUCCH_CODE); int sum_pusch = 0, sum_pucch = 0; for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); int tpc = decode_tpc(tpcfsm.encode_pusch_tpc()); TESTASSERT(tpc <= 0); sum_pusch += tpc; sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc()); TESTASSERT(sum_pucch < 0 and sum_pucch >= -snr_diff); } TESTASSERT(sum_pusch == -snr_diff); } } // namespace srsenb int main() { TESTASSERT(srsenb::test_finite_target_snr() == 0); TESTASSERT(srsenb::test_undefined_target_snr() == 0); srsenb::test_finite_target_snr_tpc_period_above_1(); printf("Success\n"); }