Change in osmo-bts[master]: MS Power Control Loop: Take C/I into account

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

pespin gerrit-no-reply at lists.osmocom.org
Mon Sep 6 10:06:36 UTC 2021


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

Change subject: MS Power Control Loop: Take C/I into account
......................................................................

MS Power Control Loop: Take C/I into account

This commit extends existing MS Power Control Loop algorithm to take
into account computed C/I values on the UL, received from MS. The
related C/I parameters used by the algorithm are configured at and
provided by the BSC, which transmits them to the BTS similar to already
existing parameters.

Using C/I instead of existing RxQual is preferred due to extended
granularity of C/I (bigger range than RxQual's 0-7).
Furthermore, existing literature (such as "GSM/EDGE: Evolution and Performance"
Table 10.3) provides detailed information about expected target values,
even different values for different channel types. Hence, it was decided
to support setting different MS Power Parameters for different channel
types.

These MS Power Parameters are Osmocom specific, ie. supported only by
newish versions of osmo-bts. Older versions of osmo-bts should ignore
the new IEs added just fine. The new IEs containing the MS POwer
Parameters are not send for non osmo-bts BTSs, hence this commit is
secure with regards to running  osmo-bsc against an ip.access BTS such
as nanoBTS.

Related: SYS#4917
Depends: libosmocore.git Change-Id Iffef0611430ad6c90606149c398d80158633bbca
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 src/common/rsl.c
M src/common/vty.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
10 files changed, 504 insertions(+), 137 deletions(-)

Approvals:
  Jenkins Builder: Verified
  osmith: Looks good to me, but someone else must approve
  laforge: Looks good to me, approved



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..2da16bb 100644
--- a/src/common/gsm_data.c
+++ b/src/common/gsm_data.c
@@ -527,4 +527,136 @@
 		/* FIXME: RxQual averaging is not yet implemented */
 		.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 */
+	},
 };
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index 3b225c2..9785bfd 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -1604,7 +1604,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;
 	}
@@ -1632,7 +1632,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..9d874c4 100644
--- a/src/common/power_control.c
+++ b/src/common/power_control.c
@@ -90,7 +90,7 @@
 
 /* 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. */
@@ -139,14 +139,40 @@
 	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:
+		if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+			return &params->ci_amr_fr_meas;
+		else
+			return &params->ci_fr_meas;
+	case GSM_LCHAN_TCH_H:
+		if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+			return &params->ci_amr_hr_meas;
+		else
+			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 +181,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 +214,16 @@
 		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;
+	} else if (ul_lqual_cb > 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(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. */
@@ -211,19 +246,21 @@
 	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",
+		LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping MS power at control level %d (%d dBm): "
+			  "(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", RSSI[curr %d, tgt %d] dBm,"
+			  " C/I[curr %d, tgt %d] dB)\n",
 			  new_power_lvl, new_dbm, ms_power_lvl, state->max,
-			  ul_rssi_dbm, target_dbm);
+			  ul_rssi_dbm, target_dbm, ul_lqual_cb/10, CALC_TARGET(*ci_meas));
 		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): "
+		  "rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", RSSI[curr %d, tgt %d] dBm,"
+		  " C/I[curr %d, tgt %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, target_dbm, ul_lqual_cb/10, CALC_TARGET(*ci_meas));
 
 	/* store the resulting new MS power level in the lchan */
 	state->current = new_power_lvl;
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 4ad807f..f585ec4 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -938,6 +938,30 @@
 		params->rxqual_meas.upper_thresh = thresh->u_rxqual;
 	}
 
