Change in osmo-bts[master]: WIP: MS power loop: Take C/I into account

pespin gerrit-no-reply at lists.osmocom.org
Thu Aug 26 17:45:14 UTC 2021


pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-bts/+/25268 )


Change subject: WIP: MS power loop: Take C/I into account
......................................................................

WIP: MS power loop: Take C/I into account

Related: SYS#4917
Change-Id: I5dfd8ff9ab6b499646498b507624758dcc160fb6
---
M include/osmo-bts/gsm_data.h
M include/osmo-bts/power_control.h
M src/common/gsm_data.c
M src/common/l1sap.c
M src/common/power_control.c
M tests/power/ms_power_loop_test.c
M tests/power/ms_power_loop_test.err
M tests/power/ms_power_loop_test.ok
8 files changed, 313 insertions(+), 128 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/68/25268/1

diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h
index 80f1833..979e6d9 100644
--- a/include/osmo-bts/gsm_data.h
+++ b/include/osmo-bts/gsm_data.h
@@ -211,6 +211,14 @@
 	/* Measurement averaging parameters for RxLev & RxQual */
 	struct gsm_power_ctrl_meas_params rxqual_meas;
 	struct gsm_power_ctrl_meas_params rxlev_meas;
+
+	/* Measurement averaging parameters for C/I, per chan type */
+	struct gsm_power_ctrl_meas_params ci_fr_meas;
+	struct gsm_power_ctrl_meas_params ci_hr_meas;
+	struct gsm_power_ctrl_meas_params ci_amr_fr_meas;
+	struct gsm_power_ctrl_meas_params ci_amr_hr_meas;
+	struct gsm_power_ctrl_meas_params ci_sdcch_meas;
+	struct gsm_power_ctrl_meas_params ci_gprs_meas;
 };
 
 /* Default MS/BS Power Control parameters */
diff --git a/include/osmo-bts/power_control.h b/include/osmo-bts/power_control.h
index f2e14cf..ed9f891 100644
--- a/include/osmo-bts/power_control.h
+++ b/include/osmo-bts/power_control.h
@@ -5,7 +5,8 @@
 
 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);
 
 int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
 		      const struct gsm48_hdr *gh);
diff --git a/src/common/gsm_data.c b/src/common/gsm_data.c
index b1f695b..e5a4d38 100644
--- a/src/common/gsm_data.c
+++ b/src/common/gsm_data.c
@@ -527,4 +527,46 @@
 		/* FIXME: RxQual averaging is not yet implemented */
 		.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
 	},
+
+	/* C/I measurement parameters. //TODO: Add ci_*meas here, based on
+	 * 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 = { /* Target C/I = 15 dB, Soft blocking threshold = 10 dB */
+		.lower_thresh = 13,
+		.upper_thresh = 17,
+		/* FIXME: C/I averaging is not yet implemented */
+		.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+	},
+	.ci_hr_meas = { /* Target C/I = 18 dB, Soft blocking threshold = 13 dB */
+		.lower_thresh = 16,
+		.upper_thresh = 21,
+		/* FIXME: C/I averaging is not yet implemented */
+		.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+	},
+	.ci_amr_fr_meas = { /* Target C/I = 9 dB, Soft blocking threshold = 4 dB */
+		.lower_thresh = 7,
+		.upper_thresh = 11,
+		/* FIXME: C/I averaging is not yet implemented */
+		.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+	},
+	.ci_amr_hr_meas = { /* Target C/I = 15 dB, Soft blocking threshold = 10 dB */
+		.lower_thresh = 13,
+		.upper_thresh = 17,
+		/* FIXME: C/I averaging is not yet implemented */
+		.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+	},
+	.ci_sdcch_meas = { /* Target C/I = 14 dB, Soft blocking threshold = 9 dB */
+		.lower_thresh = 12,
+		.upper_thresh = 16,
+		/* FIXME: C/I averaging is not yet implemented */
+		.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+	},
+	.ci_gprs_meas = { /* Target C/I = 20 dB, Soft blocking threshold = 15 dB */
+		.lower_thresh = 18,
+		.upper_thresh = 24,
+		/* FIXME: C/I averaging is not yet implemented */
+		.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+	},
 };
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index c028a2c..189a814 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -1600,7 +1600,7 @@
 			rsl_tx_meas_res(lchan, NULL, 0, le);
 
 			radio_link_timeout(lchan, true);
-			lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, data_ind->rssi);
+			lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, data_ind->rssi, data_ind->lqual_cb);
 		}
 		return -EINVAL;
 	}
@@ -1628,7 +1628,7 @@
 		lchan->meas.l1_info.ta = l1_hdr->ta;
 		lchan->meas.flags |= LC_UL_M_F_L1_VALID;
 
-		lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi);
+		lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi, data_ind->lqual_cb);
 		lchan_bs_pwr_ctrl(lchan, (const struct gsm48_hdr *) &data[5]);
 	} else
 		le = &lchan->lapdm_ch.lapdm_dcch;
diff --git a/src/common/power_control.c b/src/common/power_control.c
index 4f5d15e..1a08fcb 100644
--- a/src/common/power_control.c
+++ b/src/common/power_control.c
@@ -139,14 +139,36 @@
 	return delta;
 }
 
+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 &params->ci_sdcch_meas;
+	case GSM_LCHAN_PDTCH:
+		return &params->ci_gprs_meas;
+	case GSM_LCHAN_TCH_F:
+		/* FIXME: check if need to return ci_amr_fr_meas */
+		return &params->ci_fr_meas;
+	case GSM_LCHAN_TCH_H:
+		/* FIXME: check if need to return ci_amr_hr_meas */
+		return &params->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,6 +177,7 @@
 	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 gsm_power_ctrl_meas_params *ci_meas;
 
 	if (!trx_ms_pwr_ctrl_is_osmo(trx))
 		return 0;
@@ -187,8 +210,24 @@
 		return 0;
 	}
 
-	/* Calculate the new Tx power value (in dBm) */
-	new_dbm = ms_dbm + calc_delta(params, state, ul_rssi_dbm);
+	/* If computed C/I is out of acceptable thresholds: */
+	ci_meas = lchan_get_ci_thresholds(lchan);
+	if (ul_lqual_cb < ci_meas->lower_thresh * 10) {
+		new_dbm = ms_dbm + params->inc_step_size_db;
+		LOGPLCHAN(lchan, DLOOP, LOGL_ERROR, "Increasing MS Tx power: "
+			  "%d -> %d dB due to C/I %d cB worse than %u cB on %s\n",
+			  ms_dbm, new_dbm, ul_lqual_cb, ci_meas->lower_thresh * 10,
+			  gsm_lchant_name(lchan->type));
+	} else if (ul_lqual_cb > ci_meas->upper_thresh * 10) {
+		new_dbm = ms_dbm - params->red_step_size_db;
+		LOGPLCHAN(lchan, DLOOP, LOGL_ERROR, "Decreasing MS Tx power: "
+			  "%d -> %d dB due to C/I %d cB better than %u cB on %s\n",
+			  ms_dbm, new_dbm, ul_lqual_cb, ci_meas->upper_thresh * 10,
+			  gsm_lchant_name(lchan->type));
+	} else {
+		/* Calculate the new Tx power value (in dBm) */
+		new_dbm = ms_dbm + calc_delta(params, state, ul_rssi_dbm);
+	}
 
 	/* 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. */
@@ -212,18 +251,18 @@
 
 	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",
+			  "(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", rx-current %d dBm, rx-target %d dBm, C/I %d cB)\n",
 			  new_power_lvl, new_dbm, ms_power_lvl, state->max,
-			  ul_rssi_dbm, target_dbm);
+			  ul_rssi_dbm, target_dbm, ul_lqual_cb);
 		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",
+		  "(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", rx-current %d dBm, rx-target %d dBm, C/I %d cB)\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);
+		  ms_power_lvl, state->max, ul_rssi_dbm, target_dbm, ul_lqual_cb);
 
 	/* store the resulting new MS power level in the lchan */
 	state->current = new_power_lvl;
