<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-bts/+/21907">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  pespin: Looks good to me, but someone else must approve
  laforge: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">power_control: generalize measurement pre-processing state<br><br>This way EWMA based filtering can also be applied to RxQual.<br><br>Change-Id: I439c00b394da670e314f217b3246cc85ce8213c6<br>Related: SYS#4918, SYS#4917<br>---<br>M include/osmo-bts/gsm_data.h<br>M src/common/power_control.c<br>M tests/power/ms_power_loop_test.c<br>3 files changed, 35 insertions(+), 22 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h</span><br><span>index 6efc717..253b115 100644</span><br><span>--- a/include/osmo-bts/gsm_data.h</span><br><span>+++ b/include/osmo-bts/gsm_data.h</span><br><span>@@ -213,18 +213,28 @@</span><br><span> /* Default MS/BS Power Control parameters */</span><br><span> extern const struct gsm_power_ctrl_params power_ctrl_params_def;</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%);">+  /* 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 lchan_power_ctrl_state {</span><br><span>  /* Dynamic Power Control parameters (NULL in static mode) */</span><br><span>         const struct gsm_power_ctrl_params *dpc_params;</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Measurement pre-processing state (for dynamic mode) */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_power_ctrl_meas_proc_state rxlev_meas_proc;</span><br><span> </span><br><span>   /* Depending on the context (MS or BS power control), fields 'current' and 'max'</span><br><span>      * reflect either the MS power level (magic numbers), or BS Power reduction level</span><br><span>     * (attenuation, in dB). */</span><br><span>  uint8_t current;</span><br><span>     uint8_t max;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    /* Scaled up (100 times) average UL/DL RxLev (in dBm) */</span><br><span style="color: hsl(0, 100%, 40%);">-        int avg100_rxlev_dbm;</span><br><span> };</span><br><span> </span><br><span> struct gsm_lchan {</span><br><span>diff --git a/src/common/power_control.c b/src/common/power_control.c</span><br><span>index e159740..0629630 100644</span><br><span>--- a/src/common/power_control.c</span><br><span>+++ b/src/common/power_control.c</span><br><span>@@ -38,29 +38,29 @@</span><br><span> </span><br><span> /* Base Low-Pass Single-Pole IIR Filter (EWMA) formula:</span><br><span>  *</span><br><span style="color: hsl(0, 100%, 40%);">- *   Avg[n] = a * Pwr[n] + (1 - a) * Avg[n - 1]</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Avg[n] = a * Val[n] + (1 - a) * Avg[n - 1]</span><br><span>  *</span><br><span style="color: hsl(0, 100%, 40%);">- * where parameter 'a' determines how much weight of the latest UL RSSI measurement</span><br><span style="color: hsl(0, 100%, 40%);">- * result 'Pwr[n]' carries vs the weight of the average 'Avg[n - 1]'.  The value of</span><br><span style="color: hsl(0, 100%, 40%);">- * 'a' is usually a float in range 0 .. 1, so:</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>  *</span><br><span style="color: hsl(0, 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 0.5 gives equal weight to both 'Val[n]' and 'Avg[n - 1]';</span><br><span>  *  - value 1.0 means no filtering at all (pass through);</span><br><span>  *  - value 0.0 makes no sense.</span><br><span>  *</span><br><span>  * Further optimization:</span><br><span>  *</span><br><span style="color: hsl(0, 100%, 40%);">- *   Avg[n] = a * Pwr[n] + Avg[n - 1] - a * Avg[n - 1]</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Avg[n] = a * Val[n] + Avg[n - 1] - a * Avg[n - 1]</span><br><span>  *   ^^^^^^                ^^^^^^^^^^</span><br><span>  *</span><br><span>  * a) this can be implemented in C using '+=' operator:</span><br><span>  *</span><br><span style="color: hsl(0, 100%, 40%);">- *   Avg += a * Pwr - a * Avg</span><br><span style="color: hsl(0, 100%, 40%);">- *   Avg += a * (Pwr - Avg)</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>  *</span><br><span>  * b) everything is scaled up by 100 to avoid floating point stuff:</span><br><span>  *</span><br><span style="color: hsl(0, 100%, 40%);">- *   Avg100 += A * (Pwr - Avg)</span><br><span style="color: hsl(120, 100%, 40%);">+ *   Avg100 += A * (Val - Avg)</span><br><span>  *</span><br><span>  * where 'Avg100' is 'Avg * 100' and 'A' is 'a * 100'.</span><br><span>  *</span><br><span>@@ -70,20 +70,20 @@</span><br><span>  *   https://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter</span><br><span>  *   https://tomroelandts.com/articles/low-pass-single-pole-iir-filter</span><br><span>  */</span><br><span style="color: hsl(0, 100%, 40%);">-static int8_t do_pf_ewma(const struct gsm_power_ctrl_meas_params *mp,</span><br><span style="color: hsl(0, 100%, 40%);">-                        struct lchan_power_ctrl_state *state,</span><br><span style="color: hsl(0, 100%, 40%);">-                   const int8_t Pwr)</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> {</span><br><span>    const uint8_t A = mp->ewma.alpha;</span><br><span style="color: hsl(0, 100%, 40%);">-    int *Avg100 = &state->avg100_rxlev_dbm;</span><br><span style="color: hsl(120, 100%, 40%);">+        int *Avg100 = &mps->ewma.Avg100;</span><br><span> </span><br><span>  /* We don't have 'Avg[n - 1]' if this is the first run */</span><br><span>        if (*Avg100 == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-             *Avg100 = Pwr * EWMA_SCALE_FACTOR;</span><br><span style="color: hsl(0, 100%, 40%);">-              return Pwr;</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>  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   *Avg100 += A * (Pwr - *Avg100 / EWMA_SCALE_FACTOR);</span><br><span style="color: hsl(120, 100%, 40%);">+   *Avg100 += A * (Val - *Avg100 / EWMA_SCALE_FACTOR);</span><br><span>  return *Avg100 / EWMA_SCALE_FACTOR;</span><br><span> }</span><br><span> </span><br><span>@@ -104,7 +104,9 @@</span><br><span>   /* Filter RxLev value to reduce unnecessary Tx power oscillations */</span><br><span>         switch (params->rxlev_meas.algo) {</span><br><span>        case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA:</span><br><span style="color: hsl(0, 100%, 40%);">-              rxlev_dbm_avg = do_pf_ewma(&params->rxlev_meas, state, rxlev_dbm);</span><br><span style="color: hsl(120, 100%, 40%);">+             rxlev_dbm_avg = do_pf_ewma(&params->rxlev_meas,</span><br><span style="color: hsl(120, 100%, 40%);">+                                           &state->rxlev_meas_proc,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       rxlev_dbm);</span><br><span>               break;</span><br><span>       /* TODO: implement other pre-processing methods */</span><br><span>   case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE:</span><br><span>diff --git a/tests/power/ms_power_loop_test.c b/tests/power/ms_power_loop_test.c</span><br><span>index e93a2ef..eb0e3e9 100644</span><br><span>--- a/tests/power/ms_power_loop_test.c</span><br><span>+++ b/tests/power/ms_power_loop_test.c</span><br><span>@@ -164,7 +164,7 @@</span><br><span> </span><br><span>         init_test(__func__);</span><br><span>         lchan = &g_trx->ts[0].lchan[0];</span><br><span style="color: hsl(0, 100%, 40%);">-  avg100 = &lchan->ms_power_ctrl.avg100_rxlev_dbm;</span><br><span style="color: hsl(120, 100%, 40%);">+       avg100 = &lchan->ms_power_ctrl.rxlev_meas_proc.ewma.Avg100;</span><br><span> </span><br><span>       struct gsm_power_ctrl_meas_params *mp = &lchan->ms_dpc_params.rxlev_meas;</span><br><span>     mp->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA;</span><br><span>@@ -204,7 +204,8 @@</span><br><span> </span><br><span>        mp->ewma.alpha = 70; /* 30% smoothing */</span><br><span>  lchan->ms_power_ctrl.current = 15;</span><br><span style="color: hsl(0, 100%, 40%);">-   lchan->ms_power_ctrl.avg100_rxlev_dbm = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ lchan->ms_power_ctrl.rxlev_meas_proc = \</span><br><span style="color: hsl(120, 100%, 40%);">+           (struct gsm_power_ctrl_meas_proc_state) { 0 };</span><br><span> </span><br><span>   /* This is the first sample, the filter outputs it as-is */</span><br><span>  apply_power_test(lchan, -50, 0, 15);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-bts/+/21907">change 21907</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/+/21907"/><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: I439c00b394da670e314f217b3246cc85ce8213c6 </div>
<div style="display:none"> Gerrit-Change-Number: 21907 </div>
<div style="display:none"> Gerrit-PatchSet: 6 </div>
<div style="display:none"> Gerrit-Owner: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>