aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2014-08-22 02:46:15 +0200
committerHarald Welte <laforge@gnumonks.org>2014-08-24 10:46:21 +0200
commite43feaf231e08f108aafa26a7829820fad3447cb (patch)
tree06b0fe4d89e8c2c4194dcb7e1ae0378b48dae1fc
parentfcca2e82184f8ece6a31db48abd77d560065b31f (diff)
New generic transmit power handling
In order to support transmit power reduction by thermal management as well as the variety of new internal / external PA configurations of BTSs, we need a slightly more complex system. Also, as at high power a single dB can be quite a big difference, we are now doing all computations in milli-dB(m), i.e. 1/10000 bel. Ramping is now used both for up and down ramping, as that is useful in cases where you want to gracefully shut down a cell by shrinking its radius, gradually handing over subscribers to neighboring cells. Furthermore, this code is becoming part of the 'common' codebase, as it is not really specific to how sysmobts is working. The user can specify a single aggregate value for external system gain/attenuation. Let's say you have 1dB loss of antenna cable, so you can put that as 'user-gain -1' into the config, which means that a 'transmit power of 20dBm' will be compensatet for that and the TRX is instructed to output 21dBm to compensate the cable loss. Similarly, external PAs can be described by a positive user-gain. One of the next steps will be to communicate those values and the nominal power capability of the specific BTS to the BSC, so the BSC will automatically show correct signal levels in the VTY and log files. The code includes provisions for future extensions regarding * an external and an internal PA with calibration tables * a thermal attenuation setting to be controlled by the site manager
-rw-r--r--configure.ac4
-rw-r--r--include/osmo-bts/Makefile.am2
-rw-r--r--include/osmo-bts/bts_model.h2
-rw-r--r--include/osmo-bts/gsm_data.h1
-rw-r--r--include/osmo-bts/tx_power.h74
-rw-r--r--src/common/Makefile.am4
-rw-r--r--src/common/bts.c6
-rw-r--r--src/common/tx_power.c273
-rw-r--r--src/common/vty.c82
-rw-r--r--src/osmo-bts-sysmo/main.c6
-rw-r--r--src/osmo-bts-sysmo/oml.c21
-rw-r--r--src/osmo-bts-sysmo/sysmobts_vty.c48
-rw-r--r--src/osmo-bts-sysmo/utils.c84
13 files changed, 455 insertions, 152 deletions
diff --git a/configure.ac b/configure.ac
index 2d08b87..8708548 100644
--- a/configure.ac
+++ b/configure.ac
@@ -45,10 +45,10 @@ AC_ARG_WITH([openbsc],
AC_SUBST([OPENBSC_INCDIR], $openbsc_incdir)
oldCPPFLAGS=$CPPFLAGS
-CPPFLAGS="$CPPFLAGS -I$OPENBSC_INCDIR $LIBOSMOCORE_CFLAGS"
+CPPFLAGS="$CPPFLAGS -I$OPENBSC_INCDIR -I$srcdir/include $LIBOSMOCORE_CFLAGS"
AC_CHECK_HEADER([openbsc/gsm_data_shared.h],[],
[AC_MSG_ERROR([openbsc/gsm_data_shared.h can not be found in $openbsc_incdir])],
- [])
+ [#include <osmo-bts/tx_power.h>])
CPPFLAGS=$oldCPPFLAGS
# Check for the sbts2050_header.h that was added after the 3.6 release
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index 68d63e0..b294144 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -1,3 +1,3 @@
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
+ handover.h msg_utils.h tx_power.h
diff --git a/include/osmo-bts/bts_model.h b/include/osmo-bts/bts_model.h
index 2641db7..200d02d 100644
--- a/include/osmo-bts/bts_model.h
+++ b/include/osmo-bts/bts_model.h
@@ -46,4 +46,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx);
int bts_model_oml_estab(struct gsm_bts *bts);
+int bts_model_change_power(struct gsm_bts_trx *trx, int p_trxout_mdBm);
+
#endif
diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h
index c7a0fc6..5e0af77 100644
--- a/include/osmo-bts/gsm_data.h
+++ b/include/osmo-bts/gsm_data.h
@@ -6,6 +6,7 @@
#include <osmocom/gsm/lapdm.h>
#include <osmo-bts/paging.h>
+#include <osmo-bts/tx_power.h>
#define GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT 41
#define GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE 999999
diff --git a/include/osmo-bts/tx_power.h b/include/osmo-bts/tx_power.h
new file mode 100644
index 0000000..c5d6f2b
--- /dev/null
+++ b/include/osmo-bts/tx_power.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/timer.h>
+
+/* our unit is 'milli dB" or "milli dBm", i.e. 1/1000 of a dB(m) */
+#define to_mdB(x) (x * 1000)
+
+/* PA calibration table */
+struct pa_calibration {
+ int gain_mdB[1024]; /* gain provided at given ARFCN */
+ /* FIXME: thermal calibration */
+};
+
+/* representation of a RF power amplifier */
+struct power_amp {
+ /* nominal gain of the PA */
+ int nominal_gain_mdB;
+ /* table with calibrated actual gain for each ARFCN */
+ struct pa_calibration calib;
+};
+
+/* Transmit power related parameters of a transceiver */
+struct trx_power_params {
+ /* specified maximum output of TRX at full power, has to be
+ * initialized by BTS model at startup*/
+ int trx_p_max_out_mdBm;
+
+ /* intended current total system output power */
+ int p_total_tgt_mdBm;
+
+ /* actual current total system output power, filled in by tx_power code */
+ int p_total_cur_mdBm;
+
+ /* current temporary attenuation due to thermal management,
+ * set by thermal management code via control interface */
+ int thermal_attenuation_mdB;
+
+ /* external gain (+) or attenuation (-) added by the user, configured
+ * by the user via VTY */
+ int user_gain_mdB;
+
+ /* calibration table of internal PA */
+ struct power_amp pa;
+
+ /* calibration table of user PA */
+ struct power_amp user_pa;
+
+ /* power ramping related data */
+ struct {
+ /* maximum initial Pout including all PAs */
+ int max_initial_pout_mdBm;
+ /* temporary attenuation due to power ramping */
+ int attenuation_mdB;
+ unsigned int step_size_mdB;
+ unsigned int step_interval_sec;
+ struct osmo_timer_list step_timer;
+ } ramp;
+};
+
+int get_p_max_out_mdBm(struct gsm_bts_trx *trx);
+
+int get_p_nominal_mdBm(struct gsm_bts_trx *trx);
+
+int get_p_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
+int get_p_target_mdBm_lchan(struct gsm_lchan *lchan);
+
+int get_p_trxout_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
+int get_p_trxout_target_mdBm_lchan(struct gsm_lchan *lchan);
+
+int get_p_trxout_actual_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
+int get_p_trxout_actual_mdBm_lchan(struct gsm_lchan *lchan);
+
+int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass);
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index ea2a742..77f73b4 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -5,4 +5,6 @@ LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS)
noinst_LIBRARIES = libbts.a
libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
rsl.c vty.c paging.c measurement.c amr.c lchan.c \
- load_indication.c pcu_sock.c handover.c msg_utils.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
diff --git a/src/common/bts.c b/src/common/bts.c
index 878770a..6cbafd1 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -120,7 +120,9 @@ int bts_init(struct gsm_bts *bts)
/* initialize bts data structure */
llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct trx_power_params *tpp = &trx->power_params;
int i;
+
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
struct gsm_bts_trx_ts *ts = &trx->ts[i];
int k;
@@ -130,6 +132,10 @@ int bts_init(struct gsm_bts *bts)
INIT_LLIST_HEAD(&lchan->dl_tch_queue);
}
}
+ /* Default values for the power adjustments */
+ tpp->ramp.max_initial_pout_mdBm = to_mdB(23);
+ tpp->ramp.step_size_mdB = to_mdB(2);
+ tpp->ramp.step_interval_sec = 1;
}
osmo_rtp_init(tall_bts_ctx);
diff --git a/src/common/tx_power.c b/src/common/tx_power.c
new file mode 100644
index 0000000..e2bb9ed
--- /dev/null
+++ b/src/common/tx_power.c
@@ -0,0 +1,273 @@
+/* Transmit Power computation */
+
+/* (C) 2014 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 <limits.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+
+#include <osmo-bts/logging.h>
+#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/bts_model.h>
+#include <osmo-bts/tx_power.h>
+
+static int get_pa_drive_level_mdBm(const struct power_amp *pa,
+ int desired_p_out_mdBm, unsigned int arfcn)
+{
+ if (arfcn > ARRAY_SIZE(pa->calib.gain_mdB))
+ return INT_MIN;
+
+ /* FIXME: temperature compensation */
+
+ return desired_p_out_mdBm - pa->calib.gain_mdB[arfcn];
+}
+
+/* maximum output power of the system */
+int get_p_max_out_mdBm(struct gsm_bts_trx *trx)
+{
+ struct trx_power_params *tpp = &trx->power_params;
+ /* Add user gain, internal and external PA gain to TRX output power */
+ return tpp->trx_p_max_out_mdBm + tpp->user_gain_mdB +
+ tpp->pa.nominal_gain_mdB + tpp->user_pa.nominal_gain_mdB;
+}
+
+/* nominal output power, i.e. OML-reduced maximum output power */
+int get_p_nominal_mdBm(struct gsm_bts_trx *trx)
+{
+ /* P_max_out subtracted by OML maximum power reduction IE */
+ return get_p_max_out_mdBm(trx) - to_mdB(trx->max_power_red);
+}
+
+/* calculate the target total output power required, reduced by both
+ * OML and RSL, but ignoring the attenutation required for power ramping and
+ * thermal management */
+int get_p_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie)
+{
+ /* Pn subtracted by RSL BS Power IE (in 2 dB steps) */
+ return get_p_nominal_mdBm(trx) - to_mdB(bs_power_ie * 2);
+}
+int get_p_target_mdBm_lchan(struct gsm_lchan *lchan)
+{
+ return get_p_target_mdBm(lchan->ts->trx, lchan->bs_power);
+}
+
+/* calculate the actual total output power required, taking into account the
+ * attenutation required for power ramping but not thermal management */
+int get_p_actual_mdBm(struct gsm_bts_trx *trx, int p_target_mdBm)
+{
+ struct trx_power_params *tpp = &trx->power_params;
+
+ /* P_target subtracted by ramp attenuation */
+ return p_target_mdBm - tpp->ramp.attenuation_mdB;
+}
+
+/* calculate the effective total output power required, taking into account the
+ * attenutation required for power ramping and thermal management */
+int get_p_eff_mdBm(struct gsm_bts_trx *trx, int p_target_mdBm)
+{
+ struct trx_power_params *tpp = &trx->power_params;
+
+ /* P_target subtracted by ramp attenuation */
+ return p_target_mdBm - tpp->ramp.attenuation_mdB - tpp->thermal_attenuation_mdB;
+}
+
+/* calculate effect TRX output power required, taking into account the
+ * attenuations required for power ramping and thermal management */
+int get_p_trxout_eff_mdBm(struct gsm_bts_trx *trx, int p_target_mdBm)
+{
+ struct trx_power_params *tpp = &trx->power_params;
+ int p_actual_mdBm, user_pa_drvlvl_mdBm, pa_drvlvl_mdBm;
+ unsigned int arfcn = trx->arfcn;
+
+ /* P_actual subtracted by any bulk gaion added by the user */
+ p_actual_mdBm = get_p_eff_mdBm(trx, p_target_mdBm) - tpp->user_gain_mdB;
+
+ /* determine input drive level required at input to user PA */
+ user_pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->user_pa, p_actual_mdBm, arfcn);
+
+ /* determine input drive level required at input to internal PA */
+ pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->pa, user_pa_drvlvl_mdBm, arfcn);
+
+ /* internal PA input drive level is TRX output power */
+ return pa_drvlvl_mdBm;
+}
+
+/* calculate target TRX output power required, ignoring the
+ * attenuations required for power ramping but not thermal management */
+int get_p_trxout_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie)
+{
+ struct trx_power_params *tpp = &trx->power_params;
+ int p_target_mdBm, user_pa_drvlvl_mdBm, pa_drvlvl_mdBm;
+ unsigned int arfcn = trx->arfcn;
+
+ /* P_target subtracted by any bulk gaion added by the user */
+ p_target_mdBm = get_p_target_mdBm(trx, bs_power_ie) - tpp->user_gain_mdB;
+
+ /* determine input drive level required at input to user PA */
+ user_pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->user_pa, p_target_mdBm, arfcn);
+
+ /* determine input drive level required at input to internal PA */
+ pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->pa, user_pa_drvlvl_mdBm, arfcn);
+
+ /* internal PA input drive level is TRX output power */
+ return pa_drvlvl_mdBm;
+}
+int get_p_trxout_target_mdBm_lchan(struct gsm_lchan *lchan)
+{
+ return get_p_trxout_target_mdBm(lchan->ts->trx, lchan->bs_power);
+}
+
+
+/* output power ramping code */
+
+/* The idea here is to avoid a hard switch from 0 to 100, but to actually
+ * slowly and gradually ramp up or down the power. This is needed on the
+ * one hand side to avoid very fast dynamic load changes towards the PA power
+ * supply, but is also needed in order to avoid a DoS by too many subscriber
+ * attempting to register at the same time. Rather, grow the cell slowly in
+ * radius than start with the full raduis at once. */
+
+static int we_are_ramping_up(struct gsm_bts_trx *trx)
+{
+ struct trx_power_params *tpp = &trx->power_params;
+
+ if (tpp->p_total_tgt_mdBm > tpp->p_total_cur_mdBm)
+ return 1;
+ else
+ return 0;
+}
+
+static void power_ramp_do_step(struct gsm_bts_trx *trx, int first);
+
+/* timer call-back for the ramp tumer */
+static void power_ramp_timer_cb(void *_trx)
+{
+ struct gsm_bts_trx *trx = _trx;
+ struct trx_power_params *tpp = &trx->power_params;
+ int p_trxout_eff_mdBm;
+
+ /* compute new actual total output power (= minus ramp attenuation) */
+ tpp->p_total_cur_mdBm = get_p_actual_mdBm(trx, tpp->p_total_tgt_mdBm);
+
+ /* compute new effective (= minus ramp and thermal attenuation) TRX output required */
+ p_trxout_eff_mdBm = get_p_trxout_eff_mdBm(trx, tpp->p_total_tgt_mdBm);
+
+ LOGP(DL1C, LOGL_DEBUG, "ramp_timer_cb(cur_pout=%d, tgt_pout=%d, "
+ "ramp_att=%d, therm_att=%d, user_gain=%d)\n",
+ tpp->p_total_cur_mdBm, tpp->p_total_tgt_mdBm,
+ tpp->ramp.attenuation_mdB, tpp->thermal_attenuation_mdB,
+ tpp->user_gain_mdB);
+
+ LOGP(DL1C, LOGL_INFO,
+ "ramping TRX board output power to %d mdBm.\n", p_trxout_eff_mdBm);
+
+ /* Instruct L1 to apply new effective TRX output power required */
+ bts_model_change_power(trx, p_trxout_eff_mdBm);
+
+ /* and do another step... */
+ power_ramp_do_step(trx, 0);
+}
+
+static void power_ramp_do_step(struct gsm_bts_trx *trx, int first)
+{
+ struct trx_power_params *tpp = &trx->power_params;
+
+ /* we had finished in last loop iteration */
+ if (!first && tpp->ramp.attenuation_mdB == 0)
+ return;
+
+ if (we_are_ramping_up(trx)) {
+ /* ramp up power -> ramp down attenuation */
+ tpp->ramp.attenuation_mdB -= tpp->ramp.step_size_mdB;
+ if (tpp->ramp.attenuation_mdB <= 0) {
+ /* we are done */
+ tpp->ramp.attenuation_mdB = 0;
+ }
+ } else {
+ /* ramp down power -> ramp up attenuation */
+ tpp->ramp.attenuation_mdB += tpp->ramp.step_size_mdB;
+ if (tpp->ramp.attenuation_mdB >= 0) {
+ /* we are done */
+ tpp->ramp.attenuation_mdB = 0;
+ }
+ }
+
+ /* schedule timer for the next step */
+ tpp->ramp.step_timer.data = trx;
+ tpp->ramp.step_timer.cb = power_ramp_timer_cb;
+ osmo_timer_schedule(&tpp->ramp.step_timer, tpp->ramp.step_interval_sec, 0);
+}
+
+
+int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass)
+{
+ struct trx_power_params *tpp = &trx->power_params;
+
+ /* The input to this function is the actual desired output power, i.e.
+ * the maximum total system power subtracted by OML as well as RSL
+ * reductions */
+
+ LOGP(DL1C, LOGL_INFO, "power_ramp_start(cur=%d, tgt=%d)\n",
+ tpp->p_total_cur_mdBm, p_total_tgt_mdBm);
+
+ if (!bypass && (p_total_tgt_mdBm > get_p_nominal_mdBm(trx))) {
+ LOGP(DL1C, LOGL_ERROR, "Asked to ramp power up to "
+ "%d mdBm, which exceeds P_max_out (%d)\n",
+ p_total_tgt_mdBm, get_p_nominal_mdBm(trx));
+ return -ERANGE;
+ }
+
+ /* Cancel any pending request */
+ osmo_timer_del(&tpp->ramp.step_timer);
+
+ /* set the new target */
+ tpp->p_total_tgt_mdBm = p_total_tgt_mdBm;
+
+ if (we_are_ramping_up(trx)) {
+ if (tpp->p_total_tgt_mdBm <= tpp->ramp.max_initial_pout_mdBm) {
+ LOGP(DL1C, LOGL_INFO,
+ "target_power(%d) is below max.initial power\n",
+ tpp->p_total_tgt_mdBm);
+ /* new setting is below the maximum initial output
+ * power, so we can directly jump to this level */
+ tpp->p_total_cur_mdBm = tpp->p_total_tgt_mdBm;
+ tpp->ramp.attenuation_mdB = 0;
+ power_ramp_timer_cb(trx);
+ } else {
+ /* We need to step it up. Start from the current value */
+ /* Set attenuation to cause no power change right now */
+ tpp->ramp.attenuation_mdB = tpp->p_total_tgt_mdBm - tpp->p_total_cur_mdBm;
+
+ /* start with the firsrt step */
+ power_ramp_do_step(trx, 1);
+ }
+ } else {
+ /* Set ramp attenuation to negative value, and increse that by
+ * steps until it reaches 0 */
+ tpp->ramp.attenuation_mdB = tpp->p_total_tgt_mdBm - tpp->p_total_cur_mdBm;
+
+ /* start with the firsrt step */
+ power_ramp_do_step(trx, 1);
+ }
+
+ return 0;
+}
diff --git a/src/common/vty.c b/src/common/vty.c
index ea38ac9..a2f2fb3 100644
--- a/src/common/vty.c
+++ b/src/common/vty.c
@@ -186,7 +186,19 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
bts_model_config_write_bts(vty, bts);
llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct trx_power_params *tpp = &trx->power_params;
vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE);
+
+ if (trx->power_params.user_gain_mdB)
+ vty_out(vty, " user-gain %u mdB%s",
+ tpp->user_gain_mdB, VTY_NEWLINE);
+ vty_out(vty, " power-ramp max-initinal-pout %d mdBm%s",
+ tpp->ramp.max_initial_pout_mdBm, VTY_NEWLINE);
+ vty_out(vty, " power-ramp step-size %d mdBm%s",
+ tpp->ramp.step_size_mdB, VTY_NEWLINE);
+ vty_out(vty, " power-ramp step-interval %d%s",
+ tpp->ramp.step_interval_sec, VTY_NEWLINE);
+
bts_model_config_write_trx(vty, trx);
}
}
@@ -384,6 +396,71 @@ DEFUN(cfg_bts_agch_queue_mgmt_default,
return CMD_SUCCESS;
}
+#define DB_DBM_STR \
+ "Unit is dB (decibels)\n" \
+ "Unit is mdB (milli-decibels, or rather 1/10000 bel)\n"
+
+static int parse_mdbm(const char *valstr, const char *unit)
+{
+ int val = atoi(valstr);
+
+ if (!strcmp(unit, "dB") || !strcmp(unit, "dBm"))
+ return val * 1000;
+ else
+ return val;
+}
+
+DEFUN(cfg_trx_user_gain,
+ cfg_trx_user_gain_cmd,
+ "user-gain <-100000-100000> (dB|mdB)",
+ "Inform BTS about additional, user-provided gain or attenuation at TRX output\n"
+ "Value of user-provided external gain(+)/attenuation(-)\n" DB_DBM_STR)
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ trx->power_params.user_gain_mdB = parse_mdbm(argv[0], argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+#define PR_STR "Power-Ramp settings"
+DEFUN(cfg_trx_pr_max_initial, cfg_trx_pr_max_initial_cmd,
+ "power-ramp max-initial <0-100000> (dBm|mdBm)",
+ PR_STR "Maximum initial power\n"
+ "Value\n" DB_DBM_STR)
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ trx->power_params.ramp.max_initial_pout_mdBm =
+ parse_mdbm(argv[0], argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trx_pr_step_size, cfg_trx_pr_step_size_cmd,
+ "power-ramp step-size <1-100000> (dB|mdB)",
+ PR_STR "Power increase by step\n"
+ "Step size\n" DB_DBM_STR)
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ trx->power_params.ramp.step_size_mdB =
+ parse_mdbm(argv[0], argv[1]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trx_pr_step_interval, cfg_trx_pr_step_interval_cmd,
+ "power-ramp step-interval <1-100>",
+ PR_STR "Power increase by step\n"
+ "Step time in seconds\n")
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ trx->power_params.ramp.step_interval_sec = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+
/* ======================================================================
* SHOW
@@ -563,6 +640,11 @@ int bts_vty_init(const struct log_info *cat)
install_node(&trx_node, config_write_dummy);
install_default(TRX_NODE);
+ install_element(TRX_NODE, &cfg_trx_user_gain_cmd);
+ install_element(TRX_NODE, &cfg_trx_pr_max_initial_cmd);
+ install_element(TRX_NODE, &cfg_trx_pr_step_size_cmd);
+ install_element(TRX_NODE, &cfg_trx_pr_step_interval_cmd);
+
install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
return 0;
diff --git a/src/osmo-bts-sysmo/main.c b/src/osmo-bts-sysmo/main.c
index 5a86407..d12421d 100644
--- a/src/osmo-bts-sysmo/main.c
+++ b/src/osmo-bts-sysmo/main.c
@@ -84,11 +84,7 @@ int bts_model_init(struct gsm_bts *bts)
rc = 23;
}
bts->c0->nominal_power = rc;
-
- /* Initial values for the power adjustments */
- bts->c0->pa.max_initial_power = 23;
- bts->c0->pa.step_size = 2;
- bts->c0->pa.step_interval = 1;
+ bts->c0->power_params.trx_p_max_out_mdBm = to_mdB(rc);
bts_model_vty_init(bts);
diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c
index e5f9260..207cae8 100644
--- a/src/osmo-bts-sysmo/oml.c
+++ b/src/osmo-bts-sysmo/oml.c
@@ -272,7 +272,7 @@ static int trx_init_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg)
#endif
/* Begin to ramp up the power */
- sysmobts_pa_maybe_step(trx);
+ power_ramp_start(trx, get_p_target_mdBm(trx, 0), 0);
return opstart_compl(&trx->mo, l1_msg);
}
@@ -327,9 +327,7 @@ static int trx_init(struct gsm_bts_trx *trx)
dev_par->u8NbTsc = trx->bts->bsic & 7;
dev_par->fRxPowerLevel = fl1h->ul_power_target;
- /* initialize the power */
- sysmobts_pa_pwr_init(trx);
- dev_par->fTxPowerLevel = trx->pa.current_power;
+ dev_par->fTxPowerLevel = 0.0;
LOGP(DL1C, LOGL_NOTICE, "Init TRX (ARFCN %u, TSC %u, RxPower % 2f dBm, "
"TxPower % 2.2f dBm\n", dev_par->u16Arfcn, dev_par->u8NbTsc,
dev_par->fRxPowerLevel, dev_par->fTxPowerLevel);
@@ -1074,12 +1072,8 @@ static int chmod_txpower_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg)
LOGPC(DL1C, LOGL_INFO, "setTxPower %f dBm\n",
cc->cfgParams.setTxPowerLevel.fTxPowerLevel);
- trx->pa.current_power = cc->cfgParams.setTxPowerLevel.fTxPowerLevel;
msgb_free(l1_msg);
- /* Schedule the next step up */
- sysmobts_pa_maybe_step(trx);
-
return 0;
}
@@ -1553,10 +1547,8 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
/* Did we go through MphInit yet? If yes fire and forget */
- if (fl1h->hLayer1) {
- sysmobts_pa_pwr_init(trx);
- l1if_set_txpower(fl1h, (float) trx->pa.current_power);
- }
+ if (fl1h->hLayer1)
+ power_ramp_start(trx, get_p_target_mdBm(trx, 0), 0);
}
/* FIXME: we actaully need to send a ACK or NACK for the OML message */
@@ -1722,3 +1714,8 @@ int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
return l1if_activate_rf(fl1, 0);
}
+
+int bts_model_change_power(struct gsm_bts_trx *trx, int p_trxout_mdBm)
+{
+ return l1if_set_txpower(trx_femtol1_hdl(trx), ((float) p_trxout_mdBm)/1000.0);
+}
diff --git a/src/osmo-bts-sysmo/sysmobts_vty.c b/src/osmo-bts-sysmo/sysmobts_vty.c
index ed90a23..8b617d7 100644
--- a/src/osmo-bts-sysmo/sysmobts_vty.c
+++ b/src/osmo-bts-sysmo/sysmobts_vty.c
@@ -254,40 +254,6 @@ DEFUN(cfg_trx_nominal_power, cfg_trx_nominal_power_cmd,
return CMD_SUCCESS;
}
-#define PA_STR "Power-Amplifier settings"
-DEFUN(cfg_trx_pa_max_initial, cfg_trx_pa_max_initial_cmd,
- "power-amplifier max-initial <0-100>",
- PA_STR "Maximum initial power\n"
- "Value in dBm\n")
-{
- struct gsm_bts_trx *trx = vty->index;
-
- trx->pa.max_initial_power = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_pa_step_size, cfg_trx_pa_step_size_cmd,
- "power-amplifier step-size <1-100>",
- PA_STR "Power increase by step\n"
- "Step size in dB\n")
-{
- struct gsm_bts_trx *trx = vty->index;
-
- trx->pa.step_size = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_pa_step_interval, cfg_trx_pa_step_interval_cmd,
- "power-amplifier step-interval <1-100>",
- PA_STR "Power increase by step\n"
- "Step time in seconds\n")
-{
- struct gsm_bts_trx *trx = vty->index;
-
- trx->pa.step_interval = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
/* runtime */
DEFUN(show_trx_clksrc, show_trx_clksrc_cmd,
@@ -452,11 +418,8 @@ DEFUN(set_tx_power, set_tx_power_cmd,
int trx_nr = atoi(argv[0]);
int power = atoi(argv[1]);
struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
- struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
- trx->pa.current_power = power;
- osmo_timer_del(&trx->pa.step_timer);
- l1if_set_txpower(fl1h, (float) trx->pa.current_power);
+ power_ramp_start(trx, to_mdB(power), 1);
return CMD_SUCCESS;
}
@@ -586,12 +549,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
if (trx->nominal_power != sysmobts_get_nominal_power(trx))
vty_out(vty, " nominal-tx-power %d%s", trx->nominal_power,
VTY_NEWLINE);
- vty_out(vty, " power-amplifier max-initial %d%s",
- trx->pa.max_initial_power, VTY_NEWLINE);
- vty_out(vty, " power-amplifier step-size %d%s",
- trx->pa.step_size, VTY_NEWLINE);
- vty_out(vty, " power-amplifier step-interval %d%s",
- trx->pa.step_interval, VTY_NEWLINE);
for (i = 0; i < 32; i++) {
if (fl1h->gsmtap_sapi_mask & (1 << i)) {
@@ -663,9 +620,6 @@ int bts_model_vty_init(struct gsm_bts *bts)
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);
- install_element(TRX_NODE, &cfg_trx_pa_max_initial_cmd);
- install_element(TRX_NODE, &cfg_trx_pa_step_size_cmd);
- install_element(TRX_NODE, &cfg_trx_pa_step_interval_cmd);
return 0;
}
diff --git a/src/osmo-bts-sysmo/utils.c b/src/osmo-bts-sysmo/utils.c
index afca773..a636ae1 100644
--- a/src/osmo-bts-sysmo/utils.c
+++ b/src/osmo-bts-sysmo/utils.c
@@ -137,88 +137,4 @@ int sysmobts_get_nominal_power(struct gsm_bts_trx *trx)
return -1;
}
-int sysmobts_get_target_power(struct gsm_bts_trx *trx)
-{
- int target_power = trx->nominal_power - trx->max_power_red;
- target_power -= trx->power_reduce;
-
- if (target_power < 0)
- target_power = 0;
-
- return target_power;
-}
-
-void sysmobts_pa_pwr_init(struct gsm_bts_trx *trx)
-{
- int target_power = sysmobts_get_target_power(trx);
-
- /* Cancel any pending request */
- osmo_timer_del(&trx->pa.step_timer);
-
- /* is this below our initial target */
- if (target_power <= trx->pa.max_initial_power) {
- LOGP(DL1C, LOGL_NOTICE,
- "PA target_power(%d) is below initial power.\n",
- target_power);
- trx->pa.current_power = target_power;
- return;
- }
-
- /* is this below our current value? */
- if (target_power <= trx->pa.current_power) {
- LOGP(DL1C, LOGL_NOTICE,
- "PA target_power(%d) is below current_power.\n",
- target_power);
- trx->pa.current_power = target_power;
- return;
- }
- if (trx->pa.current_power > trx->pa.max_initial_power) {
- LOGP(DL1C, LOGL_NOTICE,
- "PA target_power(%d) starting from current_power.\n",
- target_power);
- return;
- }
-
- /* We need to step it up. Start from the initial value */
- trx->pa.current_power = trx->pa.max_initial_power;
- LOGP(DL1C, LOGL_NOTICE,
- "PA target_power(%d) starting with %d dBm.\n",
- target_power, trx->pa.current_power);
-}
-
-static void pa_trx_cb(void *_trx)
-{
- struct gsm_bts_trx *trx = _trx;
-
- LOGP(DL1C, LOGL_NOTICE,
- "PA raising power to %d dBm.\n", trx->pa.current_power);
- l1if_set_txpower(trx_femtol1_hdl(trx), (float) trx->pa.current_power);
-}
-
-void sysmobts_pa_maybe_step(struct gsm_bts_trx *trx)
-{
- /* it can not have changed */
- int target_power = sysmobts_get_target_power(trx);
-
- /* We are done */
- if (trx->pa.current_power >= target_power) {
- LOGP(DL1C, LOGL_NOTICE,
- "PA have reached target power: %d dBm.\n",
- target_power);
- return;
- }
-
- /* Step up the current power but clamp it */
- trx->pa.current_power += trx->pa.step_size;
- if (trx->pa.current_power > target_power)
- trx->pa.current_power = target_power;
-
- LOGP(DL1C, LOGL_NOTICE,
- "PA scheduling to step to %d dBm.\n",
- trx->pa.current_power);
-
- trx->pa.step_timer.data = trx;
- trx->pa.step_timer.cb = pa_trx_cb;
- osmo_timer_schedule(&trx->pa.step_timer, trx->pa.step_interval, 0);
-}