diff --git a/tests/power/ms_power_loop_test.c b/tests/power/ms_power_loop_test.c
index 0d86310..f0c0250 100644
--- a/tests/power/ms_power_loop_test.c
+++ b/tests/power/ms_power_loop_test.c
@@ -67,13 +67,13 @@
 	printf("\nStarting test case '%s'\n", name);
 }
 
-static inline void apply_power_test(struct gsm_lchan *lchan, int rxlev, int exp_ret, uint8_t exp_current)
+static inline void apply_power_test(struct gsm_lchan *lchan, int rxlev, int lqual_cb, int exp_ret, uint8_t exp_current)
 {
 	uint8_t old;
 	int ret;
 
 	old = lchan->ms_power_ctrl.current;
-	ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, rxlev);
+	ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, rxlev, lqual_cb);
 
 	/* Keep the measurement counter updated */
 	lchan->meas.res_nr++;
@@ -87,9 +87,14 @@
 static void test_power_loop(void)
 {
 	struct gsm_lchan *lchan;
+	const struct gsm_power_ctrl_params *params;
+	int16_t good_lqual;
 
 	init_test(__func__);
 	lchan = &g_trx->ts[0].lchan[0];
+	params = lchan->ms_power_ctrl.dpc_params;
+	lchan->type = GSM_LCHAN_SDCCH;
+	good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
 
 	lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0);
 	OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
@@ -97,73 +102,78 @@
 	OSMO_ASSERT(lchan->ms_power_ctrl.max == 2);
 
 	/* Simply clamping */
-	apply_power_test(lchan, -60, 0, 15);
+	apply_power_test(lchan, -60, good_lqual, 0, 15);
 
 	/*
 	 * Now 15 dB too little and we should power it up. Could be a
 	 * power level of 7 or 8 for 15 dBm. However, since we limit peace at
 	 * which we change values, expect several steps of MS_RAISE_MAX_DB/2 levels:
 	 */
-	apply_power_test(lchan, -90, 1, 13);
-	apply_power_test(lchan, -90, 1, 11);
-	apply_power_test(lchan, -90, 1, 9);
-	apply_power_test(lchan, -90, 1, 7);
-	apply_power_test(lchan, -90, 1, 5);
+	apply_power_test(lchan, -90, good_lqual, 1, 13);
+	apply_power_test(lchan, -90, good_lqual, 1, 11);
+	apply_power_test(lchan, -90, good_lqual, 1, 9);
+	apply_power_test(lchan, -90, good_lqual, 1, 7);
+	apply_power_test(lchan, -90, good_lqual, 1, 5);
 
 	/* Check good RSSI value keeps it at same power level: */
-	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, 0, 5);
+	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, good_lqual, 0, 5);
 
-	apply_power_test(lchan, -90, 1, 3);
-	apply_power_test(lchan, -90, 1, 2); /* .max is pwr lvl 2 */
-	apply_power_test(lchan, -90, 0, 2); /* .max is pwr lvl 2 */
+	apply_power_test(lchan, -90, good_lqual, 1, 3);
+	apply_power_test(lchan, -90, good_lqual, 1, 2); /* .max is pwr lvl 2 */
+	apply_power_test(lchan, -90, good_lqual, 0, 2); /* .max is pwr lvl 2 */
 
 	lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 30);
 	OSMO_ASSERT(lchan->ms_power_ctrl.max == 0);
-	apply_power_test(lchan, -90, 1, 0); /* .max is pwr lvl 0 */
-	apply_power_test(lchan, -90, 0, 0); /* .max is pwr lvl 0 */
+	apply_power_test(lchan, -90, good_lqual, 1, 0); /* .max is pwr lvl 0 */
+	apply_power_test(lchan, -90, good_lqual, 0, 0); /* .max is pwr lvl 0 */
 
 	lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 36);
 	OSMO_ASSERT(lchan->ms_power_ctrl.max == 29);
