feature - implement adaptive link adaptation in DL and UL

master
Francisco 4 years ago committed by Ismael Gomez
parent 8811b7c9a7
commit c5029fc266

@ -55,6 +55,10 @@ public:
uint32_t max_nof_ctrl_symbols = 3; uint32_t max_nof_ctrl_symbols = 3;
int max_aggr_level = 3; int max_aggr_level = 3;
bool pucch_mux_enabled = false; bool pucch_mux_enabled = false;
float target_bler = 0.05;
float max_delta_dl_cqi = 5;
float max_delta_ul_snr = 5;
float adaptive_link_step_size = 0.001;
}; };
struct cell_cfg_t { struct cell_cfg_t {

@ -167,6 +167,10 @@ enable = false
# pusch_max_mcs: Optional PUSCH MCS limit # pusch_max_mcs: Optional PUSCH MCS limit
# min_nof_ctrl_symbols: Minimum number of control symbols # min_nof_ctrl_symbols: Minimum number of control symbols
# max_nof_ctrl_symbols: Maximum number of control symbols # max_nof_ctrl_symbols: Maximum number of control symbols
# target_bler: Target BLER (in decimal) to achieve via adaptive link
# max_delta_dl_cqi: Maximum shift in CQI for adaptive DL link
# max_delta_ul_cqi: Maximum shift in UL SNR for adaptive UL link
# target_bler: Step size or learning rate used in adaptive link
# #
##################################################################### #####################################################################
[scheduler] [scheduler]
@ -180,6 +184,10 @@ enable = false
#min_nof_ctrl_symbols = 1 #min_nof_ctrl_symbols = 1
#max_nof_ctrl_symbols = 3 #max_nof_ctrl_symbols = 3
#pucch_multiplex_enable = false #pucch_multiplex_enable = false
#target_bler = 0.05
#max_delta_dl_cqi = 5
#max_delta_ul_snr = 5
#adaptive_link_step_size = 0.001
##################################################################### #####################################################################
# eMBMS configuration options # eMBMS configuration options

@ -33,7 +33,7 @@ struct sched_ue_cell {
void clear_feedback(); void clear_feedback();
void finish_tti(tti_point tti_rx); void finish_tti(tti_point tti_rx);
void set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_); int set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_);
bool configured() const { return ue_cc_idx >= 0; } bool configured() const { return ue_cc_idx >= 0; }
int get_ue_cc_idx() const { return ue_cc_idx; } int get_ue_cc_idx() const { return ue_cc_idx; }
@ -43,6 +43,11 @@ struct sched_ue_cell {
const sched_interface::ue_cfg_t* get_ue_cfg() const { return configured() ? ue_cfg : nullptr; } const sched_interface::ue_cfg_t* get_ue_cfg() const { return configured() ? ue_cfg : nullptr; }
cc_st cc_state() const { return cc_state_; } cc_st cc_state() const { return cc_state_; }
int get_dl_cqi() const;
int get_dl_cqi(const rbgmask_t& rbgs) const;
int get_ul_cqi() const;
int set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack);
int set_ul_crc(tti_point tti_rx, bool crc_res); int set_ul_crc(tti_point tti_rx, bool crc_res);
int set_ul_snr(tti_point tti_rx, float ul_snr, uint32_t ul_ch_code); int set_ul_snr(tti_point tti_rx, float ul_snr, uint32_t ul_ch_code);
@ -66,7 +71,6 @@ struct sched_ue_cell {
tti_point dl_ri_tti_rx{}; tti_point dl_ri_tti_rx{};
uint32_t dl_pmi = 0; uint32_t dl_pmi = 0;
tti_point dl_pmi_tti_rx{}; tti_point dl_pmi_tti_rx{};
uint32_t ul_cqi = 1;
tti_point ul_cqi_tti_rx{}; tti_point ul_cqi_tti_rx{};
uint32_t max_mcs_dl = 28, max_mcs_ul = 28; uint32_t max_mcs_dl = 28, max_mcs_ul = 28;
@ -84,6 +88,11 @@ private:
tti_point current_tti; tti_point current_tti;
cc_st cc_state_ = cc_st::idle; cc_st cc_state_ = cc_st::idle;
// CQI
float delta_down = 0, delta_up = 0;
float dl_cqi_coeff = 0, ul_snr_coeff = 0;
float max_cqi_coeff = -5, max_snr_coeff = 5;
sched_dl_cqi dl_cqi_ctxt; sched_dl_cqi dl_cqi_ctxt;
}; };

