aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVadim Yanitskiy <vyanitskiy@sysmocom.de>2021-01-05 00:44:40 +0100
committerVadim Yanitskiy <vyanitskiy@sysmocom.de>2021-01-08 02:05:27 +0100
commite8f857453df56a40be61b17aafc509047e141174 (patch)
tree334ceee99829b6af573d3750df195738b55d250d
parent4a0a6fd014e1ce1886fb88e7e37d67ca252b63b2 (diff)
power_control: migrate MS/BS control loops to the new params
In change [1] the new power control structures and default params were introduced. In change [2], the existing VTY commands for MS power control in the BTS were deprecated and changed to use the new structures as storage. Finally, in change [3], handling of the power control parameters on the A-bis/RSL was implemented. This change is the final logical step in the mentioned chain: it makes both MS/BS power control loops use the new parameters, and removes the old structures. The actual implementation of both power control loops remains the same, however the expected output of some unit tests for the Downlink loop needs to be changed: - TC_fixed_mode: disabling dynamic power control becomes a separate step of the test script since the field 'fixed' is removed; - TC_rxlev_target: RxLev thresholds are printed 'as-is'. Not all of the new parameters are used by the power control loops yet. Further improvements to be done in the follow up commits. [1] I6d41eb238aa6d4f5b77596c5477c2ecbe86de2a8 [2] Icbd9a7d31ce6723294130a31a179a002fccb4612 [3] I5a901eca5a78a0335a6954064e602e65cda85390 Change-Id: Ib18f84c40227841d95a36063a6789bf63054fc2e Related: SYS#4918
-rw-r--r--include/osmo-bts/bts.h4
-rw-r--r--include/osmo-bts/gsm_data.h1
-rw-r--r--src/common/bts.c16
-rw-r--r--src/common/power_control.c78
-rw-r--r--src/common/rsl.c9
-rw-r--r--src/common/vty.c12
-rw-r--r--src/osmo-bts-lc15/oml.c11
-rw-r--r--src/osmo-bts-oc2g/oml.c11
-rw-r--r--src/osmo-bts-sysmo/oml.c11
-rw-r--r--src/osmo-bts-sysmo/sysmobts_vty.c15
-rw-r--r--src/osmo-bts-trx/trx_vty.c4
-rw-r--r--tests/power/bs_power_loop_test.c76
-rw-r--r--tests/power/bs_power_loop_test.ok27
-rw-r--r--tests/power/ms_power_loop_test.c61
14 files changed, 182 insertions, 154 deletions
diff --git a/include/osmo-bts/bts.h b/include/osmo-bts/bts.h
index 0b224beb..f6389ade 100644
--- a/include/osmo-bts/bts.h
+++ b/include/osmo-bts/bts.h
@@ -321,10 +321,6 @@ struct gsm_bts {
bool vty_override; /* OML value overridden by VTY */
} radio_link_timeout;
- /* Uplink/Downlink power control (legacy parameters) */
- struct bts_power_ctrl_params ul_power_ctrl;
- struct bts_power_ctrl_params dl_power_ctrl;
-
/* Default (fall-back) Dynamic Power Control parameters for all transceivers */
struct gsm_power_ctrl_params bs_dpc_params; /* BS Dynamic Power Control */
struct gsm_power_ctrl_params ms_dpc_params; /* MS Dynamic Power Control */
diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h
index b9e0e887..6efc7176 100644
--- a/include/osmo-bts/gsm_data.h
+++ b/include/osmo-bts/gsm_data.h
@@ -222,7 +222,6 @@ struct lchan_power_ctrl_state {
* (attenuation, in dB). */
uint8_t current;
uint8_t max;
- bool fixed;
/* Scaled up (100 times) average UL/DL RxLev (in dBm) */
int avg100_rxlev_dbm;
diff --git a/src/common/bts.c b/src/common/bts.c
index de079577..06a5ccb2 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -330,22 +330,6 @@ int bts_init(struct gsm_bts *bts)
bts->rtp_port_range_next = bts->rtp_port_range_start;
bts->rtp_ip_dscp = -1;
- /* Default UL/DL power control parameters (legacy) */
- bts->ul_power_ctrl = bts->dl_power_ctrl = \
- (struct bts_power_ctrl_params) {
- .target_dbm = -75,
- .hysteresis_db = 3, /* -78 .. -72 dBm */
- .raise_step_max_db = PWR_RAISE_MAX_DB,
- .lower_step_max_db = PWR_LOWER_MAX_DB,
- .pf_algo = BTS_PF_ALGO_EWMA,
- .pf = {
- .ewma = {
- /* 50% smoothing */
- .alpha = 50
- }
- }
- };
-
/* Default (fall-back) MS/BS Power control parameters */
bts->bs_dpc_params = power_ctrl_params_def;
bts->ms_dpc_params = power_ctrl_params_def;
diff --git a/src/common/power_control.c b/src/common/power_control.c
index a616f185..e159740e 100644
--- a/src/common/power_control.c
+++ b/src/common/power_control.c
@@ -70,11 +70,11 @@
* https://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
* https://tomroelandts.com/articles/low-pass-single-pole-iir-filter
*/
-static int8_t do_pf_ewma(const struct bts_power_ctrl_params *params,
+static int8_t do_pf_ewma(const struct gsm_power_ctrl_meas_params *mp,
struct lchan_power_ctrl_state *state,
const int8_t Pwr)
{
- const uint8_t A = params->pf.ewma.alpha;
+ const uint8_t A = mp->ewma.alpha;
int *Avg100 = &state->avg100_rxlev_dbm;
/* We don't have 'Avg[n - 1]' if this is the first run */
@@ -87,41 +87,51 @@ static int8_t do_pf_ewma(const struct bts_power_ctrl_params *params,
return *Avg100 / EWMA_SCALE_FACTOR;
}
+/* Calculate target RxLev value from lower/upper thresholds */
+#define CALC_TARGET(mp) \
+ (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 bts_power_ctrl_params *params,
+static int calc_delta(const struct gsm_power_ctrl_params *params,
struct lchan_power_ctrl_state *state,
const int rxlev_dbm)
{
int rxlev_dbm_avg;
+ uint8_t rxlev_avg;
int delta;
- /* Filter input value(s) to reduce unnecessary Tx power oscillations */
- switch (params->pf_algo) {
- case BTS_PF_ALGO_EWMA:
- rxlev_dbm_avg = do_pf_ewma(params, state, rxlev_dbm);
+ /* Filter RxLev value to reduce unnecessary Tx power oscillations */
+ switch (params->rxlev_meas.algo) {
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA:
+ rxlev_dbm_avg = do_pf_ewma(&params->rxlev_meas, state, rxlev_dbm);
break;
- case BTS_PF_ALGO_NONE:
+ /* TODO: implement other pre-processing methods */
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE:
default:
/* No filtering (pass through) */
rxlev_dbm_avg = rxlev_dbm;
}
- /* How many dBs measured power should be increased (+) or decreased (-)
- * to reach expected power. */
- delta = params->target_dbm - rxlev_dbm_avg;
+ /* FIXME: avoid this conversion, accept RxLev as-is */
+ rxlev_avg = dbm2rxlev(rxlev_dbm_avg);
- /* Tolerate small deviations from 'rx-target' */
- if (abs(delta) <= params->hysteresis_db)
+ /* Check if RxLev is within the threshold window */
+ if (rxlev_avg >= params->rxlev_meas.lower_thresh &&
+ rxlev_avg <= 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;
+
/* 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
* change. A higher value means a lower level (and vice versa) */
- if (delta > params->raise_step_max_db)
- delta = params->raise_step_max_db;
- else if (delta < -params->lower_step_max_db)
- delta = -params->lower_step_max_db;
+ if (delta > params->inc_step_size_db)
+ delta = params->inc_step_size_db;
+ else if (delta < -params->red_step_size_db)
+ delta = -params->red_step_size_db;
return delta;
}
@@ -135,18 +145,17 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
const uint8_t ms_power_lvl,
const int8_t ul_rssi_dbm)
{
+ struct lchan_power_ctrl_state *state = &lchan->ms_power_ctrl;
+ const struct gsm_power_ctrl_params *params = state->dpc_params;
struct gsm_bts_trx *trx = lchan->ts->trx;
struct gsm_bts *bts = trx->bts;
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;
- const struct bts_power_ctrl_params *params = &bts->ul_power_ctrl;
- struct lchan_power_ctrl_state *state = &lchan->ms_power_ctrl;
-
if (!trx_ms_pwr_ctrl_is_osmo(trx))
return 0;
- if (state->fixed)
+ if (params == NULL)
return 0;
ms_dbm = ms_pwr_dbm(band, ms_power_lvl);
@@ -184,11 +193,14 @@ 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));
+
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, params->target_dbm);
+ ul_rssi_dbm, target_dbm);
return 0;
}
@@ -197,7 +209,7 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
"(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", rx-current %d dBm, rx-target %d dBm)\n",
(new_dbm > current_dbm) ? "Raising" : "Lowering",
state->current, current_dbm, new_power_lvl, new_dbm,
- ms_power_lvl, state->max, ul_rssi_dbm, params->target_dbm);
+ ms_power_lvl, state->max, ul_rssi_dbm, target_dbm);
/* store the resulting new MS power level in the lchan */
state->current = new_power_lvl;
@@ -213,18 +225,15 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
const struct gsm48_hdr *gh)
{
- struct gsm_bts_trx *trx = lchan->ts->trx;
- struct gsm_bts *bts = trx->bts;
+ 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;
- const struct bts_power_ctrl_params *params = &bts->dl_power_ctrl;
- struct lchan_power_ctrl_state *state = &lchan->bs_power_ctrl;
-
- /* Check if BS Power Control is enabled */
- if (state->fixed)
+ /* 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)
@@ -264,7 +273,7 @@ int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
}
/* Bit Error Rate > 0 => reduce by 2 */
- if (rxqual > 0) {
+ if (rxqual > 0) { /* FIXME: take RxQual threshold into account */
LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Reducing Downlink attenuation "
"by half: %u -> %u dB due to RXQUAL %u > 0\n",
state->current, state->current / 2, rxqual);
@@ -294,16 +303,19 @@ int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
if (new < 0)
new = 0;
+ /* FIXME: this is only needed for logging, print thresholds instead */
+ int target_dbm = rxlev2dbm(CALC_TARGET(params->rxlev_meas));
+
if (state->current != new) {
LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Changing Downlink attenuation: "
"%u -> %u dB (maximum %u dB, target %d dBm, delta %d dB)\n",
- state->current, new, state->max, params->target_dbm, delta);
+ state->current, new, state->max, target_dbm, delta);
state->current = new;
return 1;
} else {
LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping Downlink attenuation "
"at %u dB (maximum %u dB, target %d dBm, delta %d dB)\n",
- state->current, state->max, params->target_dbm, delta);
+ state->current, state->max, target_dbm, delta);
return 0;
}
}
diff --git a/src/common/rsl.c b/src/common/rsl.c
index bca365e3..b4df22cd 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -1322,14 +1322,12 @@ static int rsl_rx_chan_activ(struct msgb *msg)
lchan->ms_power_ctrl = (struct lchan_power_ctrl_state) {
.max = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0),
.current = lchan->ms_power_ctrl.max,
- .fixed = true,
};
/* Initialize BS Power Control defaults */
lchan->bs_power_ctrl = (struct lchan_power_ctrl_state) {
.max = 2 * 15, /* maximum defined in 9.3.4 */
.current = 0,
- .fixed = true,
};
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
@@ -1414,7 +1412,6 @@ static int rsl_rx_chan_activ(struct msgb *msg)
/* Spec explicitly states BTS should only perform
* autonomous MS power control loop in BTS if 'MS Power
* Parameters' IE is present! */
- lchan->ms_power_ctrl.fixed = false;
lchan->ms_power_ctrl.dpc_params = params;
}
@@ -1433,7 +1430,6 @@ static int rsl_rx_chan_activ(struct msgb *msg)
/* NOTE: it's safer to start from 0 */
lchan->bs_power_ctrl.current = 0;
- lchan->bs_power_ctrl.fixed = false;
lchan->bs_power_ctrl.dpc_params = params;
}
@@ -1925,7 +1921,6 @@ static int rsl_rx_ms_pwr_ctrl(struct msgb *msg)
/* Spec explicitly states BTS should only perform autonomous MS Power
* control loop in BTS if 'MS Power Parameters' IE is present! */
- lchan->ms_power_ctrl.fixed = !TLVP_PRESENT(&tp, RSL_IE_MS_POWER_PARAM);
lchan->ms_power_ctrl.dpc_params = NULL;
/* 9.3.31 (TLV) MS Power Parameters IE (vendor specific) */
@@ -1946,7 +1941,7 @@ static int rsl_rx_ms_pwr_ctrl(struct msgb *msg)
/* Only set current to max if actual value of current
in dBm > value in dBm from max, or if fixed. */
- if (lchan->ms_power_ctrl.fixed) {
+ if (lchan->ms_power_ctrl.dpc_params == NULL) {
lchan->ms_power_ctrl.current = lchan->ms_power_ctrl.max;
} else {
max_pwr = ms_pwr_dbm(bts->band, lchan->ms_power_ctrl.max);
@@ -2005,12 +2000,10 @@ static int rsl_rx_bs_pwr_ctrl(struct msgb *msg)
/* NOTE: it's safer to start from 0 */
lchan->bs_power_ctrl.current = 0;
lchan->bs_power_ctrl.max = new;
- lchan->bs_power_ctrl.fixed = false;
lchan->bs_power_ctrl.dpc_params = params;
} else {
lchan->bs_power_ctrl.dpc_params = NULL;
lchan->bs_power_ctrl.current = new;
- lchan->bs_power_ctrl.fixed = true;
}
if (lchan->bs_power_ctrl.current != old) {
diff --git a/src/common/vty.c b/src/common/vty.c
index 1c3b4967..689ae58c 100644
--- a/src/common/vty.c
+++ b/src/common/vty.c
@@ -1489,13 +1489,12 @@ static void lchan_bs_power_ctrl_state_dump(struct vty *vty, unsigned int indent,
const struct lchan_power_ctrl_state *st = &lchan->bs_power_ctrl;
const struct gsm_bts_trx *trx = lchan->ts->trx;
- cfg_out(vty, "BS (Downlink) Power Control (%s):%s",
- st->fixed ? "fixed" : "autonomous",
- VTY_NEWLINE);
+ cfg_out(vty, "BS (Downlink) Power Control (%s mode):%s",
+ st->dpc_params ? "dynamic" : "static", VTY_NEWLINE);
indent += 2;
cfg_out(vty, "Channel reduction: %u dB", st->current);
- if (!st->fixed)
+ if (st->dpc_params != NULL)
vty_out(vty, " (max %u dB)", st->max);
vty_out(vty, "%s", VTY_NEWLINE);
@@ -1520,8 +1519,7 @@ static void lchan_ms_power_ctrl_state_dump(struct vty *vty, unsigned int indent,
const struct gsm_bts_trx *trx = lchan->ts->trx;
cfg_out(vty, "MS (Uplink) Power Control (%s):%s",
- st->fixed ? "fixed" : "autonomous",
- VTY_NEWLINE);
+ st->dpc_params ? "dynamic" : "static", VTY_NEWLINE);
indent += 2;
int current_dbm = ms_pwr_dbm(trx->bts->band, st->current);
@@ -1529,7 +1527,7 @@ static void lchan_ms_power_ctrl_state_dump(struct vty *vty, unsigned int indent,
cfg_out(vty, "Current power level: %u, -%d dBm",
st->current, current_dbm);
- if (!st->fixed)
+ if (st->dpc_params != NULL)
vty_out(vty, " (max %u, -%d dBm)", st->max, max_dbm);
vty_out(vty, "%s", VTY_NEWLINE);
diff --git a/src/osmo-bts-lc15/oml.c b/src/osmo-bts-lc15/oml.c
index 4c717901..44139cf4 100644
--- a/src/osmo-bts-lc15/oml.c
+++ b/src/osmo-bts-lc15/oml.c
@@ -433,8 +433,15 @@ static int trx_init(struct gsm_bts_trx *trx)
dev_par->u16Arfcn = trx->arfcn;
dev_par->u16BcchArfcn = trx->bts->c0->arfcn;
dev_par->u8NbTsc = trx->bts->bsic & 7;
- dev_par->fRxPowerLevel = trx_ms_pwr_ctrl_is_osmo(trx)
- ? 0.0 : trx->bts->ul_power_ctrl.target_dbm;
+
+ if (!trx_ms_pwr_ctrl_is_osmo(trx)) {
+ /* Target is in the middle between lower and upper RxLev thresholds */
+ int lower_dbm = rxlev2dbm(trx->ms_dpc_params->rxlev_meas.lower_thresh);
+ int upper_dbm = rxlev2dbm(trx->ms_dpc_params->rxlev_meas.upper_thresh);
+ dev_par->fRxPowerLevel = (float) (lower_dbm + upper_dbm) / 2;
+ } else {
+ dev_par->fRxPowerLevel = 0.0;
+ }
dev_par->fTxPowerLevel = 0.0;
LOGP(DL1C, LOGL_NOTICE, "Init TRX (Band %d, ARFCN %u, TSC %u, RxPower % 2f dBm, "
diff --git a/src/osmo-bts-oc2g/oml.c b/src/osmo-bts-oc2g/oml.c
index b62e61c1..ced6ad1b 100644
--- a/src/osmo-bts-oc2g/oml.c
+++ b/src/osmo-bts-oc2g/oml.c
@@ -448,8 +448,15 @@ static int trx_init(struct gsm_bts_trx *trx)
dev_par->u16Arfcn = trx->arfcn;
dev_par->u16BcchArfcn = trx->bts->c0->arfcn;
dev_par->u8NbTsc = trx->bts->bsic & 7;
- dev_par->fRxPowerLevel = trx_ms_pwr_ctrl_is_osmo(trx)
- ? 0.0 : trx->bts->ul_power_ctrl.target_dbm;
+
+ if (!trx_ms_pwr_ctrl_is_osmo(trx)) {
+ /* Target is in the middle between lower and upper RxLev thresholds */
+ int lower_dbm = rxlev2dbm(trx->ms_dpc_params->rxlev_meas.lower_thresh);
+ int upper_dbm = rxlev2dbm(trx->ms_dpc_params->rxlev_meas.upper_thresh);
+ dev_par->fRxPowerLevel = (float) (lower_dbm + upper_dbm) / 2;
+ } else {
+ dev_par->fRxPowerLevel = 0.0;
+ }
dev_par->fTxPowerLevel = 0.0;
LOGP(DL1C, LOGL_NOTICE, "Init TRX (Band %d, ARFCN %u, TSC %u, RxPower % 2f dBm, "
diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c
index b79f7317..8e38c60f 100644
--- a/src/osmo-bts-sysmo/oml.c
+++ b/src/osmo-bts-sysmo/oml.c
@@ -428,8 +428,15 @@ static int trx_init(struct gsm_bts_trx *trx)
dev_par->u16Arfcn = trx->arfcn;
dev_par->u16BcchArfcn = trx->bts->c0->arfcn;
dev_par->u8NbTsc = trx->bts->bsic & 7;
- dev_par->fRxPowerLevel = trx_ms_pwr_ctrl_is_osmo(trx)
- ? 0.0 : trx->bts->ul_power_ctrl.target_dbm;
+
+ if (!trx_ms_pwr_ctrl_is_osmo(trx)) {
+ /* Target is in the middle between lower and upper RxLev thresholds */
+ int lower_dbm = rxlev2dbm(trx->ms_dpc_params->rxlev_meas.lower_thresh);
+ int upper_dbm = rxlev2dbm(trx->ms_dpc_params->rxlev_meas.upper_thresh);
+ dev_par->fRxPowerLevel = (float) (lower_dbm + upper_dbm) / 2;
+ } else {
+ dev_par->fRxPowerLevel = 0.0;
+ }
dev_par->fTxPowerLevel = ((float) initial_mdBm) / 1000;
LOGP(DL1C, LOGL_NOTICE, "Init TRX (ARFCN %u, TSC %u, RxPower % 2f dBm, "
diff --git a/src/osmo-bts-sysmo/sysmobts_vty.c b/src/osmo-bts-sysmo/sysmobts_vty.c
index 65edbf0f..a8e7401f 100644
--- a/src/osmo-bts-sysmo/sysmobts_vty.c
+++ b/src/osmo-bts-sysmo/sysmobts_vty.c
@@ -152,9 +152,20 @@ DEFUN_DEPRECATED(cfg_trx_ul_power_target, cfg_trx_ul_power_target_cmd,
"Obsolete alias for bts uplink-power-target\n"
"Target uplink Rx level in dBm\n")
{
+ struct gsm_power_ctrl_meas_params *mp;
struct gsm_bts_trx *trx = vty->index;
-
- trx->bts->ul_power_ctrl.target_dbm = atoi(argv[0]);
+ int rxlev_dbm = atoi(argv[0]);
+
+ mp = &trx->bts->ms_dpc_params.rxlev_meas;
+ mp->lower_thresh = mp->upper_thresh = dbm2rxlev(rxlev_dbm);
+
+ vty_out(vty, "%% Command '%s' has been deprecated.%s"
+ "%% MS/BS Power control parameters should be configured in osmo-bsc: "
+ "use 'rxlev-thresh lower %u upper %u'.%s",
+ self->string, VTY_NEWLINE,
+ mp->lower_thresh,
+ mp->upper_thresh,
+ VTY_NEWLINE);
return CMD_SUCCESS;
}
diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c
index 49403d4e..8e701ead 100644
--- a/src/osmo-bts-trx/trx_vty.c
+++ b/src/osmo-bts-trx/trx_vty.c
@@ -199,7 +199,9 @@ DEFUN_DEPRECATED(cfg_phy_ms_power_loop, cfg_phy_ms_power_loop_cmd,
vty_out(vty, "'%s' is deprecated, MS Power Control is now managed by BSC%s",
self->string, VTY_NEWLINE);
- g_bts->ul_power_ctrl.target_dbm = atoi(argv[0]);
+ uint8_t rxlev = dbm2rxlev(atoi(argv[0]));
+ g_bts->ms_dpc_params.rxlev_meas.lower_thresh = rxlev;
+ g_bts->ms_dpc_params.rxlev_meas.upper_thresh = rxlev;
return CMD_SUCCESS;
}
diff --git a/tests/power/bs_power_loop_test.c b/tests/power/bs_power_loop_test.c
index fd931830..ecd83e70 100644
--- a/tests/power/bs_power_loop_test.c
+++ b/tests/power/bs_power_loop_test.c
@@ -31,14 +31,9 @@
#define PWR_TEST_RXLEV_TARGET 30
-#define PWR_TEST_CFG_RXLEV_TARGET \
- .target_dbm = -110 + PWR_TEST_RXLEV_TARGET
-
-/* NOTE: raise/lower values are intentionally swapped here,
- * as it makes more sense in the context of BS Power Control. */
-#define PWR_TEST_CFG_RAISE_LOWER_MAX \
- .raise_step_max_db = PWR_LOWER_MAX_DB, \
- .lower_step_max_db = PWR_RAISE_MAX_DB
+#define PWR_TEST_CFG_RXLEV_THRESH(hyst) \
+ .lower_thresh = PWR_TEST_RXLEV_TARGET - hyst, \
+ .upper_thresh = PWR_TEST_RXLEV_TARGET + hyst
#define DL_MEAS_FULL(rxqual, rxlev) \
.rxqual_full = rxqual, \
@@ -61,8 +56,9 @@ enum power_test_step_type {
PWR_TEST_ST_IND_MEAS = 0,
PWR_TEST_ST_IND_DUMMY,
PWR_TEST_ST_SET_STATE,
- PWR_TEST_ST_SET_PARAMS,
+ PWR_TEST_ST_SET_RXLEV_PARAMS,
PWR_TEST_ST_ENABLE_DTXD,
+ PWR_TEST_ST_DISABLE_DPC,
};
struct power_test_step {
@@ -72,8 +68,8 @@ struct power_test_step {
union {
/* Power Control state */
struct lchan_power_ctrl_state state;
- /* Power Control parameters */
- struct bts_power_ctrl_params params;
+ /* Measurement pre-processing parameters */
+ struct gsm_power_ctrl_meas_params mp;
/* Indicated DL measurements */
struct {
uint8_t rxqual_full;
@@ -107,12 +103,6 @@ static void init_test(const char *name)
g_bts->band = GSM_BAND_900;
g_bts->c0 = g_trx;
- g_bts->dl_power_ctrl = g_bts->ul_power_ctrl = \
- (struct bts_power_ctrl_params) {
- PWR_TEST_CFG_RXLEV_TARGET,
- PWR_TEST_CFG_RAISE_LOWER_MAX,
- };
-
printf("\nStarting test case '%s'\n", name);
}
@@ -157,13 +147,18 @@ static int exec_power_step(struct gsm_lchan *lchan,
printf("#%02u %s() <- State (re)set (current %u dB, max %u dB)\n",
n, __func__, step->state.current, step->state.max);
lchan->bs_power_ctrl = step->state;
+ lchan->bs_power_ctrl.dpc_params = &lchan->bs_dpc_params;
return 0; /* we're done */
- case PWR_TEST_ST_SET_PARAMS:
- printf("#%02u %s() <- Param (re)set (target %d dBm, hysteresis %u dB, "
- "filtering is %sabled)\n",
- n, __func__, step->params.target_dbm, step->params.hysteresis_db,
- step->params.pf_algo != BTS_PF_ALGO_NONE ? "en" : "dis");
- g_bts->dl_power_ctrl = step->params;
+ case PWR_TEST_ST_DISABLE_DPC:
+ printf("#%02u %s() <- Dynamic power control is disabled\n", n, __func__);
+ lchan->bs_power_ctrl.dpc_params = NULL;
+ return 0; /* we're done */
+ case PWR_TEST_ST_SET_RXLEV_PARAMS:
+ printf("#%02u %s() <- (Re)set RxLev params (thresh %u .. %u, "
+ "averaging is %sabled)\n",
+ n, __func__, step->mp.lower_thresh, step->mp.upper_thresh,
+ step->mp.algo != GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE ? "en" : "dis");
+ lchan->bs_dpc_params.rxlev_meas = step->mp;
return 0; /* we're done */
case PWR_TEST_ST_ENABLE_DTXD:
printf("#%02u %s() <- Enable DTXd\n", n, __func__);
@@ -202,6 +197,17 @@ static void exec_power_test(const struct power_test_step *steps,
init_test(name);
struct gsm_lchan *lchan = &g_trx->ts[0].lchan[0];
+
+ lchan->bs_dpc_params = (struct gsm_power_ctrl_params) {
+ /* NOTE: raise/lower values are intentionally swapped here,
+ * as it makes more sense in the context of BS Power Control. */
+ .inc_step_size_db = PWR_LOWER_MAX_DB,
+ .red_step_size_db = PWR_RAISE_MAX_DB,
+
+ /* RxLev pre-processing parameters */
+ .rxlev_meas = { PWR_TEST_CFG_RXLEV_THRESH(0) },
+ };
+
for (n = 0; n < num_steps; n++)
rc |= exec_power_step(lchan, n, &steps[n]);
@@ -212,7 +218,8 @@ static void exec_power_test(const struct power_test_step *steps,
static const struct power_test_step TC_fixed_mode[] = {
/* Initial state: 10 dB, up to 20 dB */
{ .type = PWR_TEST_ST_SET_STATE,
- .state = { .current = 10, .max = 2 * 10, .fixed = true } },
+ .state = { .current = 10, .max = 2 * 10 } },
+ { .type = PWR_TEST_ST_DISABLE_DPC },
/* MS indicates random RxQual/RxLev values, which must be ignored */
{ .meas = DL_MEAS_FULL_SUB(0, 63), .exp_txred = 10 },
@@ -338,12 +345,8 @@ static const struct power_test_step TC_rxlev_hyst[] = {
{ .meas = DL_MEAS_FULL_SUB(0, PWR_TEST_RXLEV_TARGET - 2), .exp_txred = 12 },
/* Enable hysteresis */
- { .type = PWR_TEST_ST_SET_PARAMS,
- .params = {
- PWR_TEST_CFG_RXLEV_TARGET,
- PWR_TEST_CFG_RAISE_LOWER_MAX,
- .hysteresis_db = 3,
- }
+ { .type = PWR_TEST_ST_SET_RXLEV_PARAMS,
+ .mp = { PWR_TEST_CFG_RXLEV_THRESH(3) }
},
/* Hysteresis is enabled, so small deviations do not trigger any changes */
@@ -359,13 +362,12 @@ static const struct power_test_step TC_rxlev_pf_ewma[] = {
{ .type = PWR_TEST_ST_SET_STATE,
.state = { .current = 16, .max = 2 * 15 } },
- /* Enable EWMA based power filtering */
- { .type = PWR_TEST_ST_SET_PARAMS,
- .params = {
- PWR_TEST_CFG_RXLEV_TARGET,
- PWR_TEST_CFG_RAISE_LOWER_MAX,
- .pf_algo = BTS_PF_ALGO_EWMA,
- .pf.ewma.alpha = 50,
+ /* Enable EWMA based pre-processing for RxLev */
+ { .type = PWR_TEST_ST_SET_RXLEV_PARAMS,
+ .mp = {
+ PWR_TEST_CFG_RXLEV_THRESH(0),
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA,
+ .ewma.alpha = 50,
}
},
diff --git a/tests/power/bs_power_loop_test.ok b/tests/power/bs_power_loop_test.ok
index acaad824..814f7e2b 100644
--- a/tests/power/bs_power_loop_test.ok
+++ b/tests/power/bs_power_loop_test.ok
@@ -2,21 +2,22 @@ Testing BS Power loop...
Starting test case 'TC_fixed_mode'
#00 exec_power_step() <- State (re)set (current 10 dB, max 20 dB)
-#01 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(63), RXQUAL-FULL(0), RXLEV-SUB(63), RXQUAL-SUB(0)
-#01 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 3f 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-#01 lchan_bs_pwr_ctrl() -> BS power reduction: 10 -> 10 (expected 10)
-#02 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(00), RXQUAL-FULL(7), RXLEV-SUB(00), RXQUAL-SUB(7)
-#02 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 00 00 7e 00 00 00 00 00 00 00 00 00 00 00 00 00
+#01 exec_power_step() <- Dynamic power control is disabled
+#02 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(63), RXQUAL-FULL(0), RXLEV-SUB(63), RXQUAL-SUB(0)
+#02 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 3f 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#02 lchan_bs_pwr_ctrl() -> BS power reduction: 10 -> 10 (expected 10)
-#03 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0)
-#03 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 1e 1e 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+#03 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(00), RXQUAL-FULL(7), RXLEV-SUB(00), RXQUAL-SUB(7)
+#03 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 00 00 7e 00 00 00 00 00 00 00 00 00 00 00 00 00
#03 lchan_bs_pwr_ctrl() -> BS power reduction: 10 -> 10 (expected 10)
-#04 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(30), RXQUAL-FULL(1), RXLEV-SUB(30), RXQUAL-SUB(1)
-#04 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 1e 1e 12 00 00 00 00 00 00 00 00 00 00 00 00 00
+#04 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0)
+#04 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 1e 1e 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#04 lchan_bs_pwr_ctrl() -> BS power reduction: 10 -> 10 (expected 10)
-#05 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(50), RXQUAL-FULL(1), RXLEV-SUB(50), RXQUAL-SUB(1)
-#05 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 32 32 12 00 00 00 00 00 00 00 00 00 00 00 00 00
+#05 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(30), RXQUAL-FULL(1), RXLEV-SUB(30), RXQUAL-SUB(1)
+#05 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 1e 1e 12 00 00 00 00 00 00 00 00 00 00 00 00 00
#05 lchan_bs_pwr_ctrl() -> BS power reduction: 10 -> 10 (expected 10)
+#06 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(50), RXQUAL-FULL(1), RXLEV-SUB(50), RXQUAL-SUB(1)
+#06 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 32 32 12 00 00 00 00 00 00 00 00 00 00 00 00 00
+#06 lchan_bs_pwr_ctrl() -> BS power reduction: 10 -> 10 (expected 10)
Test case verdict: SUCCESS
Starting test case 'TC_rxlev_target'
@@ -175,7 +176,7 @@ Starting test case 'TC_rxlev_hyst'
#04 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(28), RXQUAL-FULL(0), RXLEV-SUB(28), RXQUAL-SUB(0)
#04 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 1c 1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#04 lchan_bs_pwr_ctrl() -> BS power reduction: 14 -> 12 (expected 12)
-#05 exec_power_step() <- Param (re)set (target -80 dBm, hysteresis 3 dB, filtering is disabled)
+#05 exec_power_step() <- (Re)set RxLev params (thresh 27 .. 33, averaging is disabled)
#06 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(31), RXQUAL-FULL(0), RXLEV-SUB(31), RXQUAL-SUB(0)
#06 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 1f 1f 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#06 lchan_bs_pwr_ctrl() -> BS power reduction: 12 -> 12 (expected 12)
@@ -192,7 +193,7 @@ Test case verdict: SUCCESS
Starting test case 'TC_rxlev_pf_ewma'
#00 exec_power_step() <- State (re)set (current 16 dB, max 30 dB)
-#01 exec_power_step() <- Param (re)set (target -80 dBm, hysteresis 0 dB, filtering is enabled)
+#01 exec_power_step() <- (Re)set RxLev params (thresh 30 .. 30, averaging is enabled)
#02 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0)
#02 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 1e 1e 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#02 lchan_bs_pwr_ctrl() -> BS power reduction: 16 -> 16 (expected 16)
diff --git a/tests/power/ms_power_loop_test.c b/tests/power/ms_power_loop_test.c
index ea3c2493..e93a2efe 100644
--- a/tests/power/ms_power_loop_test.c
+++ b/tests/power/ms_power_loop_test.c
@@ -28,6 +28,10 @@
#include <stdio.h>
+#define PWR_TEST_RXLEV_TARGET_DBM -75
+#define PWR_TEST_RXLEV_TARGET \
+ dbm2rxlev(PWR_TEST_RXLEV_TARGET_DBM)
+
static struct gsm_bts *g_bts = NULL;
static struct gsm_bts_trx *g_trx = NULL;
@@ -50,12 +54,15 @@ static void init_test(const char *name)
g_bts->band = GSM_BAND_1800;
g_bts->c0 = g_trx;
- g_bts->ul_power_ctrl = g_bts->dl_power_ctrl = \
- (struct bts_power_ctrl_params) {
- .target_dbm = -75,
- .raise_step_max_db = PWR_RAISE_MAX_DB,
- .lower_step_max_db = PWR_LOWER_MAX_DB,
- };
+ /* Init default MS power control parameters, enable dynamic power control */
+ struct gsm_power_ctrl_params *params = &g_trx->ts[0].lchan[0].ms_dpc_params;
+ g_trx->ts[0].lchan[0].ms_power_ctrl.dpc_params = params;
+ *params = power_ctrl_params_def;
+
+ /* Disable RxLev pre-processing and hysteresis by default */
+ struct gsm_power_ctrl_meas_params *mp = &params->rxlev_meas;
+ mp->lower_thresh = mp->upper_thresh = PWR_TEST_RXLEV_TARGET;
+ mp->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE;
printf("\nStarting test case '%s'\n", name);
}
@@ -104,7 +111,7 @@ static void test_power_loop(void)
apply_power_test(lchan, -90, 1, 5);
/* Check good RSSI value keeps it at same power level: */
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm, 0, 5);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, 0, 5);
apply_power_test(lchan, -90, 1, 3);
apply_power_test(lchan, -90, 1, 2); /* .max is pwr lvl 2 */
@@ -122,7 +129,7 @@ static void test_power_loop(void)
apply_power_test(lchan, -90, 0, 29);
/* Check good RSSI value keeps it at same power level: */
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm, 0, 29);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, 0, 29);
/* Now go down, steps are double size in this direction: */
apply_power_test(lchan, -45, 1, 1);
@@ -130,23 +137,23 @@ static void test_power_loop(void)
apply_power_test(lchan, -45, 1, 9);
/* Go down only one level down and up: */
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm + 2, 1, 10);
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm - 2, 1, 9);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 2, 1, 10);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 2, 1, 9);
/* Check if BSC requesting a low max power is applied after loop calculation: */
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 2);
OSMO_ASSERT(lchan->ms_power_ctrl.max == 14);
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm + 2, 1, 14);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 2, 1, 14);
/* Set back a more normal max: */
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 30);
OSMO_ASSERT(lchan->ms_power_ctrl.max == 0);
- /* Fix it and jump down */
- lchan->ms_power_ctrl.fixed = true;
+ /* Disable dynamic power control and jump down */
+ lchan->ms_power_ctrl.dpc_params = NULL;
apply_power_test(lchan, -60, 0, 14);
- /* And leave it again */
- lchan->ms_power_ctrl.fixed = false;
+ /* Enable and leave it again */
+ lchan->ms_power_ctrl.dpc_params = &lchan->ms_dpc_params;
apply_power_test(lchan, -40, 1, 15);
}
@@ -159,8 +166,9 @@ static void test_pf_algo_ewma(void)
lchan = &g_trx->ts[0].lchan[0];
avg100 = &lchan->ms_power_ctrl.avg100_rxlev_dbm;
- g_bts->ul_power_ctrl.pf_algo = BTS_PF_ALGO_EWMA;
- g_bts->ul_power_ctrl.pf.ewma.alpha = 20; /* 80% smoothing */
+ struct gsm_power_ctrl_meas_params *mp = &lchan->ms_dpc_params.rxlev_meas;
+ mp->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA;
+ mp->ewma.alpha = 20; /* 80% smoothing */
lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
@@ -194,7 +202,7 @@ static void test_pf_algo_ewma(void)
apply_power_test(lchan, -70, 1, 9);
CHECK_UL_RSSI_AVG100(-78.40);
- g_bts->ul_power_ctrl.pf.ewma.alpha = 70; /* 30% smoothing */
+ mp->ewma.alpha = 70; /* 30% smoothing */
lchan->ms_power_ctrl.current = 15;
lchan->ms_power_ctrl.avg100_rxlev_dbm = 0;
@@ -220,22 +228,23 @@ static void test_power_hysteresis(void)
lchan = &g_trx->ts[0].lchan[0];
/* Tolerate power deviations in range -80 .. -70 */
- g_bts->ul_power_ctrl.hysteresis_db = 5;
+ lchan->ms_dpc_params.rxlev_meas.lower_thresh = 30;
+ lchan->ms_dpc_params.rxlev_meas.upper_thresh = 40;
lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 26);
OSMO_ASSERT(lchan->ms_power_ctrl.max == 2);
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm, 0, 15);
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm + 3, 0, 15);
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm - 3, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 3, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 3, 0, 15);
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm, 0, 15);
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm + 5, 0, 15);
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm - 5, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 5, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 5, 0, 15);
- apply_power_test(lchan, g_bts->ul_power_ctrl.target_dbm - 10, 1, 13);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 10, 1, 13);
}
int main(int argc, char **argv)