-	apply_power_test(lchan, -90, 1, 30);
-	apply_power_test(lchan, -90, 1, 29);
-	apply_power_test(lchan, -90, 0, 29);
+	apply_power_test(lchan, -90, good_lqual, 1, 30);
+	apply_power_test(lchan, -90, good_lqual, 1, 29);
+	apply_power_test(lchan, -90, good_lqual, 0, 29);
 
 	/* Check good RSSI value keeps it at same power level: */
-	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, 0, 29);
+	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, good_lqual, 0, 29);
 
 	/* Now go down, steps are double size in this direction: */
-	apply_power_test(lchan, -45, 1, 1);
-	apply_power_test(lchan, -45, 1, 5);
-	apply_power_test(lchan, -45, 1, 9);
+	apply_power_test(lchan, -45, good_lqual, 1, 1);
+	apply_power_test(lchan, -45, good_lqual, 1, 5);
+	apply_power_test(lchan, -45, good_lqual, 1, 9);
 
 	/* Go down only one level down and up: */
-	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 2, 1, 10);
-	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 2, 1, 9);
+	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 2, good_lqual, 1, 10);
+	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 2, good_lqual, 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, PWR_TEST_RXLEV_TARGET_DBM + 2, 1, 14);
+	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 2, good_lqual, 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);
 
 	/* Disable dynamic power control and jump down */
 	lchan->ms_power_ctrl.dpc_params = NULL;
-	apply_power_test(lchan, -60, 0, 14);
+	apply_power_test(lchan, -60, good_lqual, 0, 14);
 
 	/* Enable and leave it again */
 	lchan->ms_power_ctrl.dpc_params = &lchan->ms_dpc_params;
-	apply_power_test(lchan, -40, 1, 15);
+	apply_power_test(lchan, -40, good_lqual, 1, 15);
 }
 
 static void test_pf_algo_ewma(void)
 {
 	struct gsm_lchan *lchan;
+	const struct gsm_power_ctrl_params *params;
+	int16_t good_lqual;
 	const int *avg100;
 
 	init_test(__func__);
 	lchan = &g_trx->ts[0].lchan[0];
+	lchan->type = GSM_LCHAN_SDCCH;
+	params = lchan->ms_power_ctrl.dpc_params;
+	good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
 	avg100 = &lchan->ms_power_ctrl.rxlev_meas_proc.ewma.Avg100;
 
 	struct gsm_power_ctrl_meas_params *mp = &lchan->ms_dpc_params.rxlev_meas;
@@ -180,15 +190,15 @@
 	       ((float) *avg100) / 100, exp);
 
 	/* UL RSSI remains constant => no UL power change */
-	apply_power_test(lchan, -75, 0, 15);
+	apply_power_test(lchan, -75, good_lqual, 0, 15);
 	CHECK_UL_RSSI_AVG100(-75.00);
 
 	/* Avg[t] = (0.2 * -90) + (0.8 * -75) = -78.0 dBm */
-	apply_power_test(lchan, -90, 1, 13);
+	apply_power_test(lchan, -90, good_lqual, 1, 13);
 	CHECK_UL_RSSI_AVG100(-78.00);
 
 	/* Avg[t] = (0.2 * -90) + (0.8 * -78) = -80.4 dBm */
-	apply_power_test(lchan, -90, 1, 11);
+	apply_power_test(lchan, -90, good_lqual, 1, 11);
 	CHECK_UL_RSSI_AVG100(-80.40);
 
 	/* Avg[t] = (0.2 * -70) + (0.8 * -80.4) = -78.32 dBm,
@@ -199,7 +209,7 @@
 	 *   Avg100[t] = -8040 + 20 * (-70 + 80)
 	 *   Avg100[t] = -8040 + 200 = -7840
 	 *   Avg[t] = -7840 / 100 = -78.4 */
-	apply_power_test(lchan, -70, 1, 9);
+	apply_power_test(lchan, -70, good_lqual, 1, 9);
 	CHECK_UL_RSSI_AVG100(-78.40);
 
 	mp->ewma.alpha = 70; /* 30% smoothing */