+	/* Osmocom extension, C/I related thresholds: */
+	if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_MS_PWR_CTL, sizeof(struct osmo_preproc_pc_thresh))) {
+		const struct osmo_preproc_pc_thresh *osmo_thresh;
+		ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_MS_PWR_CTL);
+		osmo_thresh = (const struct osmo_preproc_pc_thresh *) ie->val;
+		params->ci_fr_meas.lower_thresh = osmo_thresh->l_ci_fr;
+		params->ci_fr_meas.upper_thresh = osmo_thresh->u_ci_fr;
+
+		params->ci_hr_meas.lower_thresh = osmo_thresh->l_ci_hr;
+		params->ci_hr_meas.upper_thresh = osmo_thresh->u_ci_hr;
+
+		params->ci_amr_fr_meas.lower_thresh = osmo_thresh->l_ci_amr_fr;
+		params->ci_amr_fr_meas.upper_thresh = osmo_thresh->u_ci_amr_fr;
+
+		params->ci_amr_hr_meas.lower_thresh = osmo_thresh->l_ci_amr_hr;
+		params->ci_amr_hr_meas.upper_thresh = osmo_thresh->u_ci_amr_hr;
+
+		params->ci_sdcch_meas.lower_thresh = osmo_thresh->l_ci_sdcch;
+		params->ci_sdcch_meas.upper_thresh = osmo_thresh->u_ci_sdcch;
+
+		params->ci_gprs_meas.lower_thresh = osmo_thresh->l_ci_gprs;
+		params->ci_gprs_meas.upper_thresh = osmo_thresh->u_ci_gprs;
+	}
+
 	/* (TV) PC Threshold Comparators */
 	if ((ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_PC_THRESH_COMP)) != NULL) {
 		const struct ipac_preproc_pc_comp *thresh_comp;
@@ -964,6 +988,25 @@
 		params->red_step_size_db = thresh_comp->red_step_size;
 	}
 
+	/* Osmocom extension, C/I related thresholds: */
+	if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_PC_THRESH_COMP, sizeof(struct osmo_preproc_pc_thresh))) {
+		const struct osmo_preproc_pc_comp *osmo_thresh_comp;
+		ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_PC_THRESH_COMP);
+		osmo_thresh_comp = (const struct osmo_preproc_pc_comp *) ie->val;
+		#define SET_PREPROC_PC(PARAMS, FROM, TYPE) \
+			(PARAMS)->TYPE##_meas.lower_cmp_p = (FROM)->TYPE.lower_p; \
+			(PARAMS)->TYPE##_meas.lower_cmp_n = (FROM)->TYPE.lower_n; \
+			(PARAMS)->TYPE##_meas.upper_cmp_p = (FROM)->TYPE.upper_p; \
+			(PARAMS)->TYPE##_meas.upper_cmp_n = (FROM)->TYPE.upper_n
+		SET_PREPROC_PC(params, osmo_thresh_comp, ci_fr);
+		SET_PREPROC_PC(params, osmo_thresh_comp, ci_hr);
+		SET_PREPROC_PC(params, osmo_thresh_comp, ci_amr_fr);
+		SET_PREPROC_PC(params, osmo_thresh_comp, ci_amr_hr);
+		SET_PREPROC_PC(params, osmo_thresh_comp, ci_sdcch);
+		SET_PREPROC_PC(params, osmo_thresh_comp, ci_gprs);
+		#undef SET_PREPROC_PC
+	}
+
 	/* (TLV) Measurement Averaging parameters for RxLev/RxQual */
 	for (i = 0; i < ARRAY_SIZE(tp); i++) {
 		const struct ipac_preproc_ave_cfg *ave_cfg;
@@ -1008,6 +1051,42 @@
 		}
 	}
 
+	/* (TLV) Measurement Averaging parameters for C/I (Osmocom extension)*/
+	if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG, sizeof(struct osmo_preproc_ave_cfg))) {
+		ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG);
+		const struct osmo_preproc_ave_cfg *cfg = (const struct osmo_preproc_ave_cfg *) ie->val;
+		unsigned params_offset = 0;
+		#define SET_AVE_CFG(PARAMS, FROM, TYPE, PARAM_OFFSET) do {\
+				if ((FROM)->TYPE.ave_enabled) { \
+					(PARAMS)->TYPE##_meas.h_reqave = (FROM)->TYPE.h_reqave; \
+					(PARAMS)->TYPE##_meas.h_reqt = (FROM)->TYPE.h_reqt; \
+					(PARAMS)->TYPE##_meas.algo = (FROM)->TYPE.ave_method + 1; \
+					switch ((FROM)->TYPE.ave_method) { \
+					case IPAC_OSMO_EWMA_AVE: \
+						if (ie->len > sizeof(*cfg) + (PARAM_OFFSET)) {  \
+							(PARAMS)->TYPE##_meas.ewma.alpha = (FROM)->params[PARAM_OFFSET]; \
+							(PARAM_OFFSET)++; \
+						} \
+						break; \
+					/* FIXME: not implemented */ \
+					case IPAC_UNWEIGHTED_AVE: \
+					case IPAC_WEIGHTED_AVE: \
+					case IPAC_MEDIAN_AVE: \
+						break; \
+					} \
+				} else { \
+					(PARAMS)->TYPE##_meas.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE; \
+				} \
+			} while(0)
+		SET_AVE_CFG(params, cfg, ci_fr, params_offset);
+		SET_AVE_CFG(params, cfg, ci_hr, params_offset);
+		SET_AVE_CFG(params, cfg, ci_amr_fr, params_offset);
+		SET_AVE_CFG(params, cfg, ci_amr_hr, params_offset);
+		SET_AVE_CFG(params, cfg, ci_sdcch, params_offset);
+		SET_AVE_CFG(params, cfg, ci_gprs, params_offset);
+		#undef SET_AVE_CFG
+	}
+
 	return 0;
 }
 
