<p>fixeria has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/osmo-bts/+/20657">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">power_control: implement EWMA based Uplink power filtering<br><br>So far the Uplink power control loop did not filter the Uplink RSSI<br>measurements (reported by the BTS) at all.  The lack of filtering<br>makes our implementation too quick on the trigger, so in the real<br>deployments there will be unneeded Tx power oscillations.<br><br>In order to reduce this effect, let's implement a very simple EWMA<br>(also known as Single Pole IIR) filtering that is defined as follows:<br><br>  Avg[n] = a * Pwr[n] + (1 - a) * Avg[n - 1]<br><br>where parameter 'a' determines how much weight of the latest UL RSSI<br>measurement result 'Pwr[n]' carries vs the weight of the average<br>'Avg[n - 1]'.  The value of 'a' is usually a float in range 0 .. 1, so:<br><br>  - value 0.5 gives equal weight to both 'Pwr[n]' and 'Avg[n - 1]';<br>  - value 1.0 means no filtering at all (pass through);<br>  - value 0.0 makes no sense.<br><br>This formula was further optimized with the use of '+=' operator.<br>The floating point math was also eliminated by scaling everything<br>up (by 100).  For more details, see:<br><br>https://en.wikipedia.org/wiki/Moving_average<br>https://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter<br>https://tomroelandts.com/articles/low-pass-single-pole-iir-filter<br><br>The EWMA filtering is *not* enabled by default, but can be enabled,<br>disabled, or (re-)configured over the VTY at any time:<br><br>  ! Enable EWMA smoothing with the given parameters<br>  uplink-power-filtering algo ewma beta <1-99><br><br>  ! Completely disable filtering<br>  no uplink-power-filtering<br><br>Note that the VTY command expects 'beta' instead of 'alpha':<br><br>  alpha = (100 - beta)<br><br>and the value must be in %.  This is done for simplicity:<br><br>  1% means lowest smoothing,<br>  99% means highest smoothing.<br><br>Let's say we have EWMA filtering enabled with alpha = 0.4, and get<br>-98 dBm on the input, while the last output value was -60 dBm.<br>The new output would be:<br><br>  Avg[n] = 0.4 * Pwr[n] + 0.6 * Avg[n - 1]<br>  Avg[n] = (0.4 * -98) + (0.6 * -60)<br>  Avg[n] = -75.2 => around -75<br><br>Of course, this is not a silver bullet, but better than nothing.<br><br>Change-Id: Ib6dcadbf14ef59696c6a546bd323bda92d399f17<br>Related: SYS#4916<br>---<br>M include/osmo-bts/bts.h<br>M include/osmo-bts/gsm_data.h<br>M src/common/bts.c<br>M src/common/power_control.c<br>M src/common/vty.c<br>M tests/power/power_test.c<br>M tests/power/power_test.err<br>M tests/power/power_test.ok<br>8 files changed, 236 insertions(+), 3 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/57/20657/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmo-bts/bts.h b/include/osmo-bts/bts.h</span><br><span>index 4e496b5..cf69b43 100644</span><br><span>--- a/include/osmo-bts/bts.h</span><br><span>+++ b/include/osmo-bts/bts.h</span><br><span>@@ -95,6 +95,12 @@</span><br><span>      struct smscb_msg *default_msg; /* default broadcast message; NULL if none */</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Tx power filtering algorithm */</span><br><span style="color: hsl(120, 100%, 40%);">+enum ms_ul_pf_algo {</span><br><span style="color: hsl(120, 100%, 40%);">+      MS_UL_PF_ALGO_NONE = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+       MS_UL_PF_ALGO_EWMA,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* One BTS */</span><br><span> struct gsm_bts {</span><br><span>       /* list header in net->bts_list */</span><br><span>@@ -289,8 +295,23 @@</span><br><span>                 bool vty_override;      /* OML value overridden by VTY */</span><br><span>    } radio_link_timeout;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+     /* TODO: move it to bts->ul_power_ctrl struct */</span><br><span>  int ul_power_target;            /* Uplink Rx power target */</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+      /* Uplink power control */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* UL RSSI filtering algorithm */</span><br><span style="color: hsl(120, 100%, 40%);">+             enum ms_ul_pf_algo pf_algo;</span><br><span style="color: hsl(120, 100%, 40%);">+           /* (Optional) filtering parameters */</span><br><span style="color: hsl(120, 100%, 40%);">+         union {</span><br><span style="color: hsl(120, 100%, 40%);">+                       /* Exponentially Weighted Moving Average */</span><br><span style="color: hsl(120, 100%, 40%);">+                   struct {</span><br><span style="color: hsl(120, 100%, 40%);">+                              /* Smoothing factor: higher the value - less smoothing */</span><br><span style="color: hsl(120, 100%, 40%);">+                             uint8_t alpha; /* 1 .. 99 (in %) */</span><br><span style="color: hsl(120, 100%, 40%);">+                   } ewma;</span><br><span style="color: hsl(120, 100%, 40%);">+               } pf;</span><br><span style="color: hsl(120, 100%, 40%);">+ } ul_power_ctrl;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   /* used by the sysmoBTS to adjust band */</span><br><span>    uint8_t auto_band;</span><br><span> </span><br><span>diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h</span><br><span>index c6fe609..1c1c5d4 100644</span><br><span>--- a/include/osmo-bts/gsm_data.h</span><br><span>+++ b/include/osmo-bts/gsm_data.h</span><br><span>@@ -286,6 +286,9 @@</span><br><span>                uint8_t current;</span><br><span>             uint8_t max;</span><br><span>                 bool fixed;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Scaled up (100 times) average UL RSSI */</span><br><span style="color: hsl(120, 100%, 40%);">+           int avg100_ul_rssi;</span><br><span>  } ms_power_ctrl;</span><br><span> </span><br><span>         /* BTS power reduction (in dB) */</span><br><span>diff --git a/src/common/bts.c b/src/common/bts.c</span><br><span>index 5890c1a..4d7a5d9 100644</span><br><span>--- a/src/common/bts.c</span><br><span>+++ b/src/common/bts.c</span><br><span>@@ -303,6 +303,7 @@</span><br><span>         /* configurable via VTY */</span><br><span>   bts->paging_state = paging_init(bts, 200, 0);</span><br><span>     bts->ul_power_target = -75;  /* dBm default */</span><br><span style="color: hsl(120, 100%, 40%);">+     bts->ul_power_ctrl.pf_algo = MS_UL_PF_ALGO_NONE;</span><br><span>  bts->rtp_jitter_adaptive = false;</span><br><span>         bts->rtp_port_range_start = 16384;</span><br><span>        bts->rtp_port_range_end = 17407;</span><br><span>diff --git a/src/common/power_control.c b/src/common/power_control.c</span><br><span>index d36f157..4798f4f 100644</span><br><span>--- a/src/common/power_control.c</span><br><span>+++ b/src/common/power_control.c</span><br><span>@@ -1,6 +1,7 @@</span><br><span> /* MS Power Control Loop L1 */</span><br><span> </span><br><span> /* (C) 2014 by Holger Hans Peter Freyther</span><br><span style="color: hsl(120, 100%, 40%);">+ * Contributions by sysmocom - s.m.f.c. GmbH <info@sysmocom.de></span><br><span>  *</span><br><span>  * All Rights Reserved</span><br><span>  *</span><br><span>@@ -36,6 +37,60 @@</span><br><span> #define MS_RAISE_MAX_DB 4</span><br><span> #define MS_LOWER_MAX_DB 8</span><br><span> </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%);">+</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 * Pwr[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 UL RSSI measurement</span><br><span style="color: hsl(120, 100%, 40%);">+ * result 'Pwr[n]' carries vs the weight of the average 'Avg[n - 1]'.  The value of</span><br><span style="color: hsl(120, 100%, 40%);">+ * '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 'Pwr[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 * Pwr[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 * Pwr - a * Avg</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Avg += a * (Pwr - 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 * (Pwr - 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 int8_t lchan_ul_pf_ewma(const struct gsm_bts *bts,</span><br><span style="color: hsl(120, 100%, 40%);">+                              struct gsm_lchan *lchan,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const int8_t Pwr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   const uint8_t A = bts->ul_power_ctrl.pf.ewma.alpha;</span><br><span style="color: hsl(120, 100%, 40%);">+        int *Avg100 = &lchan->ms_power_ctrl.avg100_ul_rssi;</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 (lchan->meas.res_nr == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             *Avg100 = Pwr * EWMA_SCALE_FACTOR;</span><br><span style="color: hsl(120, 100%, 40%);">+            return Pwr;</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 * (Pwr - *Avg100 / EWMA_SCALE_FACTOR);</span><br><span style="color: hsl(120, 100%, 40%);">+   return *Avg100 / EWMA_SCALE_FACTOR;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  /*! compute the new MS POWER LEVEL communicated to the MS and store it in lchan.</span><br><span>   *  \param lchan logical channel for which to compute (and in which to store) new power value.</span><br><span>   *  \param[in] ms_power_lvl MS Power Level received from Uplink L1 SACCH Header in SACCH block.</span><br><span>@@ -51,6 +106,7 @@</span><br><span>    enum gsm_band band = bts->band;</span><br><span>   int8_t new_power_lvl; /* TS 05.05 power level */</span><br><span>     int8_t ms_dbm, new_dbm, current_dbm, bsc_max_dbm;</span><br><span style="color: hsl(120, 100%, 40%);">+     int8_t avg_ul_rssi_dbm;</span><br><span> </span><br><span>  if (!trx_ms_pwr_ctrl_is_osmo(lchan->ts->trx))</span><br><span>          return 0;</span><br><span>@@ -72,9 +128,20 @@</span><br><span>              return 0;</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Filter UL RSSI to reduce unnecessary Tx power oscillations */</span><br><span style="color: hsl(120, 100%, 40%);">+      switch (bts->ul_power_ctrl.pf_algo) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case MS_UL_PF_ALGO_EWMA:</span><br><span style="color: hsl(120, 100%, 40%);">+              avg_ul_rssi_dbm = lchan_ul_pf_ewma(bts, lchan, ul_rssi_dbm);</span><br><span style="color: hsl(120, 100%, 40%);">+          break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case MS_UL_PF_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%);">+             avg_ul_rssi_dbm = ul_rssi_dbm;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  /* How many dBs measured power should be increased (+) or decreased (-)</span><br><span>         to reach expected power. */</span><br><span style="color: hsl(0, 100%, 40%);">-  diff = bts->ul_power_target - ul_rssi_dbm;</span><br><span style="color: hsl(120, 100%, 40%);">+ diff = bts->ul_power_target - avg_ul_rssi_dbm;</span><br><span> </span><br><span>        /* don't ever change more than MS_{LOWER,RAISE}_MAX_DBM during one loop</span><br><span>     iteration, i.e. reduce the speed at which the MS transmit power can</span><br><span>@@ -108,7 +175,7 @@</span><br><span>                           "(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", rx-current %d dBm, rx-target %d dBm)\n",</span><br><span>                        new_power_lvl, new_dbm,</span><br><span>                      ms_power_lvl, lchan->ms_power_ctrl.max,</span><br><span style="color: hsl(0, 100%, 40%);">-                      ul_rssi_dbm, bts->ul_power_target);</span><br><span style="color: hsl(120, 100%, 40%);">+                        avg_ul_rssi_dbm, bts->ul_power_target);</span><br><span>                 return 0;</span><br><span>    }</span><br><span> </span><br><span>@@ -118,7 +185,7 @@</span><br><span>            (new_dbm > current_dbm) ? "Raising" : "Lowering",</span><br><span>             lchan->ms_power_ctrl.current, current_dbm, new_power_lvl, new_dbm,</span><br><span>                ms_power_lvl, lchan->ms_power_ctrl.max,</span><br><span style="color: hsl(0, 100%, 40%);">-              ul_rssi_dbm, bts->ul_power_target);</span><br><span style="color: hsl(120, 100%, 40%);">+                avg_ul_rssi_dbm, bts->ul_power_target);</span><br><span> </span><br><span>     /* store the resulting new MS power level in the lchan */</span><br><span>    lchan->ms_power_ctrl.current = new_power_lvl;</span><br><span>diff --git a/src/common/vty.c b/src/common/vty.c</span><br><span>index 534b632..00fd474 100644</span><br><span>--- a/src/common/vty.c</span><br><span>+++ b/src/common/vty.c</span><br><span>@@ -256,6 +256,18 @@</span><br><span>         vty_out(vty, " paging lifetime %u%s", paging_get_lifetime(bts->paging_state),</span><br><span>           VTY_NEWLINE);</span><br><span>        vty_out(vty, " uplink-power-target %d%s", bts->ul_power_target, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* MS Tx power filtering algorithm and parameters */</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (bts->ul_power_ctrl.pf_algo) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case MS_UL_PF_ALGO_EWMA:</span><br><span style="color: hsl(120, 100%, 40%);">+              vty_out(vty, " uplink-power-filtering algo ewma beta %u%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                 100 - bts->ul_power_ctrl.pf.ewma.alpha, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case MS_UL_PF_ALGO_NONE:</span><br><span style="color: hsl(120, 100%, 40%);">+      default:</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (bts->agch_queue.thresh_level != GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT</span><br><span>                || bts->agch_queue.low_level != GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT</span><br><span>              || bts->agch_queue.high_level != GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT)</span><br><span>@@ -615,6 +627,37 @@</span><br><span>   return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN_ATTR(cfg_no_bts_ul_power_filter,</span><br><span style="color: hsl(120, 100%, 40%);">+     cfg_bts_no_ul_power_filter_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+       "no uplink-power-filtering",</span><br><span style="color: hsl(120, 100%, 40%);">+        NO_STR "Disable filtering for uplink power control loop\n",</span><br><span style="color: hsl(120, 100%, 40%);">+         CMD_ATTR_IMMEDIATE)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_bts *bts = vty->index;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        bts->ul_power_ctrl.pf_algo = MS_UL_PF_ALGO_NONE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_SUCCESS;</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%);">+DEFUN_ATTR(cfg_bts_ul_power_filter_ewma,</span><br><span style="color: hsl(120, 100%, 40%);">+       cfg_bts_ul_power_filter_ewma_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+     "uplink-power-filtering algo ewma beta <1-99>",</span><br><span style="color: hsl(120, 100%, 40%);">+       "Configure filtering for uplink power control loop\n"</span><br><span style="color: hsl(120, 100%, 40%);">+       "Select the filtering algorithm\n"</span><br><span style="color: hsl(120, 100%, 40%);">+          "Exponentially Weighted Moving Average (EWMA)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+    "Smoothing factor (in %%): beta = (100 - alpha)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+          "1%% - lowest smoothing, 99%% - highest smoothing\n",</span><br><span style="color: hsl(120, 100%, 40%);">+       CMD_ATTR_IMMEDIATE)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_bts *bts = vty->index;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        bts->ul_power_ctrl.pf_algo = MS_UL_PF_ALGO_EWMA;</span><br><span style="color: hsl(120, 100%, 40%);">+   bts->ul_power_ctrl.pf.ewma.alpha = 100 - atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> DEFUN_ATTR(cfg_bts_min_qual_rach, cfg_bts_min_qual_rach_cmd,</span><br><span>        "min-qual-rach <-100-100>",</span><br><span>          "Set the minimum link quality level of Access Bursts to be accepted\n"</span><br><span>@@ -1805,6 +1848,8 @@</span><br><span>  install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_default_cmd);</span><br><span>         install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_params_cmd);</span><br><span>  install_element(BTS_NODE, &cfg_bts_ul_power_target_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+  install_element(BTS_NODE, &cfg_bts_no_ul_power_filter_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+       install_element(BTS_NODE, &cfg_bts_ul_power_filter_ewma_cmd);</span><br><span>    install_element(BTS_NODE, &cfg_bts_min_qual_rach_cmd);</span><br><span>   install_element(BTS_NODE, &cfg_bts_min_qual_norm_cmd);</span><br><span>   install_element(BTS_NODE, &cfg_bts_max_ber_rach_cmd);</span><br><span>diff --git a/tests/power/power_test.c b/tests/power/power_test.c</span><br><span>index c3bfdf9..eff722a 100644</span><br><span>--- a/tests/power/power_test.c</span><br><span>+++ b/tests/power/power_test.c</span><br><span>@@ -61,6 +61,9 @@</span><br><span>   old = lchan->ms_power_ctrl.current;</span><br><span>       ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, rxlev);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+   /* Keep the measurement counter updated */</span><br><span style="color: hsl(120, 100%, 40%);">+    lchan->meas.res_nr++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   printf("lchan_ms_pwr_ctrl(RxLvl=%d dBm) returns %d (expected %d)\n",</span><br><span>              rxlev, ret, exp_ret);</span><br><span>         printf("\tMS current power %u -> %u (expected %u)\n",</span><br><span>@@ -140,6 +143,68 @@</span><br><span>    apply_power_test(lchan, -40, 1, 15);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void test_pf_algo_ewma(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct gsm_lchan *lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+      const int *avg100;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  init_test(__func__);</span><br><span style="color: hsl(120, 100%, 40%);">+  lchan = &g_trx->ts[0].lchan[0];</span><br><span style="color: hsl(120, 100%, 40%);">+        avg100 = &lchan->ms_power_ctrl.avg100_ul_rssi;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       g_bts->ul_power_ctrl.pf_algo = MS_UL_PF_ALGO_EWMA;</span><br><span style="color: hsl(120, 100%, 40%);">+ g_bts->ul_power_ctrl.pf.ewma.alpha = 20; /* 80% smoothing */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);</span><br><span style="color: hsl(120, 100%, 40%);">+   lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 26);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(lchan->ms_power_ctrl.max == 2);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CHECK_UL_RSSI_AVG100(exp) \</span><br><span style="color: hsl(120, 100%, 40%);">+ printf("\tAvg[t] is %2.2f dBm (expected %2.2f dBm)\n", \</span><br><span style="color: hsl(120, 100%, 40%);">+           ((float) *avg100) / 100, exp);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* UL RSSI remains constant => no UL power change */</span><br><span style="color: hsl(120, 100%, 40%);">+       apply_power_test(lchan, -75, 0, 15);</span><br><span style="color: hsl(120, 100%, 40%);">+  CHECK_UL_RSSI_AVG100(-75.00);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Avg[t] = (0.2 * -90) + (0.8 * -75) = -78.0 dBm */</span><br><span style="color: hsl(120, 100%, 40%);">+  apply_power_test(lchan, -90, 1, 13);</span><br><span style="color: hsl(120, 100%, 40%);">+  CHECK_UL_RSSI_AVG100(-78.00);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Avg[t] = (0.2 * -90) + (0.8 * -78) = -80.4 dBm */</span><br><span style="color: hsl(120, 100%, 40%);">+  apply_power_test(lchan, -90, 1, 11);</span><br><span style="color: hsl(120, 100%, 40%);">+  CHECK_UL_RSSI_AVG100(-80.40);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Avg[t] = (0.2 * -70) + (0.8 * -80.4) = -78.32 dBm,</span><br><span style="color: hsl(120, 100%, 40%);">+  * but due to up-/down-scaling artefacts we get the following:</span><br><span style="color: hsl(120, 100%, 40%);">+         *   Avg100[t] = Avg100[t - 1] + A * (Pwr - Avg[t] / 100)</span><br><span style="color: hsl(120, 100%, 40%);">+      *   Avg100[t] = -8040 + 20 * (-70 - (-8040 / 100))</span><br><span style="color: hsl(120, 100%, 40%);">+    *   Avg100[t] = -8040 + 20 * (-70 - (-8040 / 100))</span><br><span style="color: hsl(120, 100%, 40%);">+    *   Avg100[t] = -8040 + 20 * (-70 + 80)</span><br><span style="color: hsl(120, 100%, 40%);">+       *   Avg100[t] = -8040 + 200 = -7840</span><br><span style="color: hsl(120, 100%, 40%);">+   *   Avg[t] = -7840 / 100 = -78.4 */</span><br><span style="color: hsl(120, 100%, 40%);">+  apply_power_test(lchan, -70, 1, 9);</span><br><span style="color: hsl(120, 100%, 40%);">+   CHECK_UL_RSSI_AVG100(-78.40);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       g_bts->ul_power_ctrl.pf.ewma.alpha = 70; /* 30% smoothing */</span><br><span style="color: hsl(120, 100%, 40%);">+       lchan->ms_power_ctrl.current = 15;</span><br><span style="color: hsl(120, 100%, 40%);">+ lchan->meas.res_nr = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* This is the first sample, the filter outputs it as-is */</span><br><span style="color: hsl(120, 100%, 40%);">+   apply_power_test(lchan, -50, 0, 15);</span><br><span style="color: hsl(120, 100%, 40%);">+  CHECK_UL_RSSI_AVG100(-50.00);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Avg[t] = (0.7 * -50) + (0.3 * -50) = -50.0 dBm */</span><br><span style="color: hsl(120, 100%, 40%);">+  apply_power_test(lchan, -50, 0, 15);</span><br><span style="color: hsl(120, 100%, 40%);">+  CHECK_UL_RSSI_AVG100(-50.0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Simulate SACCH block loss (-110 dBm):</span><br><span style="color: hsl(120, 100%, 40%);">+       * Avg[t] = (0.7 * -110) + (0.3 * -50) = -92.0 dBm */</span><br><span style="color: hsl(120, 100%, 40%);">+ apply_power_test(lchan, -110, 1, 13);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECK_UL_RSSI_AVG100(-92.0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int main(int argc, char **argv)</span><br><span> {</span><br><span>    printf("Testing power loop...\n");</span><br><span>@@ -154,6 +219,7 @@</span><br><span>   log_set_use_color(osmo_stderr_target, 0);</span><br><span> </span><br><span>        test_power_loop();</span><br><span style="color: hsl(120, 100%, 40%);">+    test_pf_algo_ewma();</span><br><span> </span><br><span>     printf("Power loop test OK\n");</span><br><span> </span><br><span>diff --git a/tests/power/power_test.err b/tests/power/power_test.err</span><br><span>index e4d821a..786f2a7 100644</span><br><span>--- a/tests/power/power_test.err</span><br><span>+++ b/tests/power/power_test.err</span><br><span>@@ -21,3 +21,10 @@</span><br><span> (bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 10 (10 dBm) to 9, 12 dBm (rx-ms-pwr-lvl 10, max-ms-pwr-lvl 29, rx-current -77 dBm, rx-target -75 dBm)</span><br><span> (bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 9 (12 dBm) to 14, 2 dBm (rx-ms-pwr-lvl 9, max-ms-pwr-lvl 14, rx-current -73 dBm, rx-target -75 dBm)</span><br><span> (bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 14 (2 dBm) to 15, 0 dBm (rx-ms-pwr-lvl 14, max-ms-pwr-lvl 0, rx-current -40 dBm, rx-target -75 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 3 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -78 dBm, rx-target -75 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -80 dBm, rx-target -75 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 11 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -78 dBm, rx-target -75 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -50 dBm, rx-target -75 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -50 dBm, rx-target -75 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -92 dBm, rx-target -75 dBm)</span><br><span>diff --git a/tests/power/power_test.ok b/tests/power/power_test.ok</span><br><span>index 6b7a0d4..25551d8 100644</span><br><span>--- a/tests/power/power_test.ok</span><br><span>+++ b/tests/power/power_test.ok</span><br><span>@@ -49,4 +49,27 @@</span><br><span>    MS current power 14 -> 14 (expected 14)</span><br><span> lchan_ms_pwr_ctrl(RxLvl=-40 dBm) returns 1 (expected 1)</span><br><span>        MS current power 14 -> 15 (expected 15)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Starting test case 'test_pf_algo_ewma'</span><br><span style="color: hsl(120, 100%, 40%);">+lchan_ms_pwr_ctrl(RxLvl=-75 dBm) returns 0 (expected 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ MS current power 15 -> 15 (expected 15)</span><br><span style="color: hsl(120, 100%, 40%);">+    Avg[t] is -75.00 dBm (expected -75.00 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+lchan_ms_pwr_ctrl(RxLvl=-90 dBm) returns 1 (expected 1)</span><br><span style="color: hsl(120, 100%, 40%);">+   MS current power 15 -> 13 (expected 13)</span><br><span style="color: hsl(120, 100%, 40%);">+    Avg[t] is -78.00 dBm (expected -78.00 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+lchan_ms_pwr_ctrl(RxLvl=-90 dBm) returns 1 (expected 1)</span><br><span style="color: hsl(120, 100%, 40%);">+   MS current power 13 -> 11 (expected 11)</span><br><span style="color: hsl(120, 100%, 40%);">+    Avg[t] is -80.40 dBm (expected -80.40 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+lchan_ms_pwr_ctrl(RxLvl=-70 dBm) returns 1 (expected 1)</span><br><span style="color: hsl(120, 100%, 40%);">+   MS current power 11 -> 9 (expected 9)</span><br><span style="color: hsl(120, 100%, 40%);">+      Avg[t] is -78.40 dBm (expected -78.40 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+lchan_ms_pwr_ctrl(RxLvl=-50 dBm) returns 0 (expected 0)</span><br><span style="color: hsl(120, 100%, 40%);">+   MS current power 15 -> 15 (expected 15)</span><br><span style="color: hsl(120, 100%, 40%);">+    Avg[t] is -50.00 dBm (expected -50.00 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+lchan_ms_pwr_ctrl(RxLvl=-50 dBm) returns 0 (expected 0)</span><br><span style="color: hsl(120, 100%, 40%);">+   MS current power 15 -> 15 (expected 15)</span><br><span style="color: hsl(120, 100%, 40%);">+    Avg[t] is -50.00 dBm (expected -50.00 dBm)</span><br><span style="color: hsl(120, 100%, 40%);">+lchan_ms_pwr_ctrl(RxLvl=-110 dBm) returns 1 (expected 1)</span><br><span style="color: hsl(120, 100%, 40%);">+  MS current power 15 -> 13 (expected 13)</span><br><span style="color: hsl(120, 100%, 40%);">+    Avg[t] is -92.00 dBm (expected -92.00 dBm)</span><br><span> Power loop test OK</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-bts/+/20657">change 20657</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-bts/+/20657"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-bts </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ib6dcadbf14ef59696c6a546bd323bda92d399f17 </div>
<div style="display:none"> Gerrit-Change-Number: 20657 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>