aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/l1sap.c1
-rw-r--r--src/common/power_control.c102
-rw-r--r--src/common/rsl.c52
-rw-r--r--src/common/scheduler.c2
-rw-r--r--src/common/tx_power.c4
-rw-r--r--src/common/vty.c4
6 files changed, 145 insertions, 20 deletions
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index 2038fbad..33d10a5a 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -1546,6 +1546,7 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
lchan->meas.flags |= LC_UL_M_F_L1_VALID;
lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi);
+ 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 34414392..4c4e2831 100644
--- a/src/common/power_control.c
+++ b/src/common/power_control.c
@@ -209,3 +209,105 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
return 1;
}
+
+ /*! compute the new Downlink attenuation value for the given logical channel.
+ * \param lchan logical channel for which to compute (and in which to store) new power value.
+ * \param[in] gh pointer to the beginning of (presumably) a Measurement Report.
+ */
+int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
+ const struct gsm48_hdr *gh)
+{
+ struct gsm_bts_trx *trx = lchan->ts->trx;
+ struct gsm_bts *bts = trx->bts;
+ uint8_t rxqual_full, rxqual_sub;
+ uint8_t rxlev_full, rxlev_sub;
+ uint8_t rxqual, rxlev;
+ int delta, new;
+
+ const struct bts_power_ctrl_params *params = &bts->dl_power_ctrl;
+ struct lchan_power_ctrl_state *state = &lchan->bs_power_ctrl;
+
+ /* Check if BS Power Control is enabled */
+ if (state->fixed)
+ return 0;
+ /* Check if this is a Measurement Report */
+ if (gh->proto_discr != GSM48_PDISC_RR)
+ return 0;
+ if (gh->msg_type != GSM48_MT_RR_MEAS_REP)
+ return 0;
+
+ /* Check if the measurement results are valid */
+ if ((gh->data[1] & 0x40) == 0x40) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG,
+ "The measurement results are not valid\n");
+ return 0;
+ }
+
+ /* See 3GPP TS 44.018, section 10.5.2.20 */
+ rxqual_full = (gh->data[2] >> 4) & 0x7;
+ rxqual_sub = (gh->data[2] >> 1) & 0x7;
+
+ rxlev_full = gh->data[0] & 0x3f;
+ rxlev_sub = gh->data[1] & 0x3f;
+
+ LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Rx DL Measurement Report: "
+ "RXLEV-FULL(%02u), RXQUAL-FULL(%u), "
+ "RXLEV-SUB(%02u), RXQUAL-SUB(%u), "
+ "DTx is %s => using %s\n",
+ rxlev_full, rxqual_full, rxlev_sub, rxqual_sub,
+ lchan->tch.dtx.dl_active ? "enabled" : "disabled",
+ lchan->tch.dtx.dl_active ? "SUB" : "FULL");
+
+ /* If DTx is active on Downlink, use the '-SUB' */
+ if (lchan->tch.dtx.dl_active) {
+ rxqual = rxqual_sub;
+ rxlev = rxlev_sub;
+ } else { /* ... otherwise use the '-FULL' */
+ rxqual = rxqual_full;
+ rxlev = rxlev_full;
+ }
+
+ /* Bit Error Rate > 0 => reduce by 2 */
+ if (rxqual > 0) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Reducing Downlink attenuation "
+ "by half: %u -> %u dB due to RXQUAL %u > 0\n",
+ state->current, state->current / 2, rxqual);
+ state->current /= 2;
+ return 1;
+ }
+
+ /* Calculate a 'delta' for the current attenuation level */
+ delta = calc_delta(params, state, rxlev2dbm(rxlev));
+
+ /* Basic signal transmission / reception formula:
+ *
+ * RxLev = TxPwr - (PathLoss + TxAtt)
+ *
+ * Here we want to change RxLev at the MS side, so:
+ *
+ * RxLev + Delta = TxPwr - (PathLoss + TxAtt) + Delta
+ *
+ * The only parameter we can change here is TxAtt, so:
+ *
+ * RxLev + Delta = TxPwr - PathLoss - TxAtt + Delta
+ * RxLev + Delta = TxPwr - PathLoss - (TxAtt - Delta)
+ */
+ new = state->current - delta;
+ if (new > state->max)
+ new = state->max;
+ if (new < 0)
+ new = 0;
+
+ if (state->current != new) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Changing Downlink attenuation: "
+ "%u -> %u dB (maximum %u dB, target %d dBm, delta %d dB)\n",
+ state->current, new, state->max, params->target, delta);
+ state->current = new;
+ return 1;
+ } else {
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping Downlink attenuation "
+ "at %u dB (maximum %u dB, target %d dBm, delta %d dB)\n",
+ state->current, state->max, params->target, delta);
+ return 0;
+ }
+}
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 8760c242..2ebfb323 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -1036,8 +1036,8 @@ static void clear_lchan_for_pdch_activ(struct gsm_lchan *lchan)
lchan->tch_mode = 0;
memset(&lchan->encr, 0, sizeof(lchan->encr));
memset(&lchan->ho, 0, sizeof(lchan->ho));
- lchan->bs_power_red = 0;
memset(&lchan->ms_power_ctrl, 0, sizeof(lchan->ms_power_ctrl));
+ memset(&lchan->bs_power_ctrl, 0, sizeof(lchan->bs_power_ctrl));
lchan->rqd_ta = 0;
copy_sacch_si_to_lchan(lchan);
memset(&lchan->tch, 0, sizeof(lchan->tch));
@@ -1153,11 +1153,16 @@ static int rsl_rx_chan_activ(struct msgb *msg)
LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, "rx Channel Activation in state: %s.\n",
gsm_lchans_name(lchan->state));
- /* Initialize channel defaults */
+ /* Initialize MS Power Control defaults */
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0);
lchan->ms_power_ctrl.current = lchan->ms_power_ctrl.max;
lchan->ms_power_ctrl.fixed = true;
+ /* Initialize BS Power Control defaults */
+ lchan->bs_power_ctrl.max = 2 * 15;
+ lchan->bs_power_ctrl.current = 0;
+ lchan->bs_power_ctrl.fixed = true;
+
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
/* 9.3.3 Activation Type */
@@ -1209,9 +1214,11 @@ static int rsl_rx_chan_activ(struct msgb *msg)
return rsl_tx_chan_act_nack(lchan, RSL_ERR_SERV_OPT_UNIMPL);
}
- lchan->bs_power_red = BS_POWER2DB(*TLVP_VAL(&tp, RSL_IE_BS_POWER));
+ lchan->bs_power_ctrl.max = BS_POWER2DB(*TLVP_VAL(&tp, RSL_IE_BS_POWER));
+ lchan->bs_power_ctrl.current = lchan->bs_power_ctrl.max;
+
LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, "BS Power attenuation %u dB\n",
- lchan->bs_power_red);
+ lchan->bs_power_ctrl.current);
}
/* 9.3.13 MS Power */
@@ -1224,7 +1231,6 @@ static int rsl_rx_chan_activ(struct msgb *msg)
if (TLVP_PRES_LEN(&tp, RSL_IE_TIMING_ADVANCE, 1))
lchan->rqd_ta = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE);
- /* 9.3.32 BS Power Parameters */
/* 9.3.31 MS Power Parameters */
if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER_PARAM)) {
/* Spec explicitly states BTS should only perform
@@ -1232,6 +1238,14 @@ static int rsl_rx_chan_activ(struct msgb *msg)
* Parameters' IE is present! */
lchan->ms_power_ctrl.fixed = false;
}
+
+ /* 9.3.32 BS Power Parameters */
+ if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER_PARAM)) {
+ /* NOTE: it's safer to start from 0 */
+ lchan->bs_power_ctrl.current = 0;
+ lchan->bs_power_ctrl.fixed = false;
+ }
+
/* 9.3.16 Physical Context */
/* 9.3.29 SACCH Information */
@@ -1753,7 +1767,7 @@ static int rsl_rx_bs_pwr_ctrl(struct msgb *msg)
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
struct tlv_parsed tp;
- uint8_t old_bs_power_red;
+ uint8_t old, new;
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
@@ -1766,18 +1780,24 @@ static int rsl_rx_bs_pwr_ctrl(struct msgb *msg)
return rsl_tx_error_report(msg->trx, RSL_ERR_SERV_OPT_UNIMPL, &dch->chan_nr, NULL, msg);
}
- old_bs_power_red = lchan->bs_power_red;
- lchan->bs_power_red = BS_POWER2DB(*TLVP_VAL(&tp, RSL_IE_BS_POWER));
-
- LOGPLCHAN(lchan, DRSL, LOGL_INFO, "BS POWER CONTROL Attenuation %d -> %d dB\n",
- old_bs_power_red, lchan->bs_power_red);
+ new = BS_POWER2DB(*TLVP_VAL(&tp, RSL_IE_BS_POWER));
+ old = lchan->bs_power_ctrl.current;
/* 9.3.31 MS Power Parameters (O) */
if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER_PARAM)) {
- /* Spec explicitly states BTS should perform autonomous
- * BS power control loop in BTS if 'BS Power Parameters'
- * IE is present! WE don't support that. */
- return rsl_tx_error_report(msg->trx, RSL_ERR_OPT_IE_ERROR, &dch->chan_nr, NULL, msg);
+ /* NOTE: it's safer to start from 0 */
+ lchan->bs_power_ctrl.current = 0;
+ lchan->bs_power_ctrl.max = new;
+ lchan->bs_power_ctrl.fixed = false;
+ } else {
+ lchan->bs_power_ctrl.current = new;
+ lchan->bs_power_ctrl.fixed = true;
+ }
+
+ if (lchan->bs_power_ctrl.current != old) {
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "BS POWER CONTROL: "
+ "attenuation change %u -> %u dB\n",
+ old, lchan->bs_power_ctrl.current);
}
return 0;
@@ -2975,7 +2995,7 @@ int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const stru
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res);
lchan->meas.flags &= ~LC_UL_M_F_RES_VALID;
}
- msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power_red / 2);
+ msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power_ctrl.current / 2);
if (lchan->meas.flags & LC_UL_M_F_L1_VALID) {
msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, 2, lchan->meas.l1_info);
lchan->meas.flags &= ~LC_UL_M_F_L1_VALID;
diff --git a/src/common/scheduler.c b/src/common/scheduler.c
index 84918e31..3d780fdd 100644
--- a/src/common/scheduler.c
+++ b/src/common/scheduler.c
@@ -1237,7 +1237,7 @@ void _sched_dl_burst(struct l1sched_trx *l1t, struct trx_dl_burst_req *br)
/* BS Power reduction (in dB) per logical channel */
if (l1cs->lchan != NULL)
- br->att = l1cs->lchan->bs_power_red;
+ br->att = l1cs->lchan->bs_power_ctrl.current;
/* encrypt */
if (br->burst_len && l1cs->dl_encr_algo) {
diff --git a/src/common/tx_power.c b/src/common/tx_power.c
index 0741429f..348aba5c 100644
--- a/src/common/tx_power.c
+++ b/src/common/tx_power.c
@@ -68,7 +68,7 @@ int get_p_target_mdBm(const struct gsm_bts_trx *trx, uint8_t bs_power_red)
}
int get_p_target_mdBm_lchan(const struct gsm_lchan *lchan)
{
- return get_p_target_mdBm(lchan->ts->trx, lchan->bs_power_red);
+ return get_p_target_mdBm(lchan->ts->trx, lchan->bs_power_ctrl.current);
}
/* calculate the actual total output power required, taking into account the
@@ -134,7 +134,7 @@ int get_p_trxout_target_mdBm(const struct gsm_bts_trx *trx, uint8_t bs_power_red
}
int get_p_trxout_target_mdBm_lchan(const struct gsm_lchan *lchan)
{
- return get_p_trxout_target_mdBm(lchan->ts->trx, lchan->bs_power_red);
+ return get_p_trxout_target_mdBm(lchan->ts->trx, lchan->bs_power_ctrl.current);
}
diff --git a/src/common/vty.c b/src/common/vty.c
index f32f6cdf..5d21e584 100644
--- a/src/common/vty.c
+++ b/src/common/vty.c
@@ -1335,10 +1335,13 @@ static void lchan_dump_full_vty(struct vty *vty, const struct gsm_lchan *lchan)
lchan->state == LCHAN_S_BROKEN ? " Error reason: " : "",
lchan->state == LCHAN_S_BROKEN ? lchan->broken_reason : "",
VTY_NEWLINE);
+#if 0
+ /* TODO: print more info about MS/BS Power Control */
vty_out(vty, " BS Power: %d dBm, MS Power: %u dBm%s",
lchan->ts->trx->nominal_power - (lchan->ts->trx->max_power_red + lchan->bs_power_red),
ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power_ctrl.max),
VTY_NEWLINE);
+#endif
vty_out(vty, " Channel Mode / Codec: %s%s",
gsm48_chan_mode_name(lchan->tch_mode),
VTY_NEWLINE);
@@ -1380,7 +1383,6 @@ static void lchan_dump_full_vty(struct vty *vty, const struct gsm_lchan *lchan)
if (lchan->loopback)
vty_out(vty, " RTP/PDCH Loopback Enabled%s", VTY_NEWLINE);
vty_out(vty, " Radio Link Failure Counter 'S': %d%s", lchan->s, VTY_NEWLINE);
- /* TODO: MS Power Control */
}
static void lchan_dump_short_vty(struct vty *vty, const struct gsm_lchan *lchan)