diff --git a/src/common/vty.c b/src/common/vty.c
index 4f618d0..62e2d70 100644
--- a/src/common/vty.c
+++ b/src/common/vty.c
@@ -1391,7 +1391,7 @@
 }
 
 static void dump_dpc_params(struct vty *vty, const unsigned int indent,
-			    const struct gsm_power_ctrl_params *cp)
+			    const struct gsm_power_ctrl_params *cp, bool uplink)
 {
 	cfg_out(vty, "Power control interval: %u ms (every %u SACCH block(s))%s",
 		cp->ctrl_interval ? cp->ctrl_interval * 2 * 480 : 480,
@@ -1408,6 +1408,26 @@
 
 	cfg_out(vty, "RxQual measurement processing:%s", VTY_NEWLINE);
 	dump_dpc_meas_params(vty, indent + 2, &cp->rxqual_meas, "RXQUAL", 3);
+
+	if (uplink) {
+		cfg_out(vty, "C/I measurement processing (FR/EFR):%s", VTY_NEWLINE);
+		dump_dpc_meas_params(vty, indent + 2, &cp->ci_fr_meas, "CI_FR", 0);
+
+		cfg_out(vty, "C/I measurement processing (HR):%s", VTY_NEWLINE);
+		dump_dpc_meas_params(vty, indent + 2, &cp->ci_hr_meas, "CI_HR", 0);
+
+		cfg_out(vty, "C/I measurement processing (AMR-FR):%s", VTY_NEWLINE);
+		dump_dpc_meas_params(vty, indent + 2, &cp->ci_amr_fr_meas, "CI_AMR_FR", 0);
+
+		cfg_out(vty, "C/I measurement processing (AMR-HR):%s", VTY_NEWLINE);
+		dump_dpc_meas_params(vty, indent + 2, &cp->ci_amr_hr_meas, "CI_AMR_HR", 0);
+
+		cfg_out(vty, "C/I measurement processing (SDCCH):%s", VTY_NEWLINE);
+		dump_dpc_meas_params(vty, indent + 2, &cp->ci_sdcch_meas, "CI_SDCCH", 0);
+
+		cfg_out(vty, "C/I measurement processing (GPRS):%s", VTY_NEWLINE);
+		dump_dpc_meas_params(vty, indent + 2, &cp->ci_gprs_meas, "CI_GPRS", 0);
+	}
 }
 
 static void trx_dump_vty(struct vty *vty, const struct gsm_bts_trx *trx)
@@ -1425,13 +1445,13 @@
 		trx->bs_dpc_params == &trx->bts->bs_dpc_params ?
 			"fall-back" : "from BSC",
 		VTY_NEWLINE);
-	dump_dpc_params(vty, 4, trx->bs_dpc_params);
+	dump_dpc_params(vty, 4, trx->bs_dpc_params, false);
 
 	vty_out(vty, "  MS Power control parameters (%s):%s",
 		trx->ms_dpc_params == &trx->bts->ms_dpc_params ?
 			"fall-back" : "from BSC",
 		VTY_NEWLINE);
-	dump_dpc_params(vty, 4, trx->ms_dpc_params);
+	dump_dpc_params(vty, 4, trx->ms_dpc_params, true);
 
 	vty_out(vty, "  NM State: ");
 	net_dump_nmstate(vty, &trx->mo.nm_state);
@@ -1638,7 +1658,7 @@
 		return;
 
 	cfg_out(vty, "Power Control parameters:%s", VTY_NEWLINE);
