diff --git a/lib/include/srsran/interfaces/sched_interface.h b/lib/include/srsran/interfaces/sched_interface.h index bbb8e9956..7464592e4 100644 --- a/lib/include/srsran/interfaces/sched_interface.h +++ b/lib/include/srsran/interfaces/sched_interface.h @@ -45,16 +45,20 @@ public: } cell_cfg_sib_t; struct sched_args_t { - std::string sched_policy = "time_pf"; - std::string sched_policy_args = "2"; - int pdsch_mcs = -1; - int pdsch_max_mcs = 28; - int pusch_mcs = -1; - int pusch_max_mcs = 28; - uint32_t min_nof_ctrl_symbols = 1; - uint32_t max_nof_ctrl_symbols = 3; - int max_aggr_level = 3; - bool pucch_mux_enabled = false; + std::string sched_policy = "time_pf"; + std::string sched_policy_args = "2"; + int pdsch_mcs = -1; + int pdsch_max_mcs = 28; + int pusch_mcs = -1; + int pusch_max_mcs = 28; + uint32_t min_nof_ctrl_symbols = 1; + uint32_t max_nof_ctrl_symbols = 3; + int max_aggr_level = 3; + 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 { diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 937192caa..fc84d6df2 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -167,6 +167,10 @@ enable = false # pusch_max_mcs: Optional PUSCH MCS limit # min_nof_ctrl_symbols: Minimum 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] @@ -180,6 +184,10 @@ enable = false #min_nof_ctrl_symbols = 1 #max_nof_ctrl_symbols = 3 #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 diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h index 3e72f4de1..6f07424dc 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h @@ -33,7 +33,7 @@ struct sched_ue_cell { void clear_feedback(); 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; } 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; } 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_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{}; uint32_t dl_pmi = 0; tti_point dl_pmi_tti_rx{}; - uint32_t ul_cqi = 1; tti_point ul_cqi_tti_rx{}; uint32_t max_mcs_dl = 28, max_mcs_ul = 28; @@ -84,6 +88,11 @@ private: tti_point current_tti; 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; }; diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h index 6de07c7dc..71bbd333f 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h @@ -88,10 +88,6 @@ public: for (size_t chidx = 0; chidx < 2; ++chidx) { float target_snr_dB = chidx == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB; auto& ch_snr = snr_estim_list[chidx]; - if (target_snr_dB < 0) { - ch_snr.pending_delta = 0; - continue; - } // Enqueue pending UL Channel SNR measurement if (ch_snr.pending_snr == null_snr) { @@ -105,7 +101,9 @@ public: 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) - ch_snr.win_tpc_values.push(ch_snr.pending_delta); + if (target_snr_dB >= 0) { + ch_snr.win_tpc_values.push(ch_snr.pending_delta); + } ch_snr.pending_delta = 0; } } @@ -126,6 +124,8 @@ public: 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: uint8_t encode_tpc_delta(int8_t delta) { diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 80ba7258d..ac40533b3 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -149,6 +149,10 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("scheduler.max_nof_ctrl_symbols", bpo::value(&args->stack.mac.sched.max_nof_ctrl_symbols)->default_value(3), "Number of control symbols") ("scheduler.min_nof_ctrl_symbols", bpo::value(&args->stack.mac.sched.min_nof_ctrl_symbols)->default_value(1), "Minimum number of control symbols") ("scheduler.pucch_multiplex_enable", bpo::value(&args->stack.mac.sched.pucch_mux_enabled)->default_value(false), "Enable PUCCH multiplexing") + ("scheduler.target_bler", bpo::value(&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(&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(&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(&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 */ diff --git a/srsenb/src/stack/mac/sched_ue.cc b/srsenb/src/stack/mac/sched_ue.cc index 5d48a322f..2b5a42b5e 100644 --- a/srsenb/src/stack/mac/sched_ue.cc +++ b/srsenb/src/stack/mac/sched_ue.cc @@ -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 tbs_acked = -1; - if (cells[enb_cc_idx].cc_state() != cc_st::idle) { - std::pair 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; + return cells[enb_cc_idx].set_ack_info(tti_rx, tb_idx, ack); } 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]; 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())); - 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) 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 nof_symb = 2 * (SRSRAN_CP_NSYMB(cell.cp) - 1) - N_srs; 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) 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; for (uint32_t cc = 0; cc < cells.size(); ++cc) { 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) { max_cqi = sum_cqi; 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]; 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) diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc index 22cacb397..0a3e3e20f 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc @@ -13,10 +13,9 @@ #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_phy_ch/sched_dci.h" -#include "srsenb/hdr/stack/mac/schedulers/sched_base.h" #include -#define CHECK_INVALID_FEEDBACK(feedback_type) \ +#define CHECK_VALID_CC(feedback_type) \ do { \ if (cc_state() == cc_st::idle) { \ 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) { 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_) @@ -148,7 +153,6 @@ void sched_ue_cell::clear_feedback() dl_pmi = 0; dl_pmi_tti_rx = tti_point{}; 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{}; } @@ -158,39 +162,84 @@ void sched_ue_cell::finish_tti(tti_point 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_); 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 cc_state_ = cc_st::active; 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) { - CHECK_INVALID_FEEDBACK("UL CRC"); + CHECK_VALID_CC("UL CRC"); // Update HARQ process int pid = harq_ent.set_ul_crc(tti_rx, 0, crc_res); if (pid < 0) { logger.warning("SCHED: rnti=0x%x received UL CRC for invalid tti_rx=%d", (int)tti_rx.to_uint()); return SRSRAN_ERROR; } + + ul_snr_coeff += delta_down - static_cast(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; } +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 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(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) { - CHECK_INVALID_FEEDBACK("UL SNR estimate"); + CHECK_VALID_CC("UL SNR estimate"); tpc_fsm.set_snr(ul_snr, ul_ch_code); if (ul_ch_code == tpc::PUSCH_CODE) { - ul_cqi = srsran_cqi_from_snr(ul_snr); ul_cqi_tti_rx = tti_rx; } 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 ************************************************************/ @@ -280,7 +329,7 @@ tbs_info cqi_to_tbs_dl(const sched_ue_cell& cell, tbs_info ret; if (cell.fixed_mcs_dl < 0 or not cell.dl_cqi().is_cqi_info_received()) { // 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( 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) { // Dynamic MCS 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 // handled by the scheduler, but we might be scheduling undecodable codewords at very low SNR diff --git a/srsenb/test/mac/sched_ue_cell_test.cc b/srsenb/test/mac/sched_ue_cell_test.cc index a6a90e81b..44ff63269 100644 --- a/srsenb/test/mac/sched_ue_cell_test.cc +++ b/srsenb/test/mac/sched_ue_cell_test.cc @@ -38,9 +38,8 @@ void test_neg_phr_scenario() ue_cc.set_ue_cfg(ue_cfg); float snr = 0; - ue_cc.tpc_fsm.set_snr(snr, 0); - ue_cc.tpc_fsm.set_snr(snr, 1); - ue_cc.ul_cqi = srsran_cqi_from_snr(snr); + ue_cc.set_ul_snr(tti_point(0), snr, 0); + ue_cc.set_ul_snr(tti_point(0), snr, 1); ue_cc.tpc_fsm.set_phr(-5); ue_cc.new_tti(tti_point(0));