<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(¶ms->rxlev_meas, state, rxlev_dbm);</span><br><span style="color: hsl(120, 100%, 40%);">+ rxlev_dbm_avg = do_pf_ewma(¶ms->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>