@ -88,10 +88,6 @@ public:
for (size_t chidx = 0; chidx < 2; ++chidx) { for (size_t chidx = 0; chidx < 2; ++chidx) {
float target_snr_dB = chidx == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB; float target_snr_dB = chidx == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB;
auto& ch_snr = snr_estim_list[chidx]; auto& ch_snr = snr_estim_list[chidx];
if (target_snr_dB < 0) {
ch_snr.pending_delta = 0;
continue;
}
// Enqueue pending UL Channel SNR measurement // Enqueue pending UL Channel SNR measurement
if (ch_snr.pending_snr == null_snr) { if (ch_snr.pending_snr == null_snr) {
@ -105,7 +101,9 @@ public:
ch_snr.pending_snr = null_snr; ch_snr.pending_snr = null_snr;
// Enqueue PUSCH/PUCCH TPC sent in last TTI (zero for both Delta_PUSCH/Delta_PUCCH=0 and TPC not sent) // Enqueue PUSCH/PUCCH TPC sent in last TTI (zero for both Delta_PUSCH/Delta_PUCCH=0 and TPC not sent)
if (target_snr_dB >= 0) {
ch_snr.win_tpc_values.push(ch_snr.pending_delta); ch_snr.win_tpc_values.push(ch_snr.pending_delta);
}
ch_snr.pending_delta = 0; ch_snr.pending_delta = 0;
} }
} }
@ -126,6 +124,8 @@ public:
uint32_t max_ul_prbs() const { return max_prbs_cached; } uint32_t max_ul_prbs() const { return max_prbs_cached; }
float get_ul_snr_estim(uint32_t ul_ch_code = PUSCH_CODE) const { return snr_estim_list[ul_ch_code].snr_avg.value(); }
private: private:
uint8_t encode_tpc_delta(int8_t delta) uint8_t encode_tpc_delta(int8_t delta)
{ {

@ -149,6 +149,10 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("scheduler.max_nof_ctrl_symbols", bpo::value<uint32_t>(&args->stack.mac.sched.max_nof_ctrl_symbols)->default_value(3), "Number of control symbols") ("scheduler.max_nof_ctrl_symbols", bpo::value<uint32_t>(&args->stack.mac.sched.max_nof_ctrl_symbols)->default_value(3), "Number of control symbols")
("scheduler.min_nof_ctrl_symbols", bpo::value<uint32_t>(&args->stack.mac.sched.min_nof_ctrl_symbols)->default_value(1), "Minimum number of control symbols") ("scheduler.min_nof_ctrl_symbols", bpo::value<uint32_t>(&args->stack.mac.sched.min_nof_ctrl_symbols)->default_value(1), "Minimum number of control symbols")
("scheduler.pucch_multiplex_enable", bpo::value<bool>(&args->stack.mac.sched.pucch_mux_enabled)->default_value(false), "Enable PUCCH multiplexing") ("scheduler.pucch_multiplex_enable", bpo::value<bool>(&args->stack.mac.sched.pucch_mux_enabled)->default_value(false), "Enable PUCCH multiplexing")
("scheduler.target_bler", bpo::value<float>(&args->stack.mac.sched.target_bler)->default_value(0.05), "Target BLER (in decimal) to achieve via adaptive link")
("scheduler.max_delta_dl_cqi", bpo::value<float>(&args->stack.mac.sched.max_delta_dl_cqi)->default_value(5.0), "Maximum shift in CQI for adaptive DL link")
("scheduler.max_delta_ul_snr", bpo::value<float>(&args->stack.mac.sched.max_delta_ul_snr)->default_value(5.0), "Maximum shift in UL SNR for adaptive UL link")
("scheduler.adaptive_link_step_size", bpo::value<float>(&args->stack.mac.sched.max_delta_ul_snr)->default_value(0.001), "Step size or learning rate used in adaptive link")
/* Downlink Channel emulator section */ /* Downlink Channel emulator section */

@ -227,20 +227,7 @@ bool sched_ue::pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_p
int sched_ue::set_ack_info(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) int sched_ue::set_ack_info(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack)
{ {
int tbs_acked = -1; return cells[enb_cc_idx].set_ack_info(tti_rx, tb_idx, ack);
if (cells[enb_cc_idx].cc_state() != cc_st::idle) {
std::pair<uint32_t, int> p2 = cells[enb_cc_idx].harq_ent.set_ack_info(tti_rx, tb_idx, ack);
tbs_acked = p2.second;
if (tbs_acked > 0) {
logger.debug(
"SCHED: Set DL ACK=%d for rnti=0x%x, pid=%d, tb=%d, tti=%d", ack, rnti, p2.first, tb_idx, tti_rx.to_uint());
} else {
logger.warning("SCHED: Received ACK info for unknown TTI=%d", tti_rx.to_uint());
}
} else {
logger.warning("Received DL ACK for invalid cell index %d", enb_cc_idx);
}
return tbs_acked;
} }
void sched_ue::set_ul_crc(tti_point tti_rx, uint32_t enb_cc_idx, bool crc_res) void sched_ue::set_ul_crc(tti_point tti_rx, uint32_t enb_cc_idx, bool crc_res)
@ -815,7 +802,7 @@ uint32_t sched_ue::get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs) co
auto& cc = cells[enb_cc_idx]; auto& cc = cells[enb_cc_idx];
uint32_t nof_re = uint32_t nof_re =
cc.cell_cfg->get_dl_lb_nof_re(to_tx_dl(current_tti), count_prb_per_tb_approx(nof_rbgs, cc.cell_cfg->nof_prb())); cc.cell_cfg->get_dl_lb_nof_re(to_tx_dl(current_tti), count_prb_per_tb_approx(nof_rbgs, cc.cell_cfg->nof_prb()));
float max_coderate = srsran_cqi_to_coderate(std::min(cc.dl_cqi().get_avg_cqi() + 1u, 15u), cfg.use_tbs_index_alt); float max_coderate = srsran_cqi_to_coderate(std::min(cc.get_dl_cqi() + 1u, 15u), cfg.use_tbs_index_alt);
// Inverse of srsran_coderate(tbs, nof_re) // Inverse of srsran_coderate(tbs, nof_re)
uint32_t tbs = max_coderate * nof_re - 24; uint32_t tbs = max_coderate * nof_re - 24;
@ -829,7 +816,7 @@ uint32_t sched_ue::get_expected_ul_bitrate(uint32_t enb_cc_idx, int nof_prbs) co
uint32_t N_srs = 0; uint32_t N_srs = 0;
uint32_t nof_symb = 2 * (SRSRAN_CP_NSYMB(cell.cp) - 1) - N_srs; uint32_t nof_symb = 2 * (SRSRAN_CP_NSYMB(cell.cp) - 1) - N_srs;
uint32_t nof_re = nof_symb * nof_prbs_alloc * SRSRAN_NRE; uint32_t nof_re = nof_symb * nof_prbs_alloc * SRSRAN_NRE;
float max_coderate = srsran_cqi_to_coderate(std::min(cells[enb_cc_idx].ul_cqi + 1u, 15u), false); float max_coderate = srsran_cqi_to_coderate(std::min(cells[enb_cc_idx].get_ul_cqi() + 1u, 15u), false);
// Inverse of srsran_coderate(tbs, nof_re) // Inverse of srsran_coderate(tbs, nof_re)
uint32_t tbs = max_coderate * nof_re - 24; uint32_t tbs = max_coderate * nof_re - 24;
@ -884,7 +871,7 @@ uint32_t sched_ue::get_pending_ul_data_total(tti_point tti_tx_ul, int this_enb_c
uint32_t max_cqi = 0, max_cc_idx = 0; uint32_t max_cqi = 0, max_cc_idx = 0;
for (uint32_t cc = 0; cc < cells.size(); ++cc) { for (uint32_t cc = 0; cc < cells.size(); ++cc) {
if (cells[cc].configured()) { if (cells[cc].configured()) {
uint32_t sum_cqi = cells[cc].dl_cqi().get_avg_cqi() + cells[cc].ul_cqi; uint32_t sum_cqi = cells[cc].get_dl_cqi() + cells[cc].get_ul_cqi();
if (cells[cc].cc_state() == cc_st::active and sum_cqi > max_cqi) { if (cells[cc].cc_state() == cc_st::active and sum_cqi > max_cqi) {
max_cqi = sum_cqi; max_cqi = sum_cqi;
max_cc_idx = cc; max_cc_idx = cc;
@ -978,7 +965,7 @@ uint32_t sched_ue::get_aggr_level(uint32_t enb_cc_idx, uint32_t nof_bits)
{ {
const auto& cc = cells[enb_cc_idx]; const auto& cc = cells[enb_cc_idx];
return srsenb::get_aggr_level( return srsenb::get_aggr_level(
nof_bits, cc.dl_cqi().get_avg_cqi(), cc.max_aggr_level, cc.cell_cfg->nof_prb(), cfg.use_tbs_index_alt); nof_bits, cc.get_dl_cqi(), cc.max_aggr_level, cc.cell_cfg->nof_prb(), cfg.use_tbs_index_alt);
} }
void sched_ue::finish_tti(tti_point tti_rx, uint32_t enb_cc_idx) void sched_ue::finish_tti(tti_point tti_rx, uint32_t enb_cc_idx)

@ -13,10 +13,9 @@
#include "srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h" #include "srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h"
#include "srsenb/hdr/stack/mac/sched_helpers.h" #include "srsenb/hdr/stack/mac/sched_helpers.h"
#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h" #include "srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h"
#include "srsenb/hdr/stack/mac/schedulers/sched_base.h"
#include <numeric> #include <numeric>
#define CHECK_INVALID_FEEDBACK(feedback_type) \ #define CHECK_VALID_CC(feedback_type) \
do { \ do { \
if (cc_state() == cc_st::idle) { \ if (cc_state() == cc_st::idle) { \
logger.warning("SCHED: rnti=0x%x received " feedback_type " for idle cc=%d", cell_cfg->enb_cc_idx); \ logger.warning("SCHED: rnti=0x%x received " feedback_type " for idle cc=%d", cell_cfg->enb_cc_idx); \
@ -47,6 +46,12 @@ sched_ue_cell::sched_ue_cell(uint16_t rnti_, const sched_cell_params_t& cell_cfg
dl_cqi_ctxt(cell_cfg_.nof_prb(), 0, 1) dl_cqi_ctxt(cell_cfg_.nof_prb(), 0, 1)
{ {
clear_feedback(); clear_feedback();
float target_bler = cell_cfg->sched_cfg->target_bler;
delta_down = cell_cfg->sched_cfg->adaptive_link_step_size;
delta_up = (1 - target_bler) * delta_down / target_bler;
max_cqi_coeff = cell_cfg->sched_cfg->max_delta_dl_cqi;
max_snr_coeff = cell_cfg->sched_cfg->max_delta_ul_snr;
} }
void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_) void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_)
@ -148,7 +153,6 @@ void sched_ue_cell::clear_feedback()
dl_pmi = 0; dl_pmi = 0;
dl_pmi_tti_rx = tti_point{}; dl_pmi_tti_rx = tti_point{};
dl_cqi_ctxt.reset_cqi(ue_cc_idx == 0 ? cell_cfg->cfg.initial_dl_cqi : 1); dl_cqi_ctxt.reset_cqi(ue_cc_idx == 0 ? cell_cfg->cfg.initial_dl_cqi : 1);
ul_cqi = 1;
ul_cqi_tti_rx = tti_point{}; ul_cqi_tti_rx = tti_point{};
} }
@ -158,39 +162,84 @@ void sched_ue_cell::finish_tti(tti_point tti_rx)
harq_ent.reset_pending_data(tti_rx); harq_ent.reset_pending_data(tti_rx);
} }
void sched_ue_cell::set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_) int sched_ue_cell::set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_)
{ {
CHECK_VALID_CC("DL CQI");
dl_cqi_ctxt.cqi_wb_info(tti_rx, dl_cqi_); dl_cqi_ctxt.cqi_wb_info(tti_rx, dl_cqi_);
if (ue_cc_idx > 0 and cc_state_ == cc_st::activating and dl_cqi_ > 0) { if (ue_cc_idx > 0 and cc_state_ == cc_st::activating and dl_cqi_ > 0) {
// Wait for SCell to receive a positive CQI before activating it // Wait for SCell to receive a positive CQI before activating it
cc_state_ = cc_st::active; cc_state_ = cc_st::active;
logger.info("SCHED: SCell index=%d is now active", ue_cc_idx); logger.info("SCHED: SCell index=%d is now active", ue_cc_idx);
} }
return SRSRAN_SUCCESS;
} }
int sched_ue_cell::set_ul_crc(tti_point tti_rx, bool crc_res) int sched_ue_cell::set_ul_crc(tti_point tti_rx, bool crc_res)
{ {
CHECK_INVALID_FEEDBACK("UL CRC"); CHECK_VALID_CC("UL CRC");
// Update HARQ process // Update HARQ process
int pid = harq_ent.set_ul_crc(tti_rx, 0, crc_res); int pid = harq_ent.set_ul_crc(tti_rx, 0, crc_res);
if (pid < 0) { if (pid < 0) {
logger.warning("SCHED: rnti=0x%x received UL CRC for invalid tti_rx=%d", (int)tti_rx.to_uint()); logger.warning("SCHED: rnti=0x%x received UL CRC for invalid tti_rx=%d", (int)tti_rx.to_uint());
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
ul_snr_coeff += delta_down - static_cast<float>(not crc_res) * (delta_down + delta_up);
ul_snr_coeff = std::min(std::max(-max_snr_coeff, ul_snr_coeff), max_snr_coeff);
logger.info("SCHED: adjusting UL SNR by %f", ul_snr_coeff);
return pid; return pid;
} }
int sched_ue_cell::set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack)
{
CHECK_VALID_CC("DL ACK Info");
std::pair<uint32_t, int> p2 = harq_ent.set_ack_info(tti_rx, tb_idx, ack);
int tbs_acked = p2.second;
if (tbs_acked > 0) {
logger.debug(
"SCHED: Set DL ACK=%d for rnti=0x%x, pid=%d, tb=%d, tti=%d", ack, rnti, p2.first, tb_idx, tti_rx.to_uint());
dl_cqi_coeff += delta_down - static_cast<float>(not ack) * (delta_down + delta_up);
dl_cqi_coeff = std::min(std::max(-max_cqi_coeff, dl_cqi_coeff), max_cqi_coeff);
logger.info("SCHED: adjusting DL CQI by %f", dl_cqi_coeff);
} else {
logger.warning("SCHED: Received ACK info for unknown TTI=%d", tti_rx.to_uint());
}
return tbs_acked;
}
int sched_ue_cell::set_ul_snr(tti_point tti_rx, float ul_snr, uint32_t ul_ch_code) int sched_ue_cell::set_ul_snr(tti_point tti_rx, float ul_snr, uint32_t ul_ch_code)
{ {
CHECK_INVALID_FEEDBACK("UL SNR estimate"); CHECK_VALID_CC("UL SNR estimate");
tpc_fsm.set_snr(ul_snr, ul_ch_code); tpc_fsm.set_snr(ul_snr, ul_ch_code);
if (ul_ch_code == tpc::PUSCH_CODE) { if (ul_ch_code == tpc::PUSCH_CODE) {
ul_cqi = srsran_cqi_from_snr(ul_snr);
ul_cqi_tti_rx = tti_rx; ul_cqi_tti_rx = tti_rx;
} }
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int sched_ue_cell::get_ul_cqi() const
{
if (not ul_cqi_tti_rx.is_valid()) {
return 1;
}
float snr = tpc_fsm.get_ul_snr_estim();
return srsran_cqi_from_snr(snr + ul_snr_coeff);
}
int sched_ue_cell::get_dl_cqi(const rbgmask_t& rbgs) const
{
float dl_cqi = std::get<1>(find_min_cqi_rbg(rbgs, dl_cqi_ctxt));
return std::max(0, (int)std::min(dl_cqi + dl_cqi_coeff, 15.0f));
}
int sched_ue_cell::get_dl_cqi() const
{
rbgmask_t rbgmask(cell_cfg->nof_rbgs);
rbgmask.fill(0, rbgmask.size());
return get_dl_cqi(rbgmask);
}
/************************************************************* /*************************************************************
* TBS/MCS derivation * TBS/MCS derivation
************************************************************/ ************************************************************/
@ -280,7 +329,7 @@ tbs_info cqi_to_tbs_dl(const sched_ue_cell& cell,
tbs_info ret; tbs_info ret;
if (cell.fixed_mcs_dl < 0 or not cell.dl_cqi().is_cqi_info_received()) { if (cell.fixed_mcs_dl < 0 or not cell.dl_cqi().is_cqi_info_received()) {
// Dynamic MCS configured or first Tx // Dynamic MCS configured or first Tx
uint32_t dl_cqi = std::get<1>(find_min_cqi_rbg(rbgs, cell.dl_cqi())); uint32_t dl_cqi = cell.get_dl_cqi(rbgs);
ret = compute_min_mcs_and_tbs_from_required_bytes( ret = compute_min_mcs_and_tbs_from_required_bytes(
nof_prbs, nof_re, dl_cqi, cell.max_mcs_dl, req_bytes, false, false, use_tbs_index_alt); nof_prbs, nof_re, dl_cqi, cell.max_mcs_dl, req_bytes, false, false, use_tbs_index_alt);
@ -309,7 +358,7 @@ tbs_info cqi_to_tbs_ul(const sched_ue_cell& cell, uint32_t nof_prb, uint32_t nof
if (mcs < 0) { if (mcs < 0) {
// Dynamic MCS // Dynamic MCS
ret = compute_min_mcs_and_tbs_from_required_bytes( ret = compute_min_mcs_and_tbs_from_required_bytes(
nof_prb, nof_re, cell.ul_cqi, cell.max_mcs_ul, req_bytes, true, ulqam64_enabled, false); nof_prb, nof_re, cell.get_ul_cqi(), cell.max_mcs_ul, req_bytes, true, ulqam64_enabled, false);
// If coderate > SRSRAN_MIN(max_coderate, 0.932 * Qm) we should set TBS=0. We don't because it's not correctly // If coderate > SRSRAN_MIN(max_coderate, 0.932 * Qm) we should set TBS=0. We don't because it's not correctly
// handled by the scheduler, but we might be scheduling undecodable codewords at very low SNR // handled by the scheduler, but we might be scheduling undecodable codewords at very low SNR

@ -38,9 +38,8 @@ void test_neg_phr_scenario()
ue_cc.set_ue_cfg(ue_cfg); ue_cc.set_ue_cfg(ue_cfg);
float snr = 0; float snr = 0;
ue_cc.tpc_fsm.set_snr(snr, 0); ue_cc.set_ul_snr(tti_point(0), snr, 0);
ue_cc.tpc_fsm.set_snr(snr, 1); ue_cc.set_ul_snr(tti_point(0), snr, 1);
ue_cc.ul_cqi = srsran_cqi_from_snr(snr);
ue_cc.tpc_fsm.set_phr(-5); ue_cc.tpc_fsm.set_phr(-5);
ue_cc.new_tti(tti_point(0)); ue_cc.new_tti(tti_point(0));

Loading…
Cancel
Save