-	dump_dpc_params(vty, indent + 2, st->dpc_params);
+	dump_dpc_params(vty, indent + 2, st->dpc_params, false);
 }
 
 static void lchan_ms_power_ctrl_state_dump(struct vty *vty, unsigned int indent,
@@ -1664,7 +1684,7 @@
 		return;
 
 	cfg_out(vty, "Power Control parameters:%s", VTY_NEWLINE);
-	dump_dpc_params(vty, indent + 2, st->dpc_params);
+	dump_dpc_params(vty, indent + 2, st->dpc_params, true);
 }
 
 static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
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..55092db 100644
--- a/tests/power/ms_power_loop_test.err
+++ b/tests/power/ms_power_loop_test.err
@@ -1,51 +1,60 @@
-(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, RSSI[curr -60, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 11 (8 dBm) => 9 (12 dBm): rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 9 (12 dBm) => 7 (16 dBm): rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 7 (16 dBm) => 5 (20 dBm): rx-ms-pwr-lvl 7, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(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, RSSI[curr -75, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 5 (20 dBm) => 3 (24 dBm): rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 3 (24 dBm) => 2 (26 dBm): rx-ms-pwr-lvl 3, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(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, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 2 (26 dBm) => 0 (30 dBm): rx-ms-pwr-lvl 2, max-ms-pwr-lvl 0, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(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, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 0 (30 dBm) => 30 (34 dBm): rx-ms-pwr-lvl 0, max-ms-pwr-lvl 29, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 30 (34 dBm) => 29 (36 dBm): rx-ms-pwr-lvl 30, max-ms-pwr-lvl 29, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(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, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(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, RSSI[curr -75, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 29 (36 dBm) => 30 (34 dBm): rx-ms-pwr-lvl 29, max-ms-pwr-lvl 29, RSSI[curr -45, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 30 (34 dBm) => 31 (32 dBm): rx-ms-pwr-lvl 30, max-ms-pwr-lvl 29, RSSI[curr -45, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 31 (32 dBm) => 0 (30 dBm): rx-ms-pwr-lvl 31, max-ms-pwr-lvl 29, RSSI[curr -45, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 0 (30 dBm) => 1 (28 dBm): rx-ms-pwr-lvl 0, max-ms-pwr-lvl 29, RSSI[curr -73, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 1 (28 dBm) => 0 (30 dBm): rx-ms-pwr-lvl 1, max-ms-pwr-lvl 29, RSSI[curr -77, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 0 (30 dBm) => 14 (2 dBm): rx-ms-pwr-lvl 0, max-ms-pwr-lvl 14, RSSI[curr -73, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 14 (2 dBm) => 15 (0 dBm): rx-ms-pwr-lvl 14, max-ms-pwr-lvl 0, RSSI[curr -40, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(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, RSSI[curr -75, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (3 dBm): rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 11 (8 dBm) => 9 (11 dBm): rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, RSSI[curr -70, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(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, RSSI[curr -50, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(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, RSSI[curr -50, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, RSSI[curr -110, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(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, RSSI[curr -75, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(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, RSSI[curr -72, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(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, RSSI[curr -78, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(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, RSSI[curr -75, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(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, RSSI[curr -70, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(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, RSSI[curr -80, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, RSSI[curr -85, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 11 (8 dBm) => 9 (12 dBm): rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 9 (12 dBm) => 7 (16 dBm): rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 7 (16 dBm) => 5 (20 dBm): rx-ms-pwr-lvl 7, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 5 (20 dBm) => 3 (24 dBm): rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 3 (24 dBm) => 2 (26 dBm): rx-ms-pwr-lvl 3, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(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, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 11 (8 dBm) => 9 (12 dBm): rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 9 (12 dBm) => 7 (16 dBm): rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, RSSI[curr -90, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(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, RSSI[curr -60, tgt -75] dBm, C/I[curr 14, tgt 14] dB)
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, RSSI[curr -100, tgt -75] dBm, C/I[curr 11, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, RSSI[curr -100, tgt -75] dBm, C/I[curr 11, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 11 (8 dBm) => 12 (6 dBm): rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, RSSI[curr -60, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 12 (6 dBm) => 13 (4 dBm): rx-ms-pwr-lvl 12, max-ms-pwr-lvl 2, RSSI[curr -60, tgt -75] dBm, C/I[curr 17, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, RSSI[curr -100, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 11 (8 dBm) => 12 (6 dBm): rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, RSSI[curr -100, tgt -75] dBm, C/I[curr 17, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 12 (6 dBm) => 13 (4 dBm): rx-ms-pwr-lvl 12, max-ms-pwr-lvl 2, RSSI[curr -60, tgt -75] dBm, C/I[curr 14, tgt 14] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 13 (4 dBm) => 14 (2 dBm): rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, RSSI[curr -60, tgt -75] dBm, C/I[curr 17, tgt 14] dB
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: 7
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: osmith <osmith at sysmocom.de>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-CC: dexter <pmaier at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210906/cbd4a9c5/attachment.htm>


More information about the gerrit-log mailing list