move MS power control handling from sysmobts to common part
MS uplink power control is required in pretty much any BTS, and we cannot assume that they PHY / L1 will always take care of it by itself. So the correspondign code is moved to common/power_control.c and called from the generic part of L1SAP. The corresponding VTY paramter has been moved from the sysmobts-specific trx VTY node to the common BTS VTY node.
This commit is contained in:
parent
f449842053
commit
819b50e1a7
|
@ -1,3 +1,4 @@
|
|||
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
|
||||
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
|
||||
handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h
|
||||
handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h \
|
||||
power_control.h
|
||||
|
|
|
@ -84,6 +84,8 @@ struct gsm_bts_role_bts {
|
|||
struct gsm_time gsm_time;
|
||||
uint8_t radio_link_timeout;
|
||||
|
||||
int ul_power_target; /* Uplink Rx power target */
|
||||
|
||||
/* used by the sysmoBTS to adjust band */
|
||||
uint8_t auto_band;
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
|
||||
const uint8_t ms_power, const int rxLevel);
|
|
@ -8,4 +8,4 @@ libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
|
|||
load_indication.c pcu_sock.c handover.c msg_utils.c \
|
||||
load_indication.c pcu_sock.c handover.c msg_utils.c \
|
||||
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
|
||||
l1sap.c cbch.c
|
||||
l1sap.c cbch.c power_control.c
|
||||
|
|
|
@ -99,6 +99,7 @@ int bts_init(struct gsm_bts *bts)
|
|||
|
||||
/* configurable via VTY */
|
||||
btsb->paging_state = paging_init(btsb, 200, 0);
|
||||
btsb->ul_power_target = -75; /* dBm default */
|
||||
|
||||
/* configurable via OML */
|
||||
btsb->load.ccch.load_ind_period = 112;
|
||||
|
|
|
@ -783,6 +783,8 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
|
|||
lchan->meas.l1_info[0] |= ((data[0] >> 5) & 1) << 2;
|
||||
lchan->meas.l1_info[1] = data[1];
|
||||
lchan->meas.flags |= LC_UL_M_F_L1_VALID;
|
||||
|
||||
lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi);
|
||||
} else
|
||||
le = &lchan->lapdm_ch.lapdm_dcch;
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/* MS Power Control Loop L1 */
|
||||
|
||||
/* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/measurement.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
/*
|
||||
* Check if manual power control is needed
|
||||
* Check if fixed power was selected
|
||||
* Check if the MS is already using our level if not
|
||||
* the value is bogus..
|
||||
* TODO: Add a timeout.. e.g. if the ms is not capable of reaching
|
||||
* the value we have set.
|
||||
*/
|
||||
int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
|
||||
const uint8_t ms_power, const int rxLevel)
|
||||
{
|
||||
int rx;
|
||||
int cur_dBm, new_dBm, new_pwr;
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
const enum gsm_band band = bts->band;
|
||||
|
||||
if (!trx_ms_pwr_ctrl_is_osmo(lchan->ts->trx))
|
||||
return 0;
|
||||
if (lchan->ms_power_ctrl.fixed)
|
||||
return 0;
|
||||
|
||||
/* The phone hasn't reached the power level yet */
|
||||
if (lchan->ms_power_ctrl.current != ms_power)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* What is the difference between what we want and received?
|
||||
* Ignore a margin that is within the range of measurement
|
||||
* and MS output issues.
|
||||
*/
|
||||
rx = btsb->ul_power_target - rxLevel;
|
||||
if (rx >= 0 && rx < 1)
|
||||
return 0;
|
||||
if (rx < 0 && rx > -1)
|
||||
return 0;
|
||||
|
||||
/* We don't really care about the truncation of int + float */
|
||||
cur_dBm = ms_pwr_dbm(band, ms_power);
|
||||
new_dBm = cur_dBm + rx;
|
||||
|
||||
/* Clamp negative values and do it depending on the band */
|
||||
if (new_dBm < 0)
|
||||
new_dBm = 0;
|
||||
|
||||
switch (band) {
|
||||
case GSM_BAND_1800:
|
||||
/* If MS_TX_PWR_MAX_CCH is set the values 29,
|
||||
* 30, 31 are not used. Avoid specifying a dBm
|
||||
* that would lead to these power levels. The
|
||||
* phone might not be able to reach them. */
|
||||
if (new_dBm > 30)
|
||||
new_dBm = 30;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
new_pwr = ms_pwr_ctl_lvl(band, new_dBm);
|
||||
if (lchan->ms_power_ctrl.current != new_pwr) {
|
||||
lchan->ms_power_ctrl.current = new_pwr;
|
||||
bts_model_adjst_ms_pwr(lchan);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -207,6 +207,7 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
|||
VTY_NEWLINE);
|
||||
vty_out(vty, " paging lifetime %u%s", paging_get_lifetime(btsb->paging_state),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " uplink-power-target %d%s", btsb->ul_power_target, VTY_NEWLINE);
|
||||
if (btsb->agch_queue_thresh_level != GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT
|
||||
|| btsb->agch_queue_low_level != GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT
|
||||
|| btsb->agch_queue_high_level != GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT)
|
||||
|
@ -441,6 +442,19 @@ DEFUN(cfg_bts_agch_queue_mgmt_default,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_ul_power_target, cfg_bts_ul_power_target_cmd,
|
||||
"uplink-power-target <-110-0>",
|
||||
"Set the nominal target Rx Level for uplink power control loop\n"
|
||||
"Target uplink Rx level in dBm\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
btsb->ul_power_target = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DB_DBM_STR \
|
||||
"Unit is dB (decibels)\n" \
|
||||
"Unit is mdB (milli-decibels, or rather 1/10000 bel)\n"
|
||||
|
@ -782,6 +796,7 @@ int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat)
|
|||
install_element(BTS_NODE, &cfg_bts_paging_lifetime_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_default_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_params_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_ul_power_target_cmd);
|
||||
|
||||
install_element(BTS_NODE, &cfg_trx_gsmtap_sapi_cmd);
|
||||
install_element(BTS_NODE, &cfg_trx_no_gsmtap_sapi_cmd);
|
||||
|
|
|
@ -868,18 +868,6 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
|
|||
data_ind->msgUnitParam.u8Size));
|
||||
dump_meas_res(LOGL_DEBUG, &data_ind->measParam);
|
||||
|
||||
switch (data_ind->sapi) {
|
||||
case GsmL1_Sapi_Sacch:
|
||||
/*
|
||||
* Handle power control
|
||||
*/
|
||||
l1if_ms_pwr_ctrl(lchan, fl1->ul_power_target,
|
||||
data_ind->msgUnitParam.u8Buffer[0] & 0x1f,
|
||||
data_ind->measParam.fRssi);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* check for TCH */
|
||||
if (data_ind->sapi == GsmL1_Sapi_TchF
|
||||
|| data_ind->sapi == GsmL1_Sapi_TchH) {
|
||||
|
@ -905,6 +893,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
|
|||
l1sap = msgb_l1sap_prim(l1p_msg);
|
||||
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA,
|
||||
PRIM_OP_INDICATION, l1p_msg);
|
||||
l1sap->u.data.rssi = data_ind->measParam.fRssi;
|
||||
l1sap->u.data.link_id = link_id;
|
||||
l1sap->u.data.chan_nr = chan_nr;
|
||||
l1sap->u.data.fn = fn;
|
||||
|
@ -1515,7 +1504,6 @@ struct femtol1_hdl *l1if_open(void *priv)
|
|||
fl1h->priv = priv;
|
||||
fl1h->clk_cal = 0;
|
||||
fl1h->clk_use_eeprom = 1;
|
||||
fl1h->ul_power_target = -75; /* dBm default */
|
||||
fl1h->min_qual_rach = MIN_QUAL_RACH;
|
||||
fl1h->min_qual_norm = MIN_QUAL_NORM;
|
||||
get_hwinfo_eeprom(fl1h);
|
||||
|
@ -1675,69 +1663,3 @@ int l1if_rf_clock_info_correct(struct femtol1_hdl *fl1h)
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check if manual power control is needed
|
||||
* Check if fixed power was selected
|
||||
* Check if the MS is already using our level if not
|
||||
* the value is bogus..
|
||||
* TODO: Add a timeout.. e.g. if the ms is not capable of reaching
|
||||
* the value we have set.
|
||||
*/
|
||||
inline int l1if_ms_pwr_ctrl(struct gsm_lchan *lchan, const int ul_power_target,
|
||||
const uint8_t ms_power, const float rxLevel)
|
||||
{
|
||||
float rx;
|
||||
int cur_dBm, new_dBm, new_pwr;
|
||||
const enum gsm_band band = lchan->ts->trx->bts->band;
|
||||
|
||||
if (!trx_ms_pwr_ctrl_is_osmo(lchan->ts->trx))
|
||||
return 0;
|
||||
if (lchan->ms_power_ctrl.fixed)
|
||||
return 0;
|
||||
|
||||
/* The phone hasn't reached the power level yet */
|
||||
if (lchan->ms_power_ctrl.current != ms_power)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* What is the difference between what we want and received?
|
||||
* Ignore a margin that is within the range of measurement
|
||||
* and MS output issues.
|
||||
*/
|
||||
rx = ul_power_target - rxLevel;
|
||||
if (rx >= 0 && rx < 1.5f)
|
||||
return 0;
|
||||
if (rx < 0 && rx > -1.5f)
|
||||
return 0;
|
||||
|
||||
/* We don't really care about the truncation of int + float */
|
||||
cur_dBm = ms_pwr_dbm(band, ms_power);
|
||||
new_dBm = cur_dBm + rx;
|
||||
|
||||
/* Clamp negative values and do it depending on the band */
|
||||
if (new_dBm < 0)
|
||||
new_dBm = 0;
|
||||
|
||||
switch (band) {
|
||||
case GSM_BAND_1800:
|
||||
/* If MS_TX_PWR_MAX_CCH is set the values 29,
|
||||
* 30, 31 are not used. Avoid specifying a dBm
|
||||
* that would lead to these power levels. The
|
||||
* phone might not be able to reach them. */
|
||||
if (new_dBm > 30)
|
||||
new_dBm = 30;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
new_pwr = ms_pwr_ctl_lvl(band, new_dBm);
|
||||
if (lchan->ms_power_ctrl.current != new_pwr) {
|
||||
lchan->ms_power_ctrl.current = new_pwr;
|
||||
bts_model_adjst_ms_pwr(lchan);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ struct femtol1_hdl {
|
|||
uint32_t dsp_trace_f;
|
||||
uint8_t clk_use_eeprom;
|
||||
int clk_cal;
|
||||
int ul_power_target;
|
||||
uint8_t clk_src;
|
||||
float min_qual_rach;
|
||||
float min_qual_norm;
|
||||
|
|
|
@ -324,6 +324,7 @@ static const uint8_t trx_rqd_attr[] = { NM_ATT_RF_MAXPOWR_R };
|
|||
static int trx_init(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(trx->bts);
|
||||
struct msgb *msg;
|
||||
GsmL1_MphInitReq_t *mi_req;
|
||||
GsmL1_DeviceParam_t *dev_par;
|
||||
|
@ -352,7 +353,7 @@ static int trx_init(struct gsm_bts_trx *trx)
|
|||
dev_par->u16BcchArfcn = trx->bts->c0->arfcn;
|
||||
dev_par->u8NbTsc = trx->bts->bsic & 7;
|
||||
dev_par->fRxPowerLevel = trx_ms_pwr_ctrl_is_osmo(trx)
|
||||
? 0.0 : fl1h->ul_power_target;
|
||||
? 0.0 : btsb->ul_power_target;
|
||||
|
||||
dev_par->fTxPowerLevel = 0.0;
|
||||
LOGP(DL1C, LOGL_NOTICE, "Init TRX (ARFCN %u, TSC %u, RxPower % 2f dBm, "
|
||||
|
|
|
@ -175,15 +175,15 @@ DEFUN(cfg_trx_cal_path, cfg_trx_cal_path_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_ul_power_target, cfg_trx_ul_power_target_cmd,
|
||||
DEFUN_DEPRECATED(cfg_trx_ul_power_target, cfg_trx_ul_power_target_cmd,
|
||||
"uplink-power-target <-110-0>",
|
||||
"Set the nominal target Rx Level for uplink power control loop\n"
|
||||
"Obsolete alias for bts uplink-power-target\n"
|
||||
"Target uplink Rx level in dBm\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(trx->bts);
|
||||
|
||||
fl1h->ul_power_target = atoi(argv[0]);
|
||||
btsb->ul_power_target = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -484,8 +484,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
|
|||
vty_out(vty, " clock-source %s%s",
|
||||
get_value_string(femtobts_clksrc_names, fl1h->clk_src),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " uplink-power-target %d%s", fl1h->ul_power_target,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " min-qual-rach %.0f%s", fl1h->min_qual_rach * 10.0f,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " min-qual-norm %.0f%s", fl1h->min_qual_norm * 10.0f,
|
||||
|
@ -536,7 +534,6 @@ int bts_model_vty_init(struct gsm_bts *bts)
|
|||
install_element(TRX_NODE, &cfg_trx_clkcal_def_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_clksrc_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_cal_path_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_ul_power_target_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_min_qual_rach_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_min_qual_norm_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
#include <osmo-bts/power_control.h>
|
||||
|
||||
#include "femtobts.h"
|
||||
#include "l1_if.h"
|
||||
|
@ -186,12 +187,14 @@ static void test_sysmobts_cipher(void)
|
|||
static void test_sysmobts_loop(void)
|
||||
{
|
||||
struct gsm_bts bts;
|
||||
struct gsm_bts_role_bts btsb;
|
||||
struct gsm_bts_trx trx;
|
||||
struct gsm_bts_trx_ts ts;
|
||||
struct gsm_lchan *lchan;
|
||||
int ret;
|
||||
|
||||
memset(&bts, 0, sizeof(bts));
|
||||
memset(&btsb, 0, sizeof(btsb));
|
||||
memset(&trx, 0, sizeof(trx));
|
||||
memset(&ts, 0, sizeof(ts));
|
||||
|
||||
|
@ -199,8 +202,10 @@ static void test_sysmobts_loop(void)
|
|||
lchan->ts = &ts;
|
||||
ts.trx = &trx;
|
||||
trx.bts = &bts;
|
||||
bts.role = &btsb;
|
||||
bts.band = GSM_BAND_1800;
|
||||
trx.ms_power_control = 1;
|
||||
btsb.ul_power_target = -75;
|
||||
|
||||
printf("Testing sysmobts power control\n");
|
||||
|
||||
|
@ -208,7 +213,7 @@ static void test_sysmobts_loop(void)
|
|||
lchan->state = LCHAN_S_NONE;
|
||||
lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
|
||||
ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -60);
|
||||
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -60);
|
||||
OSMO_ASSERT(ret == 0);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
|
||||
|
||||
|
@ -217,24 +222,24 @@ static void test_sysmobts_loop(void)
|
|||
* Now 15 dB too little and we should power it up. Could be a
|
||||
* power level of 7 or 8 for 15 dBm
|
||||
*/
|
||||
ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -90);
|
||||
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -90);
|
||||
OSMO_ASSERT(ret == 1);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 7);
|
||||
|
||||
/* It should be clamped to level 0 and 30 dBm */
|
||||
ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -100);
|
||||
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -100);
|
||||
OSMO_ASSERT(ret == 1);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 0);
|
||||
|
||||
/* Fix it and jump down */
|
||||
lchan->ms_power_ctrl.fixed = 1;
|
||||
ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -60);
|
||||
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -60);
|
||||
OSMO_ASSERT(ret == 0);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 0);
|
||||
|
||||
/* And leave it again */
|
||||
lchan->ms_power_ctrl.fixed = 0;
|
||||
ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -40);
|
||||
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -40);
|
||||
OSMO_ASSERT(ret == 1);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue