<p>keith has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/osmo-bsc/+/25654">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Implement MS Uplink Power Control Loop.<br><br>* Adds vty option dyn-bsc for ms-power-control -> mode<br>* Imports power_control.c from osmo-bts project.<br>* Removes unused C/I code from power_control.<br><br>This patch then calls the power loop on receipt of measurement<br>reports and updates the MS Power Level accordingly.<br><br>Change-Id: Ibc307e758697eb5ca3fb86622f35709d6077db9e<br>---<br>M include/osmocom/bsc/debug.h<br>M include/osmocom/bsc/gsm_data.h<br>A include/osmocom/bsc/power_control.h<br>M src/osmo-bsc/Makefile.am<br>M src/osmo-bsc/abis_rsl.c<br>M src/osmo-bsc/bsc_vty.c<br>M src/osmo-bsc/bts_vty.c<br>M src/osmo-bsc/osmo_bsc_main.c<br>A src/osmo-bsc/power_control.c<br>9 files changed, 330 insertions(+), 3 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmo-bsc refs/changes/54/25654/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/bsc/debug.h b/include/osmocom/bsc/debug.h</span><br><span>index 4ad61b4..a3cad68 100644</span><br><span>--- a/include/osmocom/bsc/debug.h</span><br><span>+++ b/include/osmocom/bsc/debug.h</span><br><span>@@ -29,6 +29,7 @@</span><br><span>       DCBS,</span><br><span>        DLCS,</span><br><span>        DRESET,</span><br><span style="color: hsl(120, 100%, 40%);">+       DLOOP,</span><br><span>       Debug_LastEntry,</span><br><span> };</span><br><span> </span><br><span>diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h</span><br><span>index 03f210f..f86503b 100644</span><br><span>--- a/include/osmocom/bsc/gsm_data.h</span><br><span>+++ b/include/osmocom/bsc/gsm_data.h</span><br><span>@@ -665,6 +665,19 @@</span><br><span> #define INTERF_DBM_UNKNOWN 0</span><br><span> #define INTERF_BAND_UNKNOWN 0xff</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Measurement pre-processing state */</span><br><span style="color: hsl(120, 100%, 40%);">+struct gsm_power_ctrl_meas_proc_state {</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Number of measurements processed */</span><br><span style="color: hsl(120, 100%, 40%);">+        unsigned int meas_num;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Algorithm specific data */</span><br><span style="color: hsl(120, 100%, 40%);">+ union {</span><br><span style="color: hsl(120, 100%, 40%);">+               struct {</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* Scaled up 100 times average value */</span><br><span style="color: hsl(120, 100%, 40%);">+                       int Avg100;</span><br><span style="color: hsl(120, 100%, 40%);">+           } ewma;</span><br><span style="color: hsl(120, 100%, 40%);">+       };</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct gsm_lchan {</span><br><span>       /* The TS that we're part of */</span><br><span>  struct gsm_bts_trx_ts *ts;</span><br><span>@@ -808,6 +821,10 @@</span><br><span>    /* Actual reported interference band index, or INTERF_BAND_UNKNOWN if this lchan was not included in the most</span><br><span>         * recent Resource Indication. */</span><br><span>    uint8_t interf_band;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gsm_power_ctrl_meas_proc_state rxlev_meas_proc;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Number of SACCH blocks to skip in Power Loop */</span><br><span style="color: hsl(120, 100%, 40%);">+    int skip_block_num;</span><br><span> };</span><br><span> </span><br><span> /* One Timeslot in a TRX */</span><br><span>@@ -1330,6 +1347,9 @@</span><br><span>         GSM_PWR_CTRL_MODE_STATIC,</span><br><span>    /* Send MS/BS Power [Parameters] IEs (dynamic mode) */</span><br><span>       GSM_PWR_CTRL_MODE_DYN_BTS,</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Do not send MS/BS Power IEs and use BSC Power Loop */</span><br><span style="color: hsl(120, 100%, 40%);">+      GSM_PWR_CTRL_MODE_DYN_BSC,</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> };</span><br><span> </span><br><span> /* MS/BS Power Control Parameters */</span><br><span>diff --git a/include/osmocom/bsc/power_control.h b/include/osmocom/bsc/power_control.h</span><br><span>new file mode 100644</span><br><span>index 0000000..d43b48e</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/bsc/power_control.h</span><br><span>@@ -0,0 +1,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/gsm_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/meas_rep.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan, struct gsm_meas_rep *mr);</span><br><span>diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am</span><br><span>index 8991697..19af569 100644</span><br><span>--- a/src/osmo-bsc/Makefile.am</span><br><span>+++ b/src/osmo-bsc/Makefile.am</span><br><span>@@ -106,6 +106,7 @@</span><br><span>    smscb.c \</span><br><span>    cbch_scheduler.c \</span><br><span>   cbsp_link.c \</span><br><span style="color: hsl(120, 100%, 40%);">+ power_control.c \</span><br><span>    $(NULL)</span><br><span> </span><br><span> libbsc_la_LIBADD = \</span><br><span>diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c</span><br><span>index fb8e386..bdf32bd 100644</span><br><span>--- a/src/osmo-bsc/abis_rsl.c</span><br><span>+++ b/src/osmo-bsc/abis_rsl.c</span><br><span>@@ -55,6 +55,7 @@</span><br><span> #include <osmocom/bsc/handover_fsm.h></span><br><span> #include <osmocom/bsc/smscb.h></span><br><span> #include <osmocom/bsc/bts.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/power_control.h></span><br><span> </span><br><span> static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan,</span><br><span>                              struct gsm_meas_rep *resp)</span><br><span>@@ -1339,6 +1340,8 @@</span><br><span>     LOGP(DRSL, LOGL_DEBUG, "%s: meas_rep_count++=%d meas_rep_last_seen_nr=%u\n",</span><br><span>            gsm_lchan_name(mr->lchan), mr->lchan->meas_rep_count, mr->lchan->meas_rep_last_seen_nr);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+      lchan_ms_pwr_ctrl(msg->lchan, mr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      print_meas_rep(msg->lchan, mr);</span><br><span> </span><br><span>       send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr);</span><br><span>diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c</span><br><span>index 8977a08..4775ff6 100644</span><br><span>--- a/src/osmo-bsc/bsc_vty.c</span><br><span>+++ b/src/osmo-bsc/bsc_vty.c</span><br><span>@@ -1356,6 +1356,12 @@</span><br><span>                return CMD_WARNING;</span><br><span>  }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (bts->ms_power_ctrl.mode == GSM_PWR_CTRL_MODE_DYN_BSC) {</span><br><span style="color: hsl(120, 100%, 40%);">+                vty_out(vty, "%% Not Sending default MS/BS Power control parameters "</span><br><span style="color: hsl(120, 100%, 40%);">+                       "because BTS%d is using dyn-bsc mode%s", bts_nr, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+              return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (bts->model->power_ctrl_send_def_params == NULL) {</span><br><span>          vty_out(vty, "%% Sending default MS/BS Power control parameters "</span><br><span>                  "for BTS%d is not implemented%s", bts_nr, VTY_NEWLINE);</span><br><span>diff --git a/src/osmo-bsc/bts_vty.c b/src/osmo-bsc/bts_vty.c</span><br><span>index fb11520..460e14a 100644</span><br><span>--- a/src/osmo-bsc/bts_vty.c</span><br><span>+++ b/src/osmo-bsc/bts_vty.c</span><br><span>@@ -2915,10 +2915,11 @@</span><br><span> DEFUN_USRATTR(cfg_power_ctrl_mode,</span><br><span>             cfg_power_ctrl_mode_cmd,</span><br><span>             X(BSC_VTY_ATTR_NEW_LCHAN),</span><br><span style="color: hsl(0, 100%, 40%);">-              "mode (static|dyn-bts) [reset]",</span><br><span style="color: hsl(120, 100%, 40%);">+            "mode (static|dyn-bts|dyn-bsc) [reset]",</span><br><span>           "Power control mode\n"</span><br><span>             "Instruct the MS/BTS to use a static power level\n"</span><br><span>        "Power control to be performed dynamically by the BTS itself\n"</span><br><span style="color: hsl(120, 100%, 40%);">+             "Power control to be performed dynamically at this BSC\n"</span><br><span>          "Reset to default parameters for the given mode\n")</span><br><span> {</span><br><span>     struct gsm_power_ctrl_params *params = vty->index;</span><br><span>@@ -2935,6 +2936,13 @@</span><br><span>               params->mode = GSM_PWR_CTRL_MODE_STATIC;</span><br><span>  else if (strcmp(argv[0], "dyn-bts") == 0)</span><br><span>          params->mode = GSM_PWR_CTRL_MODE_DYN_BTS;</span><br><span style="color: hsl(120, 100%, 40%);">+  else if (strcmp(argv[0], "dyn-bsc") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (params->dir == GSM_PWR_CTRL_DIR_DL) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  vty_out(vty, "%% mode dyn-bsc not supported for Downlink.%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                 return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+             params->mode = GSM_PWR_CTRL_MODE_DYN_BSC;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span> </span><br><span>        return CMD_SUCCESS;</span><br><span> }</span><br><span>@@ -3139,6 +3147,11 @@</span><br><span>    int upper = atoi(argv[2]);</span><br><span>   struct gsm_power_ctrl_meas_params *meas_params;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+   if (params->mode == GSM_PWR_CTRL_MODE_DYN_BSC) {</span><br><span style="color: hsl(120, 100%, 40%);">+           vty_out(vty, "%% C/I based power loop not possible in dyn-bsc mode!%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+               return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (params->dir != GSM_PWR_CTRL_DIR_UL) {</span><br><span>                 vty_out(vty, "%% C/I based power loop only possible in Uplink!%s", VTY_NEWLINE);</span><br><span>           return CMD_WARNING;</span><br><span>@@ -3962,8 +3975,10 @@</span><br><span>                         cfg_out(" bs-power static %u%s", cp->bs_power_val_db, VTY_NEWLINE);</span><br><span>             break;</span><br><span>       case GSM_PWR_CTRL_MODE_DYN_BTS:</span><br><span style="color: hsl(120, 100%, 40%);">+       case GSM_PWR_CTRL_MODE_DYN_BSC:</span><br><span>              cfg_out("%s%s", node_name, VTY_NEWLINE);</span><br><span style="color: hsl(0, 100%, 40%);">-              cfg_out(" mode dyn-bts%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+            cfg_out(" mode %s%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                       cp->mode == GSM_PWR_CTRL_MODE_DYN_BTS ? "dyn-bts" : "dyn-bsc", VTY_NEWLINE);</span><br><span>          if (cp->dir == GSM_PWR_CTRL_DIR_DL)</span><br><span>                       cfg_out(" bs-power dyn-max %u%s", cp->bs_power_max_db, VTY_NEWLINE);</span><br><span> </span><br><span>@@ -3975,7 +3990,8 @@</span><br><span>                /* Measurement processing / averaging parameters */</span><br><span>          config_write_power_ctrl_meas(vty, indent + 1, &cp->rxlev_meas, "rxlev", "");</span><br><span>              config_write_power_ctrl_meas(vty, indent + 1, &cp->rxqual_meas, "rxqual", "");</span><br><span style="color: hsl(0, 100%, 40%);">-               if (cp->dir == GSM_PWR_CTRL_DIR_UL && is_osmobts(bts)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           if (cp->dir == GSM_PWR_CTRL_DIR_UL && is_osmobts(bts)</span><br><span style="color: hsl(120, 100%, 40%);">+                  && cp->mode == GSM_PWR_CTRL_MODE_DYN_BTS) {</span><br><span>                   config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_fr_meas, "ci", " fr-efr");</span><br><span>                  config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_hr_meas, "ci", " hr");</span><br><span>                      config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_amr_fr_meas, "ci", " amr-fr");</span><br><span>diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c</span><br><span>index 395a60e..d651024 100644</span><br><span>--- a/src/osmo-bsc/osmo_bsc_main.c</span><br><span>+++ b/src/osmo-bsc/osmo_bsc_main.c</span><br><span>@@ -855,6 +855,12 @@</span><br><span>          .description = "RESET/ACK on A and Lb interfaces",</span><br><span>                 .enabled = 1, .loglevel = LOGL_NOTICE,</span><br><span>       },</span><br><span style="color: hsl(120, 100%, 40%);">+        [DLOOP] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .name = "DLOOP",</span><br><span style="color: hsl(120, 100%, 40%);">+                .description = "Control loops",</span><br><span style="color: hsl(120, 100%, 40%);">+                .color = "\033[0;34m",</span><br><span style="color: hsl(120, 100%, 40%);">+                .enabled = 1, .loglevel = LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+        },</span><br><span> };</span><br><span> </span><br><span> static int filter_fn(const struct log_context *ctx, struct log_target *tar)</span><br><span>diff --git a/src/osmo-bsc/power_control.c b/src/osmo-bsc/power_control.c</span><br><span>new file mode 100644</span><br><span>index 0000000..7279050</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-bsc/power_control.c</span><br><span>@@ -0,0 +1,267 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* MS Power Control Loop L1 */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* (C) 2014 by Holger Hans Peter Freyther</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020-2021 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <unistd.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <inttypes.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/bts.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/gsm_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/bsc_subscriber.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/abis_rsl.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/meas_rep.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/power_control.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* We don't want to deal with floating point, so we scale up */</span><br><span style="color: hsl(120, 100%, 40%);">+#define EWMA_SCALE_FACTOR 100</span><br><span style="color: hsl(120, 100%, 40%);">+/* EWMA_SCALE_FACTOR/2 = +50: Round to nearest value when downscaling, otherwise floor() is applied. */</span><br><span style="color: hsl(120, 100%, 40%);">+#define EWMA_ROUND_FACTOR (EWMA_SCALE_FACTOR / 2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Base Low-Pass Single-Pole IIR Filter (EWMA) formula:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Avg[n] = a * Val[n] + (1 - a) * Avg[n - 1]</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * where parameter 'a' determines how much weight of the latest measurement value</span><br><span style="color: hsl(120, 100%, 40%);">+ * 'Val[n]' carries vs the weight of the accumulated average 'Avg[n - 1]'.  The</span><br><span style="color: hsl(120, 100%, 40%);">+ * value of 'a' is usually a float in range 0 .. 1, so:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *  - value 0.5 gives equal weight to both 'Val[n]' and 'Avg[n - 1]';</span><br><span style="color: hsl(120, 100%, 40%);">+ *  - value 1.0 means no filtering at all (pass through);</span><br><span style="color: hsl(120, 100%, 40%);">+ *  - value 0.0 makes no sense.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Further optimization:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Avg[n] = a * Val[n] + Avg[n - 1] - a * Avg[n - 1]</span><br><span style="color: hsl(120, 100%, 40%);">+ *   ^^^^^^                ^^^^^^^^^^</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * a) this can be implemented in C using '+=' operator:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Avg += a * Val - a * Avg</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Avg += a * (Val - Avg)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * b) everything is scaled up by 100 to avoid floating point stuff:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Avg100 += A * (Val - Avg)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * where 'Avg100' is 'Avg * 100' and 'A' is 'a * 100'.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * For more details, see:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *   https://en.wikipedia.org/wiki/Moving_average</span><br><span style="color: hsl(120, 100%, 40%);">+ *   https://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter</span><br><span style="color: hsl(120, 100%, 40%);">+ *   https://tomroelandts.com/articles/low-pass-single-pole-iir-filter</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int do_pf_ewma(const struct gsm_power_ctrl_meas_params *mp,</span><br><span style="color: hsl(120, 100%, 40%);">+                      struct gsm_power_ctrl_meas_proc_state *mps,</span><br><span style="color: hsl(120, 100%, 40%);">+                   const int Val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       const uint8_t A = mp->ewma.alpha;</span><br><span style="color: hsl(120, 100%, 40%);">+  int *Avg100 = &mps->ewma.Avg100;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* We don't have 'Avg[n - 1]' if this is the first run */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mps->meas_num++ == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                *Avg100 = Val * EWMA_SCALE_FACTOR;</span><br><span style="color: hsl(120, 100%, 40%);">+            return Val;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   *Avg100 += A * (Val - (*Avg100 + EWMA_ROUND_FACTOR) / EWMA_SCALE_FACTOR);</span><br><span style="color: hsl(120, 100%, 40%);">+     return (*Avg100 + EWMA_ROUND_FACTOR) / EWMA_SCALE_FACTOR;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Calculate target RxLev value from lower/upper thresholds */</span><br><span style="color: hsl(120, 100%, 40%);">+#define CALC_TARGET(mp) \</span><br><span style="color: hsl(120, 100%, 40%);">+     ((mp).lower_thresh + (mp).upper_thresh) / 2</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int do_avg_algo(const struct gsm_power_ctrl_meas_params *mp,</span><br><span style="color: hsl(120, 100%, 40%);">+                   struct gsm_power_ctrl_meas_proc_state *mps,</span><br><span style="color: hsl(120, 100%, 40%);">+                   const int val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int val_avg;</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (mp->algo) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA:</span><br><span style="color: hsl(120, 100%, 40%);">+            val_avg = do_pf_ewma(mp, mps, val);</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* TODO: implement other pre-processing methods */</span><br><span style="color: hsl(120, 100%, 40%);">+    case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE:</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+              /* No filtering (pass through) */</span><br><span style="color: hsl(120, 100%, 40%);">+             val_avg = val;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     return val_avg;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+/* Calculate a 'delta' value (for the given MS/BS power control parameters)</span><br><span style="color: hsl(120, 100%, 40%);">+ * to be applied to the current Tx power level to approach the target level. */</span><br><span style="color: hsl(120, 100%, 40%);">+static int calc_delta_rxlev(const struct gsm_power_ctrl_params *params, const uint8_t rxlev)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int delta;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Check if RxLev is within the threshold window */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rxlev >= params->rxlev_meas.lower_thresh &&</span><br><span style="color: hsl(120, 100%, 40%);">+     rxlev <= params->rxlev_meas.upper_thresh)</span><br><span style="color: hsl(120, 100%, 40%);">+           return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* How many dBs measured power should be increased (+) or decreased (-)</span><br><span style="color: hsl(120, 100%, 40%);">+        * to reach expected power. */</span><br><span style="color: hsl(120, 100%, 40%);">+        delta = CALC_TARGET(params->rxlev_meas) - rxlev;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Don't ever change more than PWR_{LOWER,RAISE}_MAX_DBM during one loop</span><br><span style="color: hsl(120, 100%, 40%);">+   * iteration, i.e. reduce the speed at which the MS transmit power can</span><br><span style="color: hsl(120, 100%, 40%);">+         * change. A higher value means a lower level (and vice versa) */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (delta > params->inc_step_size_db)</span><br><span style="color: hsl(120, 100%, 40%);">+           delta = params->inc_step_size_db;</span><br><span style="color: hsl(120, 100%, 40%);">+  else if (delta < -params->red_step_size_db)</span><br><span style="color: hsl(120, 100%, 40%);">+             delta = -params->red_step_size_db;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return delta;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Shall we skip current block based on configured interval? */</span><br><span style="color: hsl(120, 100%, 40%);">+static bool ctrl_interval_skip_block(const struct gsm_power_ctrl_params *params,</span><br><span style="color: hsl(120, 100%, 40%);">+                              struct gsm_meas_rep *mr, struct gsm_lchan *lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Power control interval: how many blocks do we skip? */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (lchan->skip_block_num-- > 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                return true;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Can we be sure if ONE Report is always going to correspond</span><br><span style="color: hsl(120, 100%, 40%);">+  * to ONE SACCH block at the BTS? - If not this is as approximation</span><br><span style="color: hsl(120, 100%, 40%);">+    * but it should not hurt. */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Reset the number of SACCH blocks to be skipped:</span><br><span style="color: hsl(120, 100%, 40%);">+    *   ctrl_interval=0 => 0 blocks to skip,</span><br><span style="color: hsl(120, 100%, 40%);">+   *   ctrl_interval=1 => 1 blocks to skip,</span><br><span style="color: hsl(120, 100%, 40%);">+   *   ctrl_interval=2 => 3 blocks to skip,</span><br><span style="color: hsl(120, 100%, 40%);">+   *     so basically ctrl_interval * 2 - 1. */</span><br><span style="color: hsl(120, 100%, 40%);">+  lchan->skip_block_num = params->ctrl_interval * 2 - 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  return false;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan, struct gsm_meas_rep *mr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gsm_bts_trx *trx = lchan->ts->trx;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gsm_bts *bts = trx->bts;</span><br><span style="color: hsl(120, 100%, 40%);">+    enum gsm_band band = bts->band;</span><br><span style="color: hsl(120, 100%, 40%);">+    const struct gsm_power_ctrl_params *params = &bts->ms_power_ctrl;</span><br><span style="color: hsl(120, 100%, 40%);">+      int8_t new_power_lvl; /* TS 05.05 power level */</span><br><span style="color: hsl(120, 100%, 40%);">+      int8_t ms_dbm, new_dbm, current_dbm, bsc_max_dbm;</span><br><span style="color: hsl(120, 100%, 40%);">+     uint8_t rxlev_avg;</span><br><span style="color: hsl(120, 100%, 40%);">+    // int16_t ul_lqual_cb_avg;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t ms_power_lvl = ms_pwr_ctl_lvl(band, mr->ms_l1.pwr);</span><br><span style="color: hsl(120, 100%, 40%);">+        int8_t ul_rssi_dbm;</span><br><span style="color: hsl(120, 100%, 40%);">+   bool ignore;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (bts->ms_power_ctrl.mode != GSM_PWR_CTRL_MODE_DYN_BSC)</span><br><span style="color: hsl(120, 100%, 40%);">+          return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If we are sure DTx is active on Uplink,</span><br><span style="color: hsl(120, 100%, 40%);">+     * use the '-SUB', otherwise '-FULL': */</span><br><span style="color: hsl(120, 100%, 40%);">+      if (bts->dtxu == GSM48_DTX_SHALL_BE_USED)</span><br><span style="color: hsl(120, 100%, 40%);">+         ul_rssi_dbm = rxlev2dbm(mr->ul.sub.rx_lev);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+         ul_rssi_dbm = rxlev2dbm(mr->ul.full.rx_lev);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (params == NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+           return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Not doing the power loop here if we are not handling it */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (params->mode != GSM_PWR_CTRL_MODE_DYN_BSC)</span><br><span style="color: hsl(120, 100%, 40%);">+             return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Shall we skip current block based on configured interval? */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (ctrl_interval_skip_block(params, mr, lchan))</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ms_dbm = ms_pwr_dbm(band, ms_power_lvl);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (ms_dbm < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+          LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+                    "Failed to calculate dBm for power ctl level %" PRIu8 " on band %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                    ms_power_lvl, gsm_band_name(band));</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   bsc_max_dbm = bts->ms_max_power;</span><br><span style="color: hsl(120, 100%, 40%);">+   rxlev_avg = do_avg_algo(&params->rxlev_meas, &lchan->rxlev_meas_proc, dbm2rxlev(ul_rssi_dbm));</span><br><span style="color: hsl(120, 100%, 40%);">+  new_dbm = ms_dbm + calc_delta_rxlev(params, rxlev_avg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Make sure new_dbm is never negative. ms_pwr_ctl_lvl() can later on</span><br><span style="color: hsl(120, 100%, 40%);">+    cope with any unsigned dbm value, regardless of band minimal value. */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (new_dbm < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+           new_dbm = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Don't ask for smaller ms power level than the one set by ms max power for this BTS */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (new_dbm > bsc_max_dbm)</span><br><span style="color: hsl(120, 100%, 40%);">+         new_dbm = bsc_max_dbm;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      new_power_lvl = ms_pwr_ctl_lvl(band, new_dbm);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (new_power_lvl < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+                    "Failed to retrieve power level for %" PRId8 " dBm on band %d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         new_dbm, band);</span><br><span style="color: hsl(120, 100%, 40%);">+             return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   current_dbm = ms_pwr_dbm(band, lchan->ms_power);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* In this Power Control Loop, we infer a new good MS Power Level based</span><br><span style="color: hsl(120, 100%, 40%);">+        * on the previous MS Power Level announced by the MS (not the previous</span><br><span style="color: hsl(120, 100%, 40%);">+        * one we requested!) together with the related computed measurements.</span><br><span style="color: hsl(120, 100%, 40%);">+         * Hence, and since we allow for several good MS Power Levels falling into our</span><br><span style="color: hsl(120, 100%, 40%);">+         * thresholds, we could finally converge into an oscillation loop where</span><br><span style="color: hsl(120, 100%, 40%);">+        * the MS bounces between 2 different correct MS Power levels all the</span><br><span style="color: hsl(120, 100%, 40%);">+  * time, due to the fact that we "accept" and "request back" whatever</span><br><span style="color: hsl(120, 100%, 40%);">+      * good MS Power Level we received from the MS, but at that time the MS</span><br><span style="color: hsl(120, 100%, 40%);">+        * will be transmitting using the previous MS Power Level we</span><br><span style="color: hsl(120, 100%, 40%);">+   * requested, which we will later "accept" and "request back" on next loop</span><br><span style="color: hsl(120, 100%, 40%);">+         * iteration. As a result MS effectively bounces between those 2 MS</span><br><span style="color: hsl(120, 100%, 40%);">+    * Power Levels.</span><br><span style="color: hsl(120, 100%, 40%);">+       * In order to fix this permanent oscillation, if current MS_PWR used/announced</span><br><span style="color: hsl(120, 100%, 40%);">+        * by MS is good ("ms_dbm == new_dbm", hence within thresholds and no change</span><br><span style="color: hsl(120, 100%, 40%);">+         * required) but has higher Tx power than the one we last requested, we ignore</span><br><span style="color: hsl(120, 100%, 40%);">+         * it and keep requesting for one with lower Tx power. This way we converge to</span><br><span style="color: hsl(120, 100%, 40%);">+         * the lowest good Tx power avoiding oscillating over values within thresholds.</span><br><span style="color: hsl(120, 100%, 40%);">+        */</span><br><span style="color: hsl(120, 100%, 40%);">+   ignore = (ms_dbm == new_dbm && ms_dbm > current_dbm);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (lchan->ms_power == new_power_lvl || ignore) {</span><br><span style="color: hsl(120, 100%, 40%);">+          LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping MS power at control level %d (%d dBm): "</span><br><span style="color: hsl(120, 100%, 40%);">+                          "ms-pwr-lvl[curr %" PRIu8 ", max %" PRIu8 "], RSSI[curr %d, avg %d, thresh %d..%d] dBm\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                      new_power_lvl, new_dbm, ms_power_lvl, bsc_max_dbm, ul_rssi_dbm, rxlev2dbm(rxlev_avg),</span><br><span style="color: hsl(120, 100%, 40%);">+                         rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh));</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s MS power control level %d (%d dBm) => %d (%d dBm): "</span><br><span style="color: hsl(120, 100%, 40%);">+                "ms-pwr-lvl[curr %" PRIu8 ", max %" PRIu8 "], RSSI[curr %d, avg %d, thresh %d..%d] dBm\n",</span><br><span style="color: hsl(120, 100%, 40%);">+              (new_dbm > current_dbm) ? "Raising" : "Lowering",</span><br><span style="color: hsl(120, 100%, 40%);">+              lchan->ms_power, current_dbm, new_power_lvl, new_dbm, ms_power_lvl,</span><br><span style="color: hsl(120, 100%, 40%);">+                bsc_max_dbm, ul_rssi_dbm, rxlev2dbm(rxlev_avg),</span><br><span style="color: hsl(120, 100%, 40%);">+               rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* store the resulting new MS power level in the lchan */</span><br><span style="color: hsl(120, 100%, 40%);">+     lchan->ms_power = new_power_lvl;</span><br><span style="color: hsl(120, 100%, 40%);">+   rsl_chan_ms_power_ctrl(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-bsc/+/25654">change 25654</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-bsc/+/25654"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-bsc </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ibc307e758697eb5ca3fb86622f35709d6077db9e </div>
<div style="display:none"> Gerrit-Change-Number: 25654 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: keith <keith@rhizomatica.org> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>