@@ -208,25 +218,30 @@
 		(struct gsm_power_ctrl_meas_proc_state) { 0 };
 
 	/* This is the first sample, the filter outputs it as-is */
-	apply_power_test(lchan, -50, 0, 15);
+	apply_power_test(lchan, -50, good_lqual, 0, 15);
 	CHECK_UL_RSSI_AVG100(-50.00);
 
 	/* Avg[t] = (0.7 * -50) + (0.3 * -50) = -50.0 dBm */
-	apply_power_test(lchan, -50, 0, 15);
+	apply_power_test(lchan, -50, good_lqual, 0, 15);
 	CHECK_UL_RSSI_AVG100(-50.0);
 
 	/* Simulate SACCH block loss (-110 dBm):
 	 * Avg[t] = (0.7 * -110) + (0.3 * -50) = -92.0 dBm */
-	apply_power_test(lchan, -110, 1, 13);
+	apply_power_test(lchan, -110, good_lqual, 1, 13);
 	CHECK_UL_RSSI_AVG100(-92.0);
 }
 
 static void test_power_hysteresis(void)
 {
 	struct gsm_lchan *lchan;
+	const struct gsm_power_ctrl_params *params;
+	int16_t good_lqual;
 
 	init_test(__func__);
 	lchan = &g_trx->ts[0].lchan[0];
+	lchan->type = GSM_LCHAN_SDCCH;
+	params = lchan->ms_power_ctrl.dpc_params;
+	good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
 
 	/* Tolerate power deviations in range -80 .. -70 */
 	lchan->ms_dpc_params.rxlev_meas.lower_thresh = 30;
@@ -237,61 +252,66 @@
 	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, 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, PWR_TEST_RXLEV_TARGET_DBM, good_lqual, 0, 15);
+	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 3, good_lqual, 0, 15);
+	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 3, good_lqual, 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, PWR_TEST_RXLEV_TARGET_DBM, good_lqual, 0, 15);
+	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 5, good_lqual, 0, 15);
+	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 5, good_lqual, 0, 15);
 
-	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 10, 1, 13);
+	apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 10, good_lqual, 1, 13);
 }
 
 static void test_power_ctrl_interval(void)
 {
 	struct gsm_lchan *lchan;
+	const struct gsm_power_ctrl_params *params;
+	int16_t good_lqual;
 	unsigned int i, j;
 
 	init_test(__func__);
 	lchan = &g_trx->ts[0].lchan[0];
+	lchan->type = GSM_LCHAN_SDCCH;
+	params = lchan->ms_power_ctrl.dpc_params;
+	good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
 
 	lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 26);
 	OSMO_ASSERT(lchan->ms_power_ctrl.max == 2);
 
-	static const int script[][8][3] = {
+	const int script[][8][4] = {
 		{ /* P_Con_INTERVAL=0 (480 ms) */
 			/* { UL RxLev, expected rc, expected Tx power level } */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	13 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	11 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	 9 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	 7 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	 5 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	 3 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	 2 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	 2 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	13 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	11 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	 9 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	 7 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	 5 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	 3 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	 2 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	 2 },
 		},
 		{ /* P_Con_INTERVAL=1 (960 ms) */
 			/* { UL RxLev, expected rc, expected Tx power level } */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	13 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	0,	13 }, /* skipped */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	11 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	0,	11 }, /* skipped */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	 9 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	0,	 9 }, /* skipped */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	 7 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	0,	 7 }, /* skipped */
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	13 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	0,	13 }, /* skipped */
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	11 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	0,	11 }, /* skipped */
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	 9 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	0,	 9 }, /* skipped */
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	 7 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	0,	 7 }, /* skipped */
 		},
 		{ /* P_Con_INTERVAL=2 (1920 ms) */
 			/* { UL RxLev, expected rc, expected Tx power level } */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	13 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	0,	13 }, /* skipped */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	0,	13 }, /* skipped */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	0,	13 }, /* skipped */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	1,	11 },
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	0,	11 }, /* skipped */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	0,	11 }, /* skipped */
-			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	0,	11 }, /* skipped */
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	13 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	0,	13 }, /* skipped */
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	0,	13 }, /* skipped */
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	0,	13 }, /* skipped */
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	1,	11 },
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	0,	11 }, /* skipped */
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	0,	11 }, /* skipped */
+			{ PWR_TEST_RXLEV_TARGET_DBM - 15,	good_lqual,	0,	11 }, /* skipped */
 		},
 	};
 
@@ -305,14 +325,54 @@
 
 		for (j = 0; j < ARRAY_SIZE(script[i]); j++) {
 			apply_power_test(lchan, script[i][j][0],  /* UL RxLev */
-						script[i][j][1],  /* expected rc */
-						script[i][j][2]); /* expected Tx power level */
+						script[i][j][1],  /* UL C/I */
+						script[i][j][2],  /* expected rc */
+						script[i][j][3]); /* expected Tx power level */
 		}
 
 		printf("\n");
 	}
 }
 
+static void test_power_loop_ci(void)
+{
+	struct gsm_lchan *lchan;
+	const struct gsm_power_ctrl_params *params;
+	int16_t good_lqual, too_low_lqual, too_high_lqual;
+
+	init_test(__func__);
+	lchan = &g_trx->ts[0].lchan[0];
+	params = lchan->ms_power_ctrl.dpc_params;
+	lchan->type = GSM_LCHAN_SDCCH;
+	good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
+	too_low_lqual = (params->ci_sdcch_meas.lower_thresh - 1) * 10;
+	too_high_lqual = (params->ci_sdcch_meas.upper_thresh + 1) * 10;
+
+	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);
+
+	/* Simply clamping */
+	apply_power_test(lchan, -60, good_lqual, 0, 15);
+
+	/* Now UL C/I is too bad as well as RSSI: */
+	apply_power_test(lchan, -100, too_low_lqual, 1, 13);
+	apply_power_test(lchan, -100, too_low_lqual, 1, 11);
+
+	/* Now UL C/I is good again while RSSI is good: */
+	apply_power_test(lchan, -60, good_lqual, 1, 12);
+	apply_power_test(lchan, -60, too_high_lqual, 1, 13);
+
+	/* Now UL C/I is good while RSSI is bad, C/I mandates: */
+	apply_power_test(lchan, -100, good_lqual, 1, 11);
+	apply_power_test(lchan, -100, too_high_lqual, 1, 12);
+
+	/* Now UL C/I is bad again while RSSI is good, C/I mandates: */
+	apply_power_test(lchan, -60, good_lqual, 1, 13);
+	apply_power_test(lchan, -60, too_high_lqual, 1, 14);
+}
+
 int main(int argc, char **argv)
 {
 	printf("Testing power loop...\n");
@@ -332,6 +392,7 @@
 	test_pf_algo_ewma();
 	test_power_hysteresis();
 	test_power_ctrl_interval();
+	test_power_loop_ci();
 
 	printf("Power loop test OK\n");
 
diff --git a/tests/power/ms_power_loop_test.err b/tests/power/ms_power_loop_test.err
index ae8ad03..f7ebaed 100644
--- a/tests/power/ms_power_loop_test.err
+++ b/tests/power/ms_power_loop_test.err
@@ -1,51 +1,65 @@
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -60 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 12 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 9 (12 dBm) to 7, 16 dBm (rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 7 (16 dBm) to 5, 20 dBm (rx-ms-pwr-lvl 7, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 5, 20 dBm (rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 5 (20 dBm) to 3, 24 dBm (rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 3 (24 dBm) to 2, 26 dBm (rx-ms-pwr-lvl 3, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 2, 26 dBm (rx-ms-pwr-lvl 2, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 2 (26 dBm) to 0, 30 dBm (rx-ms-pwr-lvl 2, max-ms-pwr-lvl 0, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 0, 30 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 0, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 0 (30 dBm) to 30, 34 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 29, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 30 (34 dBm) to 29, 36 dBm (rx-ms-pwr-lvl 30, max-ms-pwr-lvl 29, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 29, 36 dBm (rx-ms-pwr-lvl 29, max-ms-pwr-lvl 29, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 29, 36 dBm (rx-ms-pwr-lvl 29, max-ms-pwr-lvl 29, rx-current -75 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 29 (36 dBm) to 30, 34 dBm (rx-ms-pwr-lvl 29, max-ms-pwr-lvl 29, rx-current -45 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 30 (34 dBm) to 31, 32 dBm (rx-ms-pwr-lvl 30, max-ms-pwr-lvl 29, rx-current -45 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 31 (32 dBm) to 0, 30 dBm (rx-ms-pwr-lvl 31, max-ms-pwr-lvl 29, rx-current -45 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 0 (30 dBm) to 1, 28 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 29, rx-current -73 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 1 (28 dBm) to 0, 30 dBm (rx-ms-pwr-lvl 1, max-ms-pwr-lvl 29, rx-current -77 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 0 (30 dBm) to 14, 2 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 14, rx-current -73 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 14 (2 dBm) to 15, 0 dBm (rx-ms-pwr-lvl 14, max-ms-pwr-lvl 0, rx-current -40 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 3 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 11 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -70 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -50 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -50 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -110 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -72 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -78 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -70 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -80 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -85 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 12 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 9 (12 dBm) to 7, 16 dBm (rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 7 (16 dBm) to 5, 20 dBm (rx-ms-pwr-lvl 7, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 5 (20 dBm) to 3, 24 dBm (rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 3 (24 dBm) to 2, 26 dBm (rx-ms-pwr-lvl 3, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 2, 26 dBm (rx-ms-pwr-lvl 2, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 12 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 9 (12 dBm) to 7, 16 dBm (rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -60 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 12 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 9 (12 dBm) to 7, 16 dBm (rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 7 (16 dBm) to 5, 20 dBm (rx-ms-pwr-lvl 7, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 5, 20 dBm (rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 5 (20 dBm) to 3, 24 dBm (rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 3 (24 dBm) to 2, 26 dBm (rx-ms-pwr-lvl 3, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 2, 26 dBm (rx-ms-pwr-lvl 2, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 2 (26 dBm) to 0, 30 dBm (rx-ms-pwr-lvl 2, max-ms-pwr-lvl 0, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 0, 30 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 0, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 0 (30 dBm) to 30, 34 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 29, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 30 (34 dBm) to 29, 36 dBm (rx-ms-pwr-lvl 30, max-ms-pwr-lvl 29, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 29, 36 dBm (rx-ms-pwr-lvl 29, max-ms-pwr-lvl 29, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 29, 36 dBm (rx-ms-pwr-lvl 29, max-ms-pwr-lvl 29, rx-current -75 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 29 (36 dBm) to 30, 34 dBm (rx-ms-pwr-lvl 29, max-ms-pwr-lvl 29, rx-current -45 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 30 (34 dBm) to 31, 32 dBm (rx-ms-pwr-lvl 30, max-ms-pwr-lvl 29, rx-current -45 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 31 (32 dBm) to 0, 30 dBm (rx-ms-pwr-lvl 31, max-ms-pwr-lvl 29, rx-current -45 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 0 (30 dBm) to 1, 28 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 29, rx-current -73 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 1 (28 dBm) to 0, 30 dBm (rx-ms-pwr-lvl 1, max-ms-pwr-lvl 29, rx-current -77 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 0 (30 dBm) to 14, 2 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 14, rx-current -73 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 14 (2 dBm) to 15, 0 dBm (rx-ms-pwr-lvl 14, max-ms-pwr-lvl 0, rx-current -40 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 3 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 11 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -70 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -50 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -50 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -110 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -72 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -78 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -70 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -80 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -85 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 12 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 9 (12 dBm) to 7, 16 dBm (rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 7 (16 dBm) to 5, 20 dBm (rx-ms-pwr-lvl 7, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 5 (20 dBm) to 3, 24 dBm (rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 3 (24 dBm) to 2, 26 dBm (rx-ms-pwr-lvl 3, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 2, 26 dBm (rx-ms-pwr-lvl 2, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 12 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 9 (12 dBm) to 7, 16 dBm (rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -60 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Increasing MS Tx power: 0 -> 4 dB due to C/I 110 cB worse than 120 cB on SDCCH
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -100 dBm, rx-target -75 dBm, C/I 110 cB)
+(bts=0,trx=0,ts=0,ss=0) Increasing MS Tx power: 4 -> 8 dB due to C/I 110 cB worse than 120 cB on SDCCH
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -100 dBm, rx-target -75 dBm, C/I 110 cB)
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 11 (8 dBm) to 12, 6 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -60 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Decreasing MS Tx power: 6 -> 4 dB due to C/I 170 cB better than 160 cB on SDCCH
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 12 (6 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 12, max-ms-pwr-lvl 2, rx-current -60 dBm, rx-target -75 dBm, C/I 170 cB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -100 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Decreasing MS Tx power: 8 -> 6 dB due to C/I 170 cB better than 160 cB on SDCCH
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 11 (8 dBm) to 12, 6 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -100 dBm, rx-target -75 dBm, C/I 170 cB)
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 12 (6 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 12, max-ms-pwr-lvl 2, rx-current -60 dBm, rx-target -75 dBm, C/I 140 cB)
+(bts=0,trx=0,ts=0,ss=0) Decreasing MS Tx power: 4 -> 2 dB due to C/I 170 cB better than 160 cB on SDCCH
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 13 (4 dBm) to 14, 2 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -60 dBm, rx-target -75 dBm, C/I 170 cB)
diff --git a/tests/power/ms_power_loop_test.ok b/tests/power/ms_power_loop_test.ok
index 5fea474..ccf2ddc 100644
--- a/tests/power/ms_power_loop_test.ok
+++ b/tests/power/ms_power_loop_test.ok
@@ -144,4 +144,24 @@
 lchan_ms_pwr_ctrl(RxLvl=-90 dBm) returns 0 (expected 0)
 	MS current power 11 -> 11 (expected 11)
 
+
+Starting test case 'test_power_loop_ci'
+lchan_ms_pwr_ctrl(RxLvl=-60 dBm) returns 0 (expected 0)
+	MS current power 15 -> 15 (expected 15)
+lchan_ms_pwr_ctrl(RxLvl=-100 dBm) returns 1 (expected 1)
+	MS current power 15 -> 13 (expected 13)
+lchan_ms_pwr_ctrl(RxLvl=-100 dBm) returns 1 (expected 1)
+	MS current power 13 -> 11 (expected 11)
+lchan_ms_pwr_ctrl(RxLvl=-60 dBm) returns 1 (expected 1)
+	MS current power 11 -> 12 (expected 12)
+lchan_ms_pwr_ctrl(RxLvl=-60 dBm) returns 1 (expected 1)
+	MS current power 12 -> 13 (expected 13)
+lchan_ms_pwr_ctrl(RxLvl=-100 dBm) returns 1 (expected 1)
+	MS current power 13 -> 11 (expected 11)
+lchan_ms_pwr_ctrl(RxLvl=-100 dBm) returns 1 (expected 1)
+	MS current power 11 -> 12 (expected 12)
+lchan_ms_pwr_ctrl(RxLvl=-60 dBm) returns 1 (expected 1)
+	MS current power 12 -> 13 (expected 13)
+lchan_ms_pwr_ctrl(RxLvl=-60 dBm) returns 1 (expected 1)
+	MS current power 13 -> 14 (expected 14)
 Power loop test OK

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/25268
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: I5dfd8ff9ab6b499646498b507624758dcc160fb6
Gerrit-Change-Number: 25268
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210826/c556db17/attachment.htm>


More information about the gerrit-log mailing list