diff --git a/lib/include/srslte/adt/accumulators.h b/lib/include/srslte/adt/accumulators.h index 52ea26f77..c87d4cdfb 100644 --- a/lib/include/srslte/adt/accumulators.h +++ b/lib/include/srslte/adt/accumulators.h @@ -36,10 +36,13 @@ private: template struct exp_average_fast_start { - exp_average_fast_start(T alpha_, uint32_t start_size = 100) : alpha(alpha_), nof_left(start_size) {} + exp_average_fast_start(T alpha_, uint32_t start_size = 100) : alpha(alpha_), start_count_size(start_size) + { + assert(start_size > 0); + } void push(T sample) { - if (nof_left-- > 0) { + if (count < start_count_size) { avg_ += (sample - avg_) / (count + 1); count++; } else { @@ -51,7 +54,7 @@ struct exp_average_fast_start { private: T avg_ = 0; uint32_t count = 0; - uint32_t nof_left; + uint32_t start_count_size; T alpha; }; @@ -107,25 +110,25 @@ private: template struct null_sliding_average { - null_sliding_average(uint32_t N, T null_val = std::numeric_limits::max()) : - null_value_(null_val), window(N, null_val) - {} + static constexpr T null_value = std::numeric_limits::max(); + + null_sliding_average(uint32_t N) : window(N, null_value) {} void push(T sample) { window.push(sample); } - void push_hole() { window.push(null_value_); } + void push_hole() { window.push(null_value); } T value() const { - T ret = 0; + T ret = 0; + uint32_t count = 0; for (size_t i = 0; i < window.size(); ++i) { - if (window[i] != null_value_) { + if (window[i] != null_value) { ret += window[i]; + count++; } } - return ret; + return (count == 0) ? null_value : ret / count; } - T null_value() const { return null_value_; } private: - T null_value_; detail::sliding_window window; }; diff --git a/lib/include/srslte/interfaces/enb_rrc_interface_types.h b/lib/include/srslte/interfaces/enb_rrc_interface_types.h index f32ecbc25..095391c1b 100644 --- a/lib/include/srslte/interfaces/enb_rrc_interface_types.h +++ b/lib/include/srslte/interfaces/enb_rrc_interface_types.h @@ -53,6 +53,7 @@ struct cell_cfg_t { double dl_freq_hz; uint32_t ul_earfcn; double ul_freq_hz; + int target_ul_sinr_db; uint32_t initial_dl_cqi; std::vector scell_list; rrc_meas_cfg_t meas_cfg; diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h index fe773381f..e0279de86 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h @@ -22,34 +22,33 @@ namespace srsenb { * Class to handle TPC Commands sent to the UE. * The TPC value sent to the UE in each DCI is a result of: * - the difference between the target SINR and the windowed average of past UE UL SNR estimates - * - subtracted by the sum of TPC values sent in the last TX_UL_DELAY milliseconds (i.e. 8ms), which the UE hasn't yet - * applied + * - subtracted by the sum of TPC values sent since the last PHR */ class tpc { static constexpr size_t SNR_WINDOW_SIZE_MS = 8; + static constexpr int PHR_NEG_NOF_PRB = 1; public: - tpc(float target_snr_dB_ = -1.0) : - target_snr_dB(target_snr_dB_), - pusch_tpc_values(FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS), - pucch_tpc_values(FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS), - snr_avg(SNR_WINDOW_SIZE_MS) + 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) { - pending_snr = snr_avg.null_value(); + max_prbs_cached = nof_prb; } void set_cfg(float target_snr_dB_) { target_snr_dB = target_snr_dB_; } void set_snr(float snr) { pending_snr = snr; } void set_phr(int phr_) { - last_phr = phr_; + last_phr = phr_; + sum_pusch_tpc_values = 0; + sum_pucch_tpc_values = 0; // compute and cache the max nof UL PRBs that avoids overflowing PHR - max_prbs_cached = 1; - for (int nof_prbs = 100; nof_prbs >= 0; --nof_prbs) { - if (last_phr >= 10 * log10(nof_prbs)) { - max_prbs_cached = last_phr; + max_prbs_cached = PHR_NEG_NOF_PRB; + for (int n = nof_prb; n >= PHR_NEG_NOF_PRB - 1; --n) { + if (last_phr >= 10 * log10(n)) { + max_prbs_cached = n; break; } } @@ -61,61 +60,88 @@ public: return; } // Enqueue PUSCH/PUCCH TPC sent in last TTI (zero for both Delta_PUSCH/Delta_PUCCH=0 and TPC not sent) - pusch_tpc_values.push(pending_pusch_tpc); + sum_pusch_tpc_values += pending_pusch_tpc; pending_pusch_tpc = 0; - pusch_tpc_values.push(pending_pucch_tpc); + sum_pucch_tpc_values += pending_pucch_tpc; pending_pucch_tpc = 0; // Enqueue pending SNR measurement snr_avg.push(pending_snr); - pending_snr = snr_avg.null_value(); + pending_snr = snr_avg.null_value; } /** - * Called during DCI format0 encoding to set TPC command - * @return accumulated TPC value {-3, -1, 0, 1, 3} + * Called during DCI format0 encoding to set PUSCH TPC command + * @remark See TS 36.213 Section 5.1.1 + * @return accumulated TPC value {-1, 0, 1, 3} */ int8_t encode_pusch_tpc() { - if (target_snr_dB < 0) { - return 0; - } - float diff = target_snr_dB - snr_avg.value(); - diff -= pusch_tpc_values.value(); - int8_t ret = 0; - if (diff > 1) { - ret = diff > 3 ? 3 : 1; - } else if (diff <= -1) { - ret = -1; + pending_pusch_tpc = 0; + if (target_snr_dB <= 0) { + // undefined target SINR case. Increase Tx power, while the number of allocable PRBs remains unchanged + pending_pusch_tpc = (max_prbs_cached == nof_prb) ? 1 : (last_phr < 0 ? -1 : 0); + } 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; + if (diff > 1) { + pending_pusch_tpc = diff > 3 ? 3 : 1; + } else if (diff <= -1) { + pending_pusch_tpc = -1; + } + if (last_phr <= 0) { + // In case there is no headroom, forbid power increases + pending_pusch_tpc = std::min(pending_pusch_tpc, 0); + } } - pending_pusch_tpc = ret; - return ret; + return pending_pusch_tpc; } + /** + * Called during DCI format1/2A/A encoding to set PUCCH TPC command + * Note: For now we use the same algorithm for PUCCH and PUSCH + * @remark See TS 36.213 Section 5.1.2 + * @return accumulated TPC value {-1, 0, 1, 3} + */ int8_t encode_pucch_tpc() { - float diff = target_snr_dB - snr_avg.value(); - diff -= pucch_tpc_values.value(); - int8_t ret = 0; - if (diff > 1) { - ret = diff > 3 ? 3 : 1; - } else if (diff <= -1) { - ret = -1; + pending_pusch_tpc = 0; + if (target_snr_dB <= 0) { + // undefined target SINR case. Increase Tx power, while the number of allocable PRBs remains unchanged + pending_pucch_tpc = (max_prbs_cached == nof_prb) ? 1 : (last_phr < 0 ? -1 : 0); + } 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; + pending_pucch_tpc = 0; + if (diff > 1) { + pending_pucch_tpc = diff > 3 ? 3 : 1; + } else if (diff <= -1) { + pending_pucch_tpc = -1; + } + if (last_phr <= 0) { + // In case there is no headroom, forbid power increases + pending_pucch_tpc = std::min(pending_pucch_tpc, 0); + } } - pending_pucch_tpc = ret; - return ret; + return pending_pucch_tpc; } uint32_t max_ul_prbs() const { return max_prbs_cached; } private: - float target_snr_dB; + uint32_t nof_prb; + float target_snr_dB; + srslte::null_sliding_average snr_avg; - srslte::sliding_sum pusch_tpc_values, pucch_tpc_values; - uint32_t max_prbs_cached = 100; - int last_phr = 0; + int sum_pusch_tpc_values = 0; + int sum_pucch_tpc_values = 0; + uint32_t max_prbs_cached = 100; + int last_phr = std::numeric_limits::max(); int pending_pusch_tpc = 0, pending_pucch_tpc = 0; - float pending_snr = 0; + float pending_snr = srslte::null_sliding_average::null_value; }; } // namespace srsenb diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 8dbbbda5c..4e442a057 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -734,6 +734,7 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root) cell_cfg.root_seq_idx, cellroot, "root_seq_idx", rrc_cfg->sibs[1].sib2().rr_cfg_common.prach_cfg.root_seq_idx); parse_default_field(cell_cfg.initial_dl_cqi, cellroot, "initial_dl_cqi", 5u); parse_default_field(cell_cfg.meas_cfg.meas_gap_period, cellroot, "meas_gap_period", 0u); + HANDLEPARSERCODE(parse_default_field(cell_cfg.target_ul_sinr_db, cellroot, "target_ul_sinr", -1)); if (cellroot.exists("ho_active") and cellroot["ho_active"]) { HANDLEPARSERCODE(parse_meas_cell_list(&cell_cfg.meas_cfg, cellroot["meas_cell_list"])); diff --git a/srsenb/src/stack/mac/sched_ue.cc b/srsenb/src/stack/mac/sched_ue.cc index 365c8f802..772901995 100644 --- a/srsenb/src/stack/mac/sched_ue.cc +++ b/srsenb/src/stack/mac/sched_ue.cc @@ -1310,7 +1310,7 @@ cc_sched_ue::cc_sched_ue(const sched_interface::ue_cfg_t& cfg_, ue_cc_idx(ue_cc_idx_), last_tti(current_tti), harq_ent(SCHED_MAX_HARQ_PROC, SCHED_MAX_HARQ_PROC), - tpc_fsm(cell_cfg_.cfg.target_ul_sinr) + tpc_fsm(cell_cfg_.nof_prb(), cell_cfg_.cfg.target_ul_sinr) { dl_cqi_rx = false; dl_cqi = (ue_cc_idx == 0) ? cell_params->cfg.initial_dl_cqi : 0; diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index c571af1f4..d67277f5c 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -590,6 +590,7 @@ void rrc::config_mac() item.maxharq_msg3tx = cfg.sibs[1].sib2().rr_cfg_common.rach_cfg_common.max_harq_msg3_tx; item.enable_64qam = cfg.sibs[1].sib2().rr_cfg_common.pusch_cfg_common.pusch_cfg_basic.enable64_qam; item.initial_dl_cqi = cfg.cell_list[ccidx].initial_dl_cqi; + item.target_ul_sinr = cfg.cell_list[ccidx].target_ul_sinr_db; item.nrb_pucch = SRSLTE_MAX(cfg.sr_cfg.nof_prb, cfg.cqi_cfg.nof_prb); rrc_log->info("Allocating %d PRBs for PUCCH\n", item.nrb_pucch); diff --git a/srsenb/test/mac/sched_test_rand.cc b/srsenb/test/mac/sched_test_rand.cc index 2a3ea6575..81ecb29ac 100644 --- a/srsenb/test/mac/sched_test_rand.cc +++ b/srsenb/test/mac/sched_test_rand.cc @@ -352,6 +352,7 @@ sched_sim_events rand_sim_params(uint32_t nof_ttis) sched_sim_event_generator generator; sim_gen.sim_args.cell_cfg = {generate_default_cell_cfg(nof_prb)}; + sim_gen.sim_args.cell_cfg[0].target_ul_sinr = pick_random_uniform({10, 15, 20, -1}); sim_gen.sim_args.default_ue_sim_cfg.ue_cfg = generate_default_ue_cfg(); sim_gen.sim_args.default_ue_sim_cfg.periodic_cqi = true; sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.maxharq_tx = std::uniform_int_distribution<>{1, 5}(srsenb::get_rand_gen()); diff --git a/srsenb/test/mac/sched_test_utils.h b/srsenb/test/mac/sched_test_utils.h index 452452f7f..6cf656b42 100644 --- a/srsenb/test/mac/sched_test_utils.h +++ b/srsenb/test/mac/sched_test_utils.h @@ -48,6 +48,7 @@ inline srsenb::sched_interface::cell_cfg_t generate_default_cell_cfg(uint32_t no cell_cfg.prach_rar_window = 3; cell_cfg.maxharq_msg3tx = 3; cell_cfg.initial_dl_cqi = 5; + cell_cfg.target_ul_sinr = -1; return cell_cfg; }