diff options
Diffstat (limited to 'src/common/power_control.c')
-rw-r--r-- | src/common/power_control.c | 503 |
1 files changed, 358 insertions, 145 deletions
diff --git a/src/common/power_control.c b/src/common/power_control.c index 4f5d15e3..7f98a417 100644 --- a/src/common/power_control.c +++ b/src/common/power_control.c @@ -1,7 +1,7 @@ /* MS Power Control Loop L1 */ /* (C) 2014 by Holger Hans Peter Freyther - * (C) 2020-2021 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de> + * (C) 2020-2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de> * * All Rights Reserved @@ -14,7 +14,7 @@ * This program 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 General Public License for more details. + * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. @@ -36,6 +36,8 @@ /* We don't want to deal with floating point, so we scale up */ #define EWMA_SCALE_FACTOR 100 +/* EWMA_SCALE_FACTOR/2 = +50: Round to nearest value when downscaling, otherwise floor() is applied. */ +#define EWMA_ROUND_FACTOR (EWMA_SCALE_FACTOR / 2) /* Base Low-Pass Single-Pole IIR Filter (EWMA) formula: * @@ -84,49 +86,45 @@ static int do_pf_ewma(const struct gsm_power_ctrl_meas_params *mp, return Val; } - *Avg100 += A * (Val - *Avg100 / EWMA_SCALE_FACTOR); - return *Avg100 / EWMA_SCALE_FACTOR; + *Avg100 += A * (Val - (*Avg100 + EWMA_ROUND_FACTOR) / EWMA_SCALE_FACTOR); + return (*Avg100 + EWMA_ROUND_FACTOR) / EWMA_SCALE_FACTOR; } /* Calculate target RxLev value from lower/upper thresholds */ #define CALC_TARGET(mp) \ - (mp.lower_thresh + mp.upper_thresh) / 2 + ((mp).lower_thresh + (mp).upper_thresh) / 2 -/* Calculate a 'delta' value (for the given MS/BS power control state and parameters) - * to be applied to the current Tx power level to approach the target level. */ -static int calc_delta(const struct gsm_power_ctrl_params *params, - struct lchan_power_ctrl_state *state, - const int rxlev_dbm) +static int do_avg_algo(const struct gsm_power_ctrl_meas_params *mp, + struct gsm_power_ctrl_meas_proc_state *mps, + const int val) { - int rxlev_dbm_avg; - uint8_t rxlev_avg; - int delta; - - /* Filter RxLev value to reduce unnecessary Tx power oscillations */ - switch (params->rxlev_meas.algo) { + int val_avg; + switch (mp->algo) { case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA: - rxlev_dbm_avg = do_pf_ewma(¶ms->rxlev_meas, - &state->rxlev_meas_proc, - rxlev_dbm); + val_avg = do_pf_ewma(mp, mps, val); break; /* TODO: implement other pre-processing methods */ case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE: default: /* No filtering (pass through) */ - rxlev_dbm_avg = rxlev_dbm; + val_avg = val; } - - /* FIXME: avoid this conversion, accept RxLev as-is */ - rxlev_avg = dbm2rxlev(rxlev_dbm_avg); + return val_avg; +} +/* Calculate a 'delta' value (for the given MS/BS power control parameters) + * to be applied to the current Tx power level to approach the target level. */ +static int calc_delta_rxlev(const struct gsm_power_ctrl_params *params, const uint8_t rxlev) +{ + int delta; /* Check if RxLev is within the threshold window */ - if (rxlev_avg >= params->rxlev_meas.lower_thresh && - rxlev_avg <= params->rxlev_meas.upper_thresh) + if (rxlev >= params->rxlev_meas.lower_thresh && + rxlev <= params->rxlev_meas.upper_thresh) return 0; /* How many dBs measured power should be increased (+) or decreased (-) * to reach expected power. */ - delta = CALC_TARGET(params->rxlev_meas) - rxlev_avg; + delta = CALC_TARGET(params->rxlev_meas) - rxlev; /* Don't ever change more than PWR_{LOWER,RAISE}_MAX_DBM during one loop * iteration, i.e. reduce the speed at which the MS transmit power can @@ -139,14 +137,57 @@ static int calc_delta(const struct gsm_power_ctrl_params *params, return delta; } +/* Shall we skip current block based on configured interval? */ +static bool ctrl_interval_skip_block(const struct gsm_power_ctrl_params *params, + struct lchan_power_ctrl_state *state) +{ + /* Power control interval: how many blocks do we skip? */ + if (state->skip_block_num-- > 0) + return true; + + /* Reset the number of SACCH blocks to be skipped: + * ctrl_interval=0 => 0 blocks to skip, + * ctrl_interval=1 => 1 blocks to skip, + * ctrl_interval=2 => 3 blocks to skip, + * so basically ctrl_interval * 2 - 1. */ + state->skip_block_num = params->ctrl_interval * 2 - 1; + return false; +} + +static const struct gsm_power_ctrl_meas_params *lchan_get_ci_thresholds(const struct gsm_lchan *lchan) +{ + const struct gsm_power_ctrl_params *params = lchan->ms_power_ctrl.dpc_params; + + switch (lchan->type) { + case GSM_LCHAN_SDCCH: + return ¶ms->ci_sdcch_meas; + case GSM_LCHAN_PDTCH: + return ¶ms->ci_gprs_meas; + case GSM_LCHAN_TCH_F: + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) + return ¶ms->ci_amr_fr_meas; + else + return ¶ms->ci_fr_meas; + case GSM_LCHAN_TCH_H: + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) + return ¶ms->ci_amr_hr_meas; + else + return ¶ms->ci_hr_meas; + default: + OSMO_ASSERT(0); + } +} + /*! compute the new MS POWER LEVEL communicated to the MS and store it in lchan. * \param lchan logical channel for which to compute (and in which to store) new power value. * \param[in] ms_power_lvl MS Power Level received from Uplink L1 SACCH Header in SACCH block. * \param[in] ul_rssi_dbm Signal level of the received SACCH block, in dBm. + * \param[in] ul_lqual_cb C/I of the received SACCH block, in dB. */ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan, const uint8_t ms_power_lvl, - const int8_t ul_rssi_dbm) + const int8_t ul_rssi_dbm, + const int16_t ul_lqual_cb) { struct lchan_power_ctrl_state *state = &lchan->ms_power_ctrl; const struct gsm_power_ctrl_params *params = state->dpc_params; @@ -155,23 +196,20 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan, enum gsm_band band = bts->band; int8_t new_power_lvl; /* TS 05.05 power level */ int8_t ms_dbm, new_dbm, current_dbm, bsc_max_dbm; + uint8_t rxlev_avg; + int16_t ul_lqual_cb_avg; + const struct gsm_power_ctrl_meas_params *ci_meas; + bool ignore, ci_on; if (!trx_ms_pwr_ctrl_is_osmo(trx)) return 0; if (params == NULL) return 0; - /* Power control interval: how many blocks do we skip? */ - if (state->skip_block_num-- > 0) + /* Shall we skip current block based on configured interval? */ + if (ctrl_interval_skip_block(params, state)) return 0; - /* Reset the number of SACCH blocks to be skipped: - * ctrl_interval=0 => 0 blocks to skip, - * ctrl_interval=1 => 1 blocks to skip, - * ctrl_interval=2 => 3 blocks to skip, - * so basically ctrl_interval * 2 - 1. */ - state->skip_block_num = params->ctrl_interval * 2 - 1; - ms_dbm = ms_pwr_dbm(band, ms_power_lvl); if (ms_dbm < 0) { LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE, @@ -187,8 +225,24 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan, return 0; } - /* Calculate the new Tx power value (in dBm) */ - new_dbm = ms_dbm + calc_delta(params, state, ul_rssi_dbm); + ci_meas = lchan_get_ci_thresholds(lchan); + + /* Is C/I based algo enabled by config? + * FIXME: this can later be generalized when properly implementing P & N counting. */ + ci_on = ci_meas->lower_cmp_n && ci_meas->upper_cmp_n; + + ul_lqual_cb_avg = do_avg_algo(ci_meas, &state->ci_meas_proc, ul_lqual_cb); + rxlev_avg = do_avg_algo(¶ms->rxlev_meas, &state->rxlev_meas_proc, dbm2rxlev(ul_rssi_dbm)); + + /* If computed C/I is enabled and out of acceptable thresholds: */ + if (ci_on && ul_lqual_cb_avg < ci_meas->lower_thresh * 10) { + new_dbm = ms_dbm + params->inc_step_size_db; + } else if (ci_on && ul_lqual_cb_avg > ci_meas->upper_thresh * 10) { + new_dbm = ms_dbm - params->red_step_size_db; + } else { + /* Calculate the new Tx power value (in dBm) */ + new_dbm = ms_dbm + calc_delta_rxlev(params, rxlev_avg); + } /* Make sure new_dbm is never negative. ms_pwr_ctl_lvl() can later on cope with any unsigned dbm value, regardless of band minimal value. */ @@ -207,23 +261,46 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan, return 0; } - /* FIXME: this is only needed for logging, print thresholds instead */ - int target_dbm = rxlev2dbm(CALC_TARGET(params->rxlev_meas)); + current_dbm = ms_pwr_dbm(band, state->current); - if (state->current == new_power_lvl) { - LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping MS power at control level %d, %d dBm " - "(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", rx-current %d dBm, rx-target %d dBm)\n", - new_power_lvl, new_dbm, ms_power_lvl, state->max, - ul_rssi_dbm, target_dbm); + /* In this Power Control Loop, we infer a new good MS Power Level based + * on the previous MS Power Level announced by the MS (not the previous + * one we requested!) together with the related computed measurements. + * Hence, and since we allow for several good MS Power Levels falling into our + * thresholds, we could finally converge into an oscillation loop where + * the MS bounces between 2 different correct MS Power levels all the + * time, due to the fact that we "accept" and "request back" whatever + * good MS Power Level we received from the MS, but at that time the MS + * will be transmitting using the previous MS Power Level we + * requested, which we will later "accept" and "request back" on next loop + * iteration. As a result MS effectively bounces between those 2 MS + * Power Levels. + * In order to fix this permanent oscillation, if current MS_PWR used/announced + * by MS is good ("ms_dbm == new_dbm", hence within thresholds and no change + * required) but has higher Tx power than the one we last requested, we ignore + * it and keep requesting for one with lower Tx power. This way we converge to + * the lowest good Tx power avoiding oscillating over values within thresholds. + */ + ignore = (ms_dbm == new_dbm && ms_dbm > current_dbm); + + if (state->current == new_power_lvl || ignore) { + LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping MS power at control level %d (%d dBm): " + "ms-pwr-lvl[curr %" PRIu8 ", max %" PRIu8 "], RSSI[curr %d, avg %d, thresh %d..%d] dBm," + " C/I[curr %d, avg %d, thresh %d..%d] dB\n", + new_power_lvl, new_dbm, ms_power_lvl, state->max, ul_rssi_dbm, rxlev2dbm(rxlev_avg), + rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh), + ul_lqual_cb/10, ul_lqual_cb_avg/10, ci_meas->lower_thresh, ci_meas->upper_thresh); return 0; } - current_dbm = ms_pwr_dbm(band, state->current); - LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s MS power from control level %d (%d dBm) to %d, %d dBm " - "(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", rx-current %d dBm, rx-target %d dBm)\n", + LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s MS power control level %d (%d dBm) => %d (%d dBm): " + "ms-pwr-lvl[curr %" PRIu8 ", max %" PRIu8 "], RSSI[curr %d, avg %d, thresh %d..%d] dBm," + " C/I[curr %d, avg %d, thresh %d..%d] dB\n", (new_dbm > current_dbm) ? "Raising" : "Lowering", - state->current, current_dbm, new_power_lvl, new_dbm, - ms_power_lvl, state->max, ul_rssi_dbm, target_dbm); + state->current, current_dbm, new_power_lvl, new_dbm, ms_power_lvl, + state->max, ul_rssi_dbm, rxlev2dbm(rxlev_avg), + rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh), + ul_lqual_cb/10, ul_lqual_cb_avg/10, ci_meas->lower_thresh, ci_meas->upper_thresh); /* store the resulting new MS power level in the lchan */ state->current = new_power_lvl; @@ -234,129 +311,265 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan, /*! compute the new Downlink attenuation value for the given logical channel. * \param lchan logical channel for which to compute (and in which to store) new power value. - * \param[in] gh pointer to the beginning of (presumably) a Measurement Report. + * \param[in] mr pointer to a *valid* Measurement Report. */ int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan, - const struct gsm48_hdr *gh) + const struct gsm48_meas_res *mr) { struct lchan_power_ctrl_state *state = &lchan->bs_power_ctrl; const struct gsm_power_ctrl_params *params = state->dpc_params; - uint8_t rxqual_full, rxqual_sub; - uint8_t rxlev_full, rxlev_sub; - uint8_t rxqual, rxlev; - int delta, new; + uint8_t rxqual, rxqual_avg, rxlev, rxlev_avg; + int new_att; /* Check if dynamic BS Power Control is enabled */ if (params == NULL) return 0; - /* Check if this is a Measurement Report */ - if (gh->proto_discr != GSM48_PDISC_RR) - return 0; - if (gh->msg_type != GSM48_MT_RR_MEAS_REP) - return 0; - - /* Check if the measurement results are valid */ - if ((gh->data[1] & 0x40) == 0x40) { - LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, - "The measurement results are not valid\n"); - return 0; - } - - /* See 3GPP TS 44.018, section 10.5.2.20 */ - rxqual_full = (gh->data[2] >> 4) & 0x7; - rxqual_sub = (gh->data[2] >> 1) & 0x7; - - rxlev_full = gh->data[0] & 0x3f; - rxlev_sub = gh->data[1] & 0x3f; LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Rx DL Measurement Report: " "RXLEV-FULL(%02u), RXQUAL-FULL(%u), " "RXLEV-SUB(%02u), RXQUAL-SUB(%u), " "DTx is %s => using %s\n", - rxlev_full, rxqual_full, rxlev_sub, rxqual_sub, + mr->rxlev_full, mr->rxqual_full, + mr->rxlev_sub, mr->rxqual_sub, lchan->tch.dtx.dl_active ? "enabled" : "disabled", lchan->tch.dtx.dl_active ? "SUB" : "FULL"); - /* Power control interval: how many blocks do we skip? */ - if (state->skip_block_num-- > 0) + /* Shall we skip current block based on configured interval? */ + if (ctrl_interval_skip_block(params, state)) return 0; - /* Reset the number of SACCH blocks to be skipped: - * ctrl_interval=0 => 0 blocks to skip, - * ctrl_interval=1 => 1 blocks to skip, - * ctrl_interval=2 => 3 blocks to skip, - * so basically ctrl_interval * 2 - 1. */ - state->skip_block_num = params->ctrl_interval * 2 - 1; - /* If DTx is active on Downlink, use the '-SUB' */ if (lchan->tch.dtx.dl_active) { - rxqual = rxqual_sub; - rxlev = rxlev_sub; + rxqual = mr->rxqual_sub; + rxlev = mr->rxlev_sub; } else { /* ... otherwise use the '-FULL' */ - rxqual = rxqual_full; - rxlev = rxlev_full; + rxqual = mr->rxqual_full; + rxlev = mr->rxlev_full; } + rxlev_avg = do_avg_algo(¶ms->rxlev_meas, &state->rxlev_meas_proc, rxlev); + rxqual_avg = do_avg_algo(¶ms->rxqual_meas, &state->rxqual_meas_proc, rxqual); /* If RxQual > L_RXQUAL_XX_P, try to increase Tx power */ - if (rxqual > params->rxqual_meas.lower_thresh) { - uint8_t old = state->current; - - /* Tx power has reached the maximum, nothing to do */ - if (state->current == 0) - return 0; - + if (rxqual_avg > params->rxqual_meas.lower_thresh) { /* Increase Tx power by reducing Tx attenuation */ - if (state->current >= params->inc_step_size_db) - state->current -= params->inc_step_size_db; - else - state->current = 0; - - LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Reducing Downlink attenuation: " - "%u -> %d dB due to RxQual %u worse than L_RXQUAL_XX_P %u\n", - old, state->current, rxqual, params->rxqual_meas.lower_thresh); - return 1; + new_att = state->current - params->inc_step_size_db; + } else if (rxqual_avg < params->rxqual_meas.upper_thresh) { + /* Increase Tx power by Increasing Tx attenuation */ + new_att = state->current + params->red_step_size_db; + } else { + /* Basic signal transmission / reception formula: + * + * RxLev = TxPwr - (PathLoss + TxAtt) + * + * Here we want to change RxLev at the MS side, so: + * + * RxLev + Delta = TxPwr - (PathLoss + TxAtt) + Delta + * + * The only parameter we can change here is TxAtt, so: + * + * RxLev + Delta = TxPwr - PathLoss - TxAtt + Delta + * RxLev + Delta = TxPwr - PathLoss - (TxAtt - Delta) + */ + new_att = state->current - calc_delta_rxlev(params, rxlev_avg); } - /* Calculate a 'delta' for the current attenuation level */ - delta = calc_delta(params, state, rxlev2dbm(rxlev)); - - /* Basic signal transmission / reception formula: - * - * RxLev = TxPwr - (PathLoss + TxAtt) - * - * Here we want to change RxLev at the MS side, so: - * - * RxLev + Delta = TxPwr - (PathLoss + TxAtt) + Delta - * - * The only parameter we can change here is TxAtt, so: - * - * RxLev + Delta = TxPwr - PathLoss - TxAtt + Delta - * RxLev + Delta = TxPwr - PathLoss - (TxAtt - Delta) - */ - new = state->current - delta; - if (new > state->max) - new = state->max; - if (new < 0) - new = 0; - - if (state->current != new) { - LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Changing Downlink attenuation: " - "%u -> %u dB (maximum %u dB, suggested delta %d dB, " - "RxLev current %u (%d dBm), thresholds %u .. %u)\n", - state->current, new, state->max, - -delta, rxlev, rxlev2dbm(rxlev), - params->rxlev_meas.lower_thresh, - params->rxlev_meas.upper_thresh); - state->current = new; - return 1; - } else { - LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping Downlink attenuation " - "at %u dB (maximum %u dB, suggested delta %d dB, " - "RxLev current %u (%d dBm), thresholds %u .. %u)\n", - state->current, state->max, - -delta, rxlev, rxlev2dbm(rxlev), - params->rxlev_meas.lower_thresh, - params->rxlev_meas.upper_thresh); + /* Make sure new TxAtt is never negative: */ + if (new_att < 0) + new_att = 0; + + /* Don't ask for higher TxAtt than permitted: */ + if (new_att > state->max) + new_att = state->max; + + if (state->current == new_att) { + LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping DL attenuation at %u dB: " + "max %u dB, RSSI[curr %d, avg %d, thresh %d..%d] dBm, " + "RxQual[curr %d, avg %d, thresh %d..%d]\n", + state->current, state->max, rxlev2dbm(rxlev), rxlev2dbm(rxlev_avg), + rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh), + rxqual, rxqual_avg, params->rxqual_meas.lower_thresh, params->rxqual_meas.upper_thresh); return 0; } + + LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s DL attenuation %u dB => %u dB:" + "max %u dB, RSSI[curr %d, avg %d, thresh %d..%d] dBm, " + "RxQual[curr %d, avg %d, thresh %d..%d]\n", + (new_att > state->current) ? "Raising" : "Lowering", + state->current, new_att, state->max, rxlev2dbm(rxlev), rxlev2dbm(rxlev_avg), + rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh), + rxqual, rxqual_avg, params->rxqual_meas.lower_thresh, params->rxqual_meas.upper_thresh); + state->current = new_att; + return 1; +} + +/* Default MS/BS Power Control parameters (see 3GPP TS 45.008, table A.1) */ +const struct gsm_power_ctrl_params power_ctrl_params_def = { + /* Power increasing/reducing step size (optimal defaults) */ + .inc_step_size_db = 4, /* quickly increase MS/BS power */ + .red_step_size_db = 2, /* slowly decrease MS/BS power */ + + /* RxLev measurement parameters */ + .rxlev_meas = { + /* Thresholds for RxLev (see 3GPP TS 45.008, A.3.2.1) */ + .lower_thresh = 32, /* L_RXLEV_XX_P (-78 dBm) */ + .upper_thresh = 38, /* U_RXLEV_XX_P (-72 dBm) */ + + /* NOTE: only Osmocom specific EWMA is supported */ + .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA, + .ewma.alpha = 50, /* Smoothing factor 50% */ + }, + + /* RxQual measurement parameters */ + .rxqual_meas = { + /* Thresholds for RxQual (see 3GPP TS 45.008, A.3.2.1) */ + .lower_thresh = 3, /* L_RXQUAL_XX_P (0.8% <= BER < 1.6%) */ + .upper_thresh = 0, /* U_RXQUAL_XX_P (BER < 0.2%) */ + + /* No averaging (filtering) by default. + * NOTE: only Osmocom specific EWMA is supported */ + .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE, + }, + + /* C/I measurement parameters. + * Target C/I retrieved from "GSM/EDGE: Evolution and Performance" Table 10.3. + * Set lower and upper so that (lower + upper) / 2 is equal or slightly + * above the target. + */ + .ci_fr_meas = { /* FR: Target C/I = 15 dB, Soft blocking threshold = 10 dB */ + .lower_thresh = 13, + .upper_thresh = 17, + + /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages + * out of LOWER_CMP_N averages are lower than L_CI_FR_XX_P */ + .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages + * out of UPPER_CMP_N averages are greater than L_CI_FR_XX_P */ + .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + + /* No averaging (filtering) by default */ + .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE, + + /* Hreqave: the period over which an average is produced */ + .h_reqave = 4, /* TODO: investigate a reasonable default value */ + /* Hreqt: the number of averaged results maintained */ + .h_reqt = 6, /* TODO: investigate a reasonable default value */ + }, + .ci_hr_meas = { /* HR: Target C/I = 18 dB, Soft blocking threshold = 13 dB */ + .lower_thresh = 16, + .upper_thresh = 21, + + /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages + * out of LOWER_CMP_N averages are lower than L_CI_HR_XX_P */ + .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages + * out of UPPER_CMP_N averages are greater than L_CI_HR_XX_P */ + .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + + /* No averaging (filtering) by default */ + .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE, + + /* Hreqave: the period over which an average is produced */ + .h_reqave = 4, /* TODO: investigate a reasonable default value */ + /* Hreqt: the number of averaged results maintained */ + .h_reqt = 6, /* TODO: investigate a reasonable default value */ + }, + .ci_amr_fr_meas = { /* AMR-FR: Target C/I = 9 dB, Soft blocking threshold = 4 dB */ + .lower_thresh = 7, + .upper_thresh = 11, + + /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages + * out of LOWER_CMP_N averages are lower than L_CI_AMR_FR_XX_P */ + .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages + * out of UPPER_CMP_N averages are greater than L_CI_AMR_FR_XX_P */ + .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + + /* No averaging (filtering) by default */ + .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE, + + /* Hreqave: the period over which an average is produced */ + .h_reqave = 4, /* TODO: investigate a reasonable default value */ + /* Hreqt: the number of averaged results maintained */ + .h_reqt = 6, /* TODO: investigate a reasonable default value */ + }, + .ci_amr_hr_meas = { /* AMR-HR: Target C/I = 15 dB, Soft blocking threshold = 10 dB */ + .lower_thresh = 13, + .upper_thresh = 17, + + /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages + * out of LOWER_CMP_N averages are lower than L_CI_AMR_HR_XX_P */ + .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages + * out of UPPER_CMP_N averages are greater than L_CI_AMR_HR_XX_P */ + .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + + /* No averaging (filtering) by default */ + .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE, + + /* Hreqave: the period over which an average is produced */ + .h_reqave = 4, /* TODO: investigate a reasonable default value */ + /* Hreqt: the number of averaged results maintained */ + .h_reqt = 6, /* TODO: investigate a reasonable default value */ + }, + .ci_sdcch_meas = { /* SDCCH: Target C/I = 14 dB, Soft blocking threshold = 9 dB */ + .lower_thresh = 12, + .upper_thresh = 16, + + /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages + * out of LOWER_CMP_N averages are lower than L_CI_SDCCH_XX_P */ + .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages + * out of UPPER_CMP_N averages are greater than L_CI_SDCCH_XX_P */ + .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + + /* No averaging (filtering) by default */ + .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE, + + /* Hreqave: the period over which an average is produced */ + .h_reqave = 4, /* TODO: investigate a reasonable default value */ + /* Hreqt: the number of averaged results maintained */ + .h_reqt = 6, /* TODO: investigate a reasonable default value */ + }, + .ci_gprs_meas = { /* GPRS: Target C/I = 20 dB, Soft blocking threshold = 15 dB */ + .lower_thresh = 18, + .upper_thresh = 24, + + /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages + * out of LOWER_CMP_N averages are lower than L_CI_GPRS_XX_P */ + .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */ + /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages + * out of UPPER_CMP_N averages are greater than L_CI_GPRS_XX_P */ + .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */ + + /* No averaging (filtering) by default */ + .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE, + + /* Hreqave: the period over which an average is produced */ + .h_reqave = 4, /* TODO: investigate a reasonable default value */ + /* Hreqt: the number of averaged results maintained */ + .h_reqt = 6, /* TODO: investigate a reasonable default value */ + }, +}; + +void power_ctrl_params_def_reset(struct gsm_power_ctrl_params *params, bool is_bs_pwr) +{ + *params = power_ctrl_params_def; + + /* Trigger loop every N-th SACCH block. See 3GPP TS 45.008 section 4.7.1. */ + if (!is_bs_pwr) + params->ctrl_interval = 2; /* N=4 (1.92s) */ + else + params->ctrl_interval = 1; /* N=2 (0.